Drawing on the HTML5 canvas element

2012

Below is a canvas you can draw on as if your mouse was a pen. This tutorial assumes some basic knowledge of the canvas element and the 2d drawing context. I've listed some additional canvas element references and tutorials below this guide, so be sure to check them out.

Getting started:

Drawing a Path

In order to draw a continuous line, we need to keep track of all the points where the mouse has been. We can use two arrays to track the mouse position, one for x coordinates and one for y coordinates. As we move the mouse, we want to add new coordinates to our arrays, clear, and then update the canvas.

        
  var ctx = document.getElementById('draw-canvas').getContext('2d'),
    penX = [],
    penY = [],
    canvasWidth,
    canvasHeight;

  //set the stroke style
  ctx.strokeStyle = '#000';
  ctx.lineJoin = 'round';
  ctx.lineWidth = 3;

  //listener for mousemove
  function moveUpdate(e){
    //keep saving the new x and y values of the mouse
    penX.push(e.offsetX);
    penY.push(e.offsetY);

    //clear the canvas using it's height and width
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);

    //begin a drawing path
    ctx.beginPath();

    for(var i = 1; i < penX.length; i++){
      //move to an adjacent coordinate, and draw the position
      ctx.moveTo(penX[i - 1], penY[i - 1]);
      ctx.lineTo(penX[i], penY[i]);
    }

    //close the path and draw the line
    ctx.closePath();
    ctx.stroke();
  }

  function init(){
    var drawCanvas = document.getElementById('draw-canvas');

    canvasWidth = drawCanvas.width;
    canvasHeight = drawCanvas.height;

    drawCanvas.addEventListener('mousemove', moveUpdate, false);
  }

  init();
          
        

Draw Events

To make our drawing surface a bit easier to use, we only want to draw when the mouse is being held down and moving. We also want to be able to stop and start drawing a clean path without losing what we've already drawn. We'll need manage mousedown, mousemove, and mouseup events. To allow a clean path, we'll get the current state of the canvas every time you stop drawing, and then use it as the base when you draw again. Saving a version of the canvas can be done using getImageData and putImageData. By using an stack for draw state, we could also implement an undo feature later.

          
  var ctx = document.getElementById('draw-canvas').getContext('2d'),
    penX = [],
    penY = [],

    //save states for imageData
    saveStates = [],

    //flags to manage when we can start drawing
    drawOn = false,
    trackingInitialized = false,
    canvasWidth,
    canvasHeight;

    //set the stroke style
    ctx.strokeStyle = '#000';
    ctx.lineJoin = 'round';
    ctx.lineWidth = 3;

  //listener for tracking our pen and redrawing
  function moveUpdate(e){
    if(drawOn === true){
      //mousedown === true

      //keep saving the new x and y values of the mouse
      penX.push(e.offsetX);
      penY.push(e.offsetY);

      //clear the canvas and draw the last saved state before the new path
      ctx.clearRect(0, 0, canvasWidth, canvasHeight);
      ctx.putImageData(saveStates[saveStates.length - 1], 0, 0);

      ctx.beginPath();

      for(var i = 1; i < penX.length; i++){
        ctx.moveTo(penX[i - 1], penY[i - 1]);
        ctx.lineTo(penX[i], penY[i]);
      }

      ctx.closePath();
      ctx.stroke();
    }
  }

  function downUpdate(e){
    if(trackingInitialized === false){
      //we want to initialize the state and path coordinates
      penX.push(e.offsetX);
      penY.push(e.offsetY);

      saveStates.push(ctx.getImageData(0, 0, canvasWidth, canvasHeight));
      trackingInitialized = true;
    }
    drawOn = true;
  }

  //when the user lets go, we need to turn drawing off
  //push a new state, and clear the path arrays
  function letGoUpdate(e){
    saveStates.push(ctx.getImageData(0, 0, canvasWidth, canvasHeight));
    drawOn = false;

    penX.splice(0, penX.length);
    penY.splice(0, penY.length);
  }

  function init(){
    var drawCanvas = document.getElementById('draw-canvas');

    canvasWidth = drawCanvas.width;
    canvasHeight = drawCanvas.height;

    //draw handlers
    drawCanvas.addEventListener('mousemove', moveUpdate, false);
    drawCanvas.addEventListener('mousedown', downUpdate, false);
    drawCanvas.addEventListener('mouseup', letGoUpdate, false);
  }

  init();
          
        

Clear and Reset

Clearing and resetting our canvas and variables is pretty straight forward.

          
  function clearCanvas(){
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    saveStates.splice(0, saveStates.length);
    trackingInitialized = false;
  }
          
        

Code available here or in a gist.

HTML5 Canvas References

comments powered by Disqus