Monday, May 30, 2011

HTML5 - Canvas

Recently got a chance to learn some of the new HTML5 standards. There are lot of new features supported out of box by the browsers adapting the HTML5 syntax.

DIVE INTO HTML5 is a wonderful book/tutorial on HTML5. This article, lists out the top 28 features one must know before coding in HTML5.

There are many new element tags introduced by HTML5 standards. As per HTML5 standards, Canvas element is defined “a resolution-dependent bitmap canvas which can be used for rendering graphs, game graphics, or other visual images on the fly”. Along with strong JavaScript support, Canvas is one of the best competitor to RIA development tools like Flash & Silverlight.

To start with HTML5 learning, I thought of migrating my MIX 10 10K coding puzzle developed in Silvelight to HTML5 Canvas element. Below is the puzzle displayed using Canvas element.

The above puzzle is displayed by the below given source code.

<!DOCTYPE html>
<html>
<head>
    <title>Lights On</title>
    <!--[if IE]>
      <script src="http://explorercanvas.googlecode.com/svn/trunk/excanvas.js"></script>
    <![endif]-->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
    <style>
      #container { position: relative; background-color:#005B96; width:550px; height:280px; }   
      #surface { position: relative; background-color:#ffffff; margin:9px; }   
      #title {margin-left:10px; font-family:Verdana; font-size:smaller; color:White; font-weight:bold}
      #description {float:left; margin-left:10px; font-family:Verdana; font-size:smaller; color:White; font-weight:lighter;}
      #reset {float:left; margin-left:10px; font-family:Verdana; font-size:smaller; font-weight:lighter;}
      #clickCount {margin-left:5px; font-family:Verdana; font-size:smaller; font-weight:lighter; color:White; vertical-align:bottom;}
      #clickCounter {margin-left:5px; font-family:Verdana; font-size:smaller; font-weight:lighter; color:White; vertical-align:bottom;}
    </style>
</head>
<body>
    <div id="container">
        <table>
            <tr>
                <td style="width:50%; vertical-align:top;">
                    <span id="title">Turn On the lights in all the cells.</span><br><br>
                    <span id="description">Clicking a cell will Turn On / Off its light, as well as its Horizontal & Vertical adjacent cells.</span><br><br><br><br><br><br><br><br><br><br>
                    <input type="button" id="reset" value="Restart" onclick="reset();" />
                    <span id="clickCount">No of Clicks: </span>
                    <span id="clickCounter">0</span>
                </td>
                <td style="width:50%; vertical-align:middle;">
                    <canvas id="surface">Canvas is not supported</canvas> 
                </td>
            </tr>
        </table>        
    </div>
    <script>
    var rowCount;
    var columnCount;
    var blockWidth;
    var blockHeight;
    var padding;    
    var canvasWidth;
    var canvasHeight;
    var ctx;
    var blocks;
    var canvasLeft;
    var canvasTop;
    var clickCount;
    function init() {
        rowCount = 5;
        columnCount = 5;
        blockWidth = 50;
        blockHeight = 50;
        padding = 1;
        clickCount = 0;
        blocks = new Array(rowCount);
        for (i = 0; i < rowCount; i++) {
            blocks[i] = new Array(columnCount);
            for (j = 0; j < columnCount; j++) {
                blocks[i][j] = 0; // Set as not lighted
            }
        }
        
        ctx = $('#surface')[0].getContext("2d");
        canvasWidth = ((blockWidth + padding) * columnCount) + padding;
        ctx.canvas.width = canvasWidth;
        canvasHeight = ((blockHeight + padding) * rowCount + padding);
        ctx.canvas.height = canvasHeight;
        draw();           
        
        canvasLeft = $("#surface").offset().left;
        canvasTop = $("#surface").offset().top;
        $("#clickCounter").text(clickCount); 
    }
    function rect(x, y, w, h) {
        ctx.beginPath();
        ctx.rect(x, y, w, h);
        ctx.closePath();
        ctx.fill();
    }
    function clear() {
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    }
    function reset() {
        init();
    }
    function draw() {
        //draw blocks
        for (i = 0; i < rowCount; i++) {
            for (j = 0; j < columnCount; j++) {
                if (blocks[i][j] == 0) {
                    ctx.fillStyle = "#D3D3D3";
                }
                else {
                    ctx.fillStyle = "#FF0000";
                }
                rect((j * (blockWidth + padding)) + padding, (i * (blockHeight + padding)) + padding, blockWidth, blockHeight);
            }
        }
    }
    function onClick(e) {
        var x = Math.floor((e.pageX - canvasLeft) / blockWidth);
        var y = Math.floor((e.pageY - canvasTop) / blockHeight);
        var lightState;
        lightState = blocks[y][x] == 1 ? 0 : 1;
        blocks[y][x] = lightState;
        if (y - 1 >= 0) {
            lightState = blocks[y - 1][x] == 1 ? 0 : 1;
            blocks[y - 1][x] = lightState;
        }
        if (x - 1 >= 0) {
            lightState = blocks[y][x - 1] == 1 ? 0 : 1;
            blocks[y][x - 1] = lightState;
        }
        if (y + 1 < rowCount) {
            lightState = blocks[y + 1][x] == 1 ? 0 : 1;
            blocks[y + 1][x] = lightState;
        }
        if (x + 1 < columnCount) {
            lightState = blocks[y][x + 1] == 1 ? 0 : 1;
            blocks[y][x + 1] = lightState;
        }
        draw();
        clickCount += 1;
        $("#clickCounter").text(clickCount);       
    }        
    $(document).ready(function() {
        init();
        $("#surface").click(onClick);
    });
    </script>       
</body>
</html>