Scribble app with HTML5

The “canvas” is one of the new elements in HTML5, it provides a drawing surface and APIs for yes you guessed it…drawing. The actual drawing is done through JavaScript. I’m going to show you a simple Scribble app that uses the canvas. It should be very straightforward and will help you get familiar with the canvas.

Before we begin I want to have a word about development tools for HTML5. Basically you can use any text editor to write HTML and JavaScript code. There are more sophisticated editors that provide syntax highlighting and basic debugging features such as Notepad++.

For this app I used Visual Studio 2010 SP1. The reason for my choice is that VS2010 SP1 now supports HTML5 and CSS3. That means you get syntax highlighting , Intellisense and some debugging capabalities (IE only). I know VS2010 is not free but you can get your hand on Microsoft’s Visual Web Developer 2010 Express which is free and also supports HTML5. Microsoft will be updating Visual Studio every 3 months to keep it up to date with the latest HTML5 standard changes.

Enough talking, lets dive into the code. First we will design the HTML page, on top we will have 2 drop down lists for selecting the colour and the line width along with a button to clear the canvas. Below these controls we will add the canvas. To achieve this I am using a simple table layout. Here is the HTML for the page:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Scribble</title>
</head>
<body>
    <table>
        <tr>
            <td>
                Colour:
            </td>
            <td>
                <select id="ddlColour" onchange="ddlColour_onChange();">
                    <option value="#000000">Black</option>
                    <option value="#FF0000">Red</option>
                    <option value="#00FF00">Green</option>
                    <option value="#0000FF">Blue</option>
                </select>
            </td>
            <td>
                Line Width:
            </td>
            <td>
                <select id="ddlLineWidth" onchange="ddlLineWidth_onChange();">
                    <option value="1">1</option>
                    <option value="3">3</option>
                    <option value="5">5</option>
                    <option value="10">10</option>
                </select>
            </td>
            <td>
                <input id="btnClear" value="Clear" type="button" onclick="btnClear_onClick();" />
            </td>
        </tr>
    </table>
    <canvas id="mainCanvas" width="400" height="400" style="border: 1px solid #000;">
    </canvas>
</body>
</html>

There are few things I want to point out, the first line in the HTML file:

<!DOCTYPE html>

This line indicates that the HTML version is HTML5. You probably noticed the canvas element, there is nothing fancy about the markup for the canvas. The width and height are set and the canvas is given a border (so we actually know where we are drawing) and the canvas is given an ID so we can access it in JavaScript. I also added event handlers to the drop down lists and the button, in a bit we will see what these do.

If you open the HTML page in a browser (preferably Chrome) you will see a page that looks something like this:

At this point the app does nothing, it’s still not functional. We need to add the JavaScript Code that will contain the drawing logic.

We need some variables which we will use later.

var doDraw = false;
var mainCanvas, canvasContext;
var xCoords = new Array();
var yCoords = new Array();
var colour = new Array();
var isMouseDownEvent = new Array();
var lineWidth = new Array();

The first and most important function is the “window.onload” function

window.onload = function () {

    //Get canvas element
    mainCanvas = document.getElementById('mainCanvas');
    if (!mainCanvas) {
        alert('No canvas found !');
        return;
    }

    //Check for canvas support
    if (!mainCanvas.getContext) {
        alert('Sorry, your browser does not support the canvas element :(');
        return;
    }

    //Get canvas context
    canvasContext = mainCanvas.getContext('2d');

    //Attach the event handlers
    mainCanvas.addEventListener('mousemove', mainCanvas_mouseMove, false);
    mainCanvas.addEventListener('mousedown', mainCanvas_mouseDown, false);
    mainCanvas.addEventListener('mouseup', mainCanvas_mouseUp, false);
}

This function is responsible for 2 main things:

  1. Checking for canvas support in the browser. This is done by retrieving the canvas object and checking for the “getContext” method. If this method exists in the returned object then the browser supports the canvas element, otherwise the user should upgrade his browser.
  2. Attach the event handlers to the canvas element.

Notice that we are retrieving the “2D” context. The 2D context is responsible for everything 2D, it provides the APIs necessary for 2D drawing.

Now we want to change the line colour and width when the user changes the value from the drop down lists.

//Change the drawing colour
function ddlColour_onChange() {

    var ddlColour = document.getElementById('ddlColour');
    canvasContext.strokeStyle = ddlColour.options[ddlColour.selectedIndex].value;
}

//Change the line width
function ddlLineWidth_onChange() {

    var ddlLineWidth = document.getElementById('ddlLineWidth');
    canvasContext.lineWidth = ddlLineWidth.options[ddlLineWidth.selectedIndex].value;
}

There is nothing fancy about this code, we set the line colour by assigning a value to “canvasContext.strokeStyle” and we set the line width by assigning a value to “canvasContext.lineWidth”.

Now onto the drawing itself, we want to control what happens when the user presses down on a mouse button, what happens when the user moves the mouse and what happens when the user lifts up the mouse button.

When a mouse button is pressed down we want to record the click location, the current properties of the canvas.

//Start drawing when a mouse button is pressed
function mainCanvas_mouseDown(args) {
    xCoords.push(args.layerX);
    yCoords.push(args.layerY);
    colour.push(canvasContext.strokeStyle);
    lineWidth.push(canvasContext.lineWidth);
    isMouseDownEvent.push(true);
    doDraw = true;
}

The “doDraw” variable is set to true, this variable will be used to indicate that a mouse button is pressed. The reason for this is that we do not want to draw anything on the canvas if the mouse is moving without pressing down onto a button. Here is he variable in action in the “mainCanvas_mouseMove” function:

//The event handler that does the actual drawing
function mainCanvas_mouseMove(args) {

    if (doDraw) {

        xCoords.push(args.layerX);
        yCoords.push(args.layerY);
        colour.push(canvasContext.strokeStyle);
        lineWidth.push(canvasContext.lineWidth);
        isMouseDownEvent.push(false);
        redraw();
    }
}

If “doDraw” is set to “false” no drawing will take place when the mouse moves. Again we are saving the current mouse location and canvas properties. Then the “redraw” function is called (we’ll get to that in a moment).

When the mouse button is released we want to stop drawing on the canvas, “mainCanvas_mouseUp” does exactly that:

//Stop drawing when the mouse is released
function mainCanvas_mouseUp(args) {
    doDraw = false;
}

Onto the “redraw” function, this function is responsible for drawing the lines onto the canvas:

//Do the actual drawing
function redraw() {
    //Clear canvas
    mainCanvas.width = mainCanvas.width;

    for (i = 0; i < xCoords.length; i++) {

        canvasContext.beginPath();
        if (!i || isMouseDownEvent[i]) {
            canvasContext.moveTo(xCoords[i], yCoords[i]);
        }
        else {
            canvasContext.moveTo(xCoords[i - 1], yCoords[i - 1]);
        }

        canvasContext.lineTo(xCoords[i], yCoords[i]);

        canvasContext.closePath();

        //Stroke properties
        canvasContext.strokeStyle = colour[i];
        canvasContext.lineWidth = lineWidth[i];
        canvasContext.lineCap = 'round';
        canvasContext.lineJoin = 'round';
        canvasContext.stroke();
    }
}

First we want to clear the canvas, the easiest way to do this is to set the canvas width property to itself. “canvasContext.moveTo” moves the cursor to a given X,Y coordinate. “canvasContext.lineTo” draws a line to a given X,Y coordinate.

Surprisingly “canvasContext.lineTo” doesn’t render any drawing, it just draws invisible lines. To actually ink in the line we call the “canvasContext.stroke” function. Before calling it we tell the canvasContext how we want it to render our lines (strokeStyle, lineWidth…etc.)

When you string it all together you should have a working Scribble app 😀

That was a simple demo of the HTML5 canvas element. You can get the source code for the Scribble app here.

I’m sure there are more sophisticated effects that can be achieved using the canvas APIs but I’m still learning about HTML5 🙂 All comments are appreciated.

Advertisements

8 thoughts on “Scribble app with HTML5

  1. I love the app and have learned a lot from your post. Just one question, if you are still tracking this: I am trying to set the default color to red. For the life of me I can’t figure out how to do it. Would you mind letting me know? Thank you.

    1. You need to do 2 things. First of all you have to make the color you want selected by default in the drop down list. This can be done by adding the selected=”true” attribute to the option you want to make default.

      Second this is to initialize the canvas with the selected color, this is done by adding these 2 lines as the end of the window.onload function:

      var ddlColour = document.getElementById('ddlColour');
      canvasContext.strokeStyle = ddlColour.options[ddlColour.selectedIndex].value;

      What this does is that it loads the selected color from the drop down list (this color will be the default color since this code is executed on page load) and sets the canvas stroke style with it.

      I'm glad that you enjoyed the post 🙂

  2. Hi Mustafa

    I also really appreciated your post. Had a couple of follow up Qs – if rendering a background image into the canvas and then wanting users to ‘shade’ in a part of it, do you think the better user experience would be attained with the scribble on canvas as per this post or joining the dots using say a jsdraw2d polygon?

    If scribble app could work for diagram shading, how are the values stored in a db in order to render the shading back on the page on return? Are these multiple x y coordinates?

    Using .NET 4 aspx for our web apps and MS VS 2010 IDE

    Thanks!

  3. In terms of user experience I think scribbling on the canvas is more realistic than joining dots, on the other hand I thinks it will be more difficult to store and retrieve later.

    An approach I have in mind is to save the user’s shading as an image and when retrieving add it as a layer on top of the original drawing. I’m not really sure how this can be implemented

    1. Thank you for your prompt reply post !

      Image save sounds like a good option. Could be neat for analytics also as we could measure the before and after pixel size of the shaded area / image instead of multiple points on a polygon which might work visually but it would be hard to make any sense of the stored data.

  4. I know this is really an old post, but would it be possible for you to illustrate how to trap touch events for making this nice webapp work on mobile screens?

    1. Nevermind. I did this the following: Added the following event handlers:
      mainCanvas.addEventListener(‘touchstart’, mainCanvas_mouseDown, false);
      mainCanvas.addEventListener(‘touchmove’, mainCanvas_mouseMove, false);
      mainCanvas.addEventListener(‘touchend’, mainCanvas_mouseUp, false);

      Also, in mainCanvas_mouseMove, I added another line towards the end in the if(doDraw) block:
      args.preventDefault();

      This is to prevent scrolling when drawing using a finger.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s