Skip to content

Instantly share code, notes, and snippets.

@henrahmagix
Created December 8, 2012 13:43
Show Gist options
  • Save henrahmagix/4240312 to your computer and use it in GitHub Desktop.
Save henrahmagix/4240312 to your computer and use it in GitHub Desktop.
Snow in an HTML5 Canvas
/*
Source: http://www.codeproject.com/Tips/374219/HTML5-Snow-Canvas
Licence: Ms-PL
Author: Agon Avdimetaj, @AgonAvdimetaj
This code is slightly altered from Agon Avdimetaj's code at the link above.
It is changed to use prototypes and so the only global variable pollution comes
from the window.CanvasSnow property. It is also more extensible.
I don't know the terms for this kind of Javascript, so this section is open to
changes so it can become a README.
*/
(function() {
window.CanvasSnow = function(opts) {
return this.init(opts);
};
CanvasSnow.config = {
backgroundColor: '#ec008e',
flakeColor: '#fff',
flakeInterval: 200,
maxFlakes: 200,
minDrift: 0,
maxDrift: 1,
minSize: 2,
maxSize: 5,
minSpeed: 1,
maxSpeed: 6,
minRoundedSize: 3,
round: false,
snowTracker: {}
};
CanvasSnow.animate = function() {
// Animate each CanvasSnow object we are tracking.
for (var canvasID in CanvasSnow.config.snowTracker) {
CanvasSnow.config.snowTracker[canvasID].animate();
}
};
CanvasSnow.prototype = {
init: function(opts) {
this.canvasID = opts.canvasID;
this.canvas = document.getElementById(this.canvasID);
this.context = this.canvas.getContext('2d');
this.bufferCanvas = document.createElement('canvas');
// Set heights and width. Needs cleaning up.
this.height = this.canvas.getAttribute('height') || this.canvas.style.height;
this.height = parseInt(this.height.match(/^([0-9]*)/)[1]) || 0;
this.canvas.setAttribute('height', this.height);
this.width = this.canvas.getAttribute('width') || this.canvas.style.width;
this.width = parseInt(this.width.match(/^([0-9]*)/)[1]) || 0;
this.canvas.setAttribute('width', this.width);
this.bufferCanvas.setAttribute('height', this.height);
this.bufferCanvas.setAttribute('width', this.width);
this.bufferCanvas.style.height = this.height;
this.bufferCanvas.style.width = this.width;
this.bufferCanvasCtx = this.bufferCanvas.getContext('2d');
this.backgroundColor = opts.backgroundColor || CanvasSnow.config.backgroundColor;
this.flakeColor = opts.flakeColor || CanvasSnow.config.flakeColor;
this.flakeArray = [];
this.flakeInterval = opts.flakeInterval || CanvasSnow.config.flakeInterval;
this.round = opts.round || CanvasSnow.config.round;
// Ensure values of 0 are correctly assigned.
this.maxFlakes = (typeof opts.maxFlakes === 'number') ? opts.maxFlakes : CanvasSnow.config.maxFlakes;
this.minSize = (typeof opts.minSize === 'number') ? opts.minSize : CanvasSnow.config.minSize;
this.maxSize = (typeof opts.maxSize === 'number') ? opts.maxSize : CanvasSnow.config.maxSize;
this.minDrift = (typeof opts.minDrift === 'number') ? opts.minDrift : CanvasSnow.config.minDrift;
this.maxDrift = (typeof opts.maxDrift === 'number') ? opts.maxDrift : CanvasSnow.config.maxDrift;
this.minSpeed = (typeof opts.minSpeed === 'number') ? opts.minSpeed : CanvasSnow.config.minSpeed;
this.maxSpeed = (typeof opts.maxSpeed === 'number') ? opts.maxSpeed : CanvasSnow.config.maxSpeed;
this.minRoundedSize = (typeof opts.minRoundedSize === 'number') ? opts.minRoundedSize : CanvasSnow.config.minRoundedSize;
CanvasSnow.config.snowTracker[this.canvasID] = this;
this.flakeTimer = setInterval('CanvasSnow.config.snowTracker["' + this.canvasID + '"].addFlake()', this.flakeInterval);
// Start animating!
this.animate();
return this;
},
Flake: function(snow) {
this.drift = snow.randomFloatInRange(snow.minDrift, snow.maxDrift);
this.speed = snow.randomFloatInRange(snow.minSpeed, snow.maxSpeed);
this.width = snow.randomFloatInRange(snow.minSize, snow.maxSize);
this.height = this.width;
this.x = Math.round(Math.random() * snow.width);
this.y = -this.height / 2;
},
addFlake: function() {
this.flakeArray[this.flakeArray.length] = new this.Flake(this);
if (this.flakeArray.length == this.maxFlakes)
clearInterval(this.flakeTimer);
},
blank: function() {
if (this.backgroundColor === 'transparent') {
this.bufferCanvasCtx.clearRect(0, 0, this.bufferCanvasCtx.canvas.width, this.bufferCanvasCtx.canvas.height);
this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
}
else {
this.bufferCanvasCtx.fillStyle = this.backgroundColor;
this.bufferCanvasCtx.fillRect(0, 0, this.bufferCanvasCtx.canvas.width, this.bufferCanvasCtx.canvas.height);
}
},
animate: function() {
// Delegate each animation frame to the browser.
requestAnimationFrame(CanvasSnow.animate, this.canvas);
this.Update();
this.Draw();
},
Update: function() {
var flake;
var max = {
x: this.width,
y: this.height
};
var min = {
x: 0,
y: 0
};
var halfFlake = {};
var bounds = {
x: max.x,
y: max.y
};
var driftLeft;
for (var i = 0; i < this.flakeArray.length; i++) {
flake = this.flakeArray[i];
halfFlake.x = flake.width / 2;
halfFlake.y = flake.height / 2;
driftLeft = !! (flake.drift < 0);
bounds.x = (driftLeft) ? min.x - halfFlake.x : max.x + halfFlake.x;
bounds.y += halfFlake.y;
// Move along y.
if (flake.y <= bounds.y) {
// Within bounds: move.
flake.y += flake.speed;
}
else {
// Out of bounds: reset.
flake.y = min.y - halfFlake.y;
}
// Move along x.
if (driftLeft) {
if (flake.x >= bounds.x) {
// Within bounds: move.
flake.x += flake.drift;
}
else {
// Out of bounds, drifting left: reset to right x.
flake.x = max.x + halfFlake.x;
}
}
else {
if (flake.x <= bounds.x) {
// Within bounds: move.
flake.x += flake.drift;
}
else {
// Out of bounds, drifting right: reset to left x.
flake.x = min.x - halfFlake.x;
}
}
}
},
Draw: function() {
this.context.save();
this.blank();
this.bufferCanvasCtx.fillStyle = this.flakeColor;
var flake;
for (var i = 0; i < this.flakeArray.length; i++) {
flake = this.flakeArray[i];
if (this.round && flake.width >= this.minRoundedSize) {
this.bufferCanvasCtx.beginPath();
this.bufferCanvasCtx.arc(flake.x, flake.y, flake.width / 2, 0, 2 * Math.PI);
this.bufferCanvasCtx.closePath();
this.bufferCanvasCtx.fill();
}
else {
xPos = flake.x - (flake.width / 2);
yPos = flake.y - (flake.height / 2);
this.bufferCanvasCtx.fillRect(xPos, yPos, flake.width, flake.height);
}
}
this.context.drawImage(this.bufferCanvas, 0, 0, this.bufferCanvas.width, this.bufferCanvas.height);
this.context.restore();
},
randomIntInRange: function(min, max) {
return Math.round(this.randomFloatInRange(min, max));
},
randomFloatInRange: function(min, max) {
return min + Math.random() * (max - min);
}
};
})();
body {
background-color: #020659;
color: #f2f2f2;
font-family: Arvo, Arial;
position: relative;
text-align: center;
}
* {
position: relative;
z-index: 2;
}
a {
color: #730202;
text-decoration: none;
}
a:hover, a:focus {
color: #fff;
text-decoration: underline;
}
#snow {
position: fixed;
top: 0;
left: 0;
z-index: 1;
}
<html>
<head>
<title>Canvas Snow</title>
<link href="http://fonts.googleapis.com/css?family=Arvo" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=PT+Sans" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="index.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript" src="requestAnimationFrame.js"></script>
<script type="text/javascript" src="canvas-snow.js"></script>
<script type="text/javascript">
(function($) {
$(function() {
var snowHeight = $('html').outerHeight();
var snowWidth = $('html').outerWidth();
var snowObj = $('#snow');
snowObj.height(snowHeight).width(snowWidth);
// snowObj.attr('height', snowHeight).attr('width', snowWidth);
var snow = new CanvasSnow({
canvasID: 'snow',
flakeInterval: 1,
maxFlakes: 5000,
backgroundColor: 'transparent',
round: true,
minDrift: -0.5,
maxDrift: 2.2,
minSize: 1,
maxSize: 6,
minRoundedSize: 4,
minSpeed: 3.2,
maxSpeed: 4.5
});
});
})(jQuery);
</script>
</head>
<body>
<h1 class="title">Canvas Snow</h1>
<h2 class="subtitle">Using <a href="http://www.codeproject.com/Tips/374219/HTML5-Snow-Canvas">http://www.codeproject.com/Tips/374219/HTML5-Snow-Canvas</a></h2>
<canvas id="snow">Your browser ain't got Canvas support. WTF?</canvas>
</body>
</html>
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment