HTML5 Snow falling on my blog
Based on an interesting script of David Flanagan to generate a snow flake using recursive JavaScript and HTML5 <canvas>, I had some fun during the weekend and added some snow to my blog.
David’s implementation is based on several canvas elements (one for each snow flake) added to the DOM tree and animated using CSS. I preferred to create only one Canvas and animate all the flakes inside it. To avoid the Canvas to block the interaction with the page I added it as an element with background priority.
(function () {
// Start Animation only if browser support <canvas>
if (document.createElement('canvas').getContext) {
if (document.readyState === 'complete')
Snow();
else
window.addEventListener('DOMContentLoaded', Snow, false);
}
var deg = Math.PI / 180; // For converting degrees to radians
var sqrt3_2 = Math.sqrt(3) / 2; // Height of an equilateral triangle
var flakes = []; // Things that are dropping
var scrollspeed = 64; // How often we animate things
var snowspeed = 500; // How often we add a new snowflake
var maxflakes = 20; // Max number of flakes to be added at the same time
var rand = function (n) { return Math.floor(n * Math.random()); }
var canvas, sky;
var snowingTimer;
var invalidateMeasure = false;
function Snow() {
canvas = document.createElement('canvas');
canvas.style.position = 'fixed';
canvas.style.top = '0px';
canvas.style.left = '0px';
canvas.style.zIndex = '0';
document.body.insertBefore(canvas, document.body.firstChild);
sky = canvas.getContext('2d');
ResetCanvas();
snowingTimer = setInterval(createSnowflake, snowspeed);
setInterval(moveSnowflakes, scrollspeed);
window.addEventListener('resize', ResetCanvas, false);
}
function ResetCanvas() {
invalidateMeasure = true;
canvas.width = document.body.offsetWidth;
canvas.height = window.innerHeight;
sky.strokeStyle = '#0066CC';
sky.fillStyle = 'white';
}
function drawFlake(x, y, size, order) {
sky.save();
sky.translate(x, y);
snowflake(order, 0, Math.floor(sqrt3_2 * y), size);
sky.fill();
sky.stroke();
sky.restore();
}
function snowflake(n, x, y, len) {
sky.save(); // Save current transformation
sky.beginPath();
sky.translate(x, y); // Translate to starting point
sky.moveTo(0, 0); // Begin a new subpath there
leg(n); // Draw the first leg of the fractal
sky.rotate(-120 * deg); // Rotate 120 degrees anticlockwise
leg(n); // Draw the second leg
sky.rotate(-120 * deg); // Rotate again.
leg(n); // Draw the final leg
sky.closePath(); // Close the subpath
sky.restore(); // Restore original transformation
// Draw a single leg of a level-n Koch snowflake.
// This function leaves the current point at the end of
// the leg it has drawn and translates the coordinate
// system so the current point is (0,0). This means you
// can easily call rotate() after drawing a leg.
function leg(n) {
sky.save(); // Save current transform
if (n == 0) { // Non-recursive case:
sky.lineTo(len, 0); // Just a horizontal line
}
else { // Recursive case: _ _
// draw 4 sub-legs like: \/
sky.scale(1 / 3, 1 / 3); // Sub-legs are 1/3rd size
leg(n - 1); // Draw the first sub-leg
sky.rotate(60 * deg); // Turn 60 degrees clockwise
leg(n - 1); // Draw the second sub-leg
sky.rotate(-120 * deg); // Rotate 120 degrees back
leg(n - 1); // Third sub-leg
sky.rotate(60 * deg); // Back to original heading
leg(n - 1); // Final sub-leg
}
sky.restore(); // Restore the transform
sky.translate(len, 0); // Translate to end of leg
}
}
function createSnowflake() {
var order = 2;
var size = 10 + rand(90);
var t = (document.body.offsetWidth - 964) / 2;
var x = (rand(2) == 0) ? rand(t) : t + 964 + rand(t); // Make it fit with my blog
var y = window.pageYOffset;
flakes.push({ x: x, y: y, vx: 0, vy: 3 + rand(3), size: size, order: order });
if (flakes.length > maxflakes) clearInterval(snowingTimer);
}
function moveSnowflakes() {
sky.clearRect(0, 0, canvas.width, canvas.height);
var maxy = canvas.height;
for (var i = 0; i < flakes.length; i++) {
var flake = flakes[i];
flake.y += flake.vy;
flake.x += flake.vx;
if (flake.y > maxy) flake.y = 0;
if (invalidateMeasure) {
var t = (canvas.width - 964) / 2;
flake.x = (rand(2) == 0) ? rand(t) : t + 964 + rand(t);
}
drawFlake(flake.x, flake.y, flake.size, flake.order);
// Sometimes change the sideways velocity
if (rand(4) == 1) flake.vx += (rand(11) - 5) / 10;
if (flake.vx > 2) flake.vx = 2;
if (flake.vx < -2) flake.vx = -2;
}
if (invalidateMeasure) invalidateMeasure = false;
}
} ());
I did a quick test check with performance, and it looks like IE9 perform better with CPU/Memory usage (IE9 Beta Refresh, Firefox 4 Beta7, Chrome 10 Canary, Opera 11Beta and Safari 5).
You can find the original script from David here, the script with my updates here.
Happy Holidays!
Comments
- Anonymous
December 14, 2010
I like how you've used the snowflakes in the background. I've finally responded to your comments here: www.davidflanagan.com/.../let-it-snow.html