Giorgio Sardo Blog

Senior Director of Windows, Windows Phone & XBOX Technical Evangelism & Development at Microsoft Corporation

HTML5 Snow falling on my blog

HTML5 Snow falling on my blog

  • Comments 1

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.A bocca aperta

snow

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).

image

You can find the original script from David here, the script with my updates here.

Happy Holidays! Stella

Attachment: Snow.zip
Page 1 of 1 (1 items)