A Gentle Introduction to Making HTML5 Canvas Interactive

I wrote a book on HTML5, including three chapters on Canvas! Buy it here.

This is a big overhaul of one of my tutorials on making and moving shapes on an HTML5 Canvas. This new tutorial is vastly cleaner than my old one, but if you still want to see that one or are looking for the concept of a “ghost context” you can find that one here.

This tutorial will show you how to create a simple data structure for shapes on an HTML5 canvas and how to have them be selectable. The finished canvas will look like this:


This text is displayed if your browser does not support HTML5 Canvas.

Click to drag the shapes. Double click to add a new shape.

We’ll be going over a few things that are essential to interactive apps such as games (drawing loop, hit testing), and in later tutorials I will probably turn this example into a small game of some kind. The code also contains simple examples of using JavaScript prototypes and closures. I will try to accommodate JavaScript beginners but this introduction does expect at least a rudimentary understanding of JS. Not every piece of code is explained in the text, but almost every piece of code is thoroughly commented!

The HTML5 Canvas

A Canvas is made by using the <canvas> tag in HTML:

<canvas id="canvas" width="400" height="300">
    This text is displayed if your browser does not support HTML5 Canvas.
   </canvas>

A canvas isn’t smart: it’s just a place for drawing pixels. If you ask it to draw something it will execute the drawing command and then immediately forget everything about what it has just drawn. This is sometimes referred to as an immediate drawing surface, as contrasted with SVG as a retained drawing surface, since SVG keeps a reference to everything drawn. Because we have no such references, we have to keep track ourselves of all the things we want to draw (and re-draw) each frame.

Canvas also has no built-in way of dealing with animation. If you want to make something that you’ve drawn move, you have to clear the entire canvas and redraw all of the objects with one or more of them in a new location. And you have to do it often, of course, if you want a semblance of animation or motion.

So we’ll need to add:

  1. Code for keeping track of objects
  2. Code for keeping track of canvas state
  3. Code for mouse events
  4. Code for drawing the objects as they are made and move around

The things we draw

To keep things simple for this example we will start with a Shape class to represent rectangular objects.

JavaScript doesn’t technically have classes, but that isn’t a problem because JavaScript programmers are very good at playing pretend. Functionally (well, for our example) we are going to have a Shape class and create Shape instances with it. What we are really doing is defining a function named Shape and adding functions to Shape’s prototype. You can make new instances of the function Shape and all instances will share the functions defined on Shape’s prototype.

If you’ve never encountered prototypes in JavaScript before or if the above sounds confusing to you, I highly recommend reading Crockford’s JavaScript: The Good Parts. The book is an intermediate overview of JavaScript that gives a good understanding of why programmers choose to create objects in different ways, why certain conventions are frowned upon, and just what makes JavaScript so different.

Here’s our Shape constructor and one of the two prototype methods, which are comparable to a class instance methods:

// Constructor for Shape objects to hold data for all drawn objects.
// For now they will just be defined as rectangles.
function Shape(x, y, w, h, fill) {
  // This is a very simple and unsafe constructor. 
  // All we're doing is checking if the values exist.
  // "x || 0" just means "if there is a value for x, use that. Otherwise use 0."
  this.x = x || 0;
  this.y = y || 0;
  this.w = w || 1;
  this.h = h || 1;
  this.fill = fill || '#AAAAAA';
}

// Draws this shape to a given context
Shape.prototype.draw = function(ctx) {
  ctx.fillStyle = this.fill;
  ctx.fillRect(this.x, this.y, this.w, this.h);
}

They are pretty self-explanatory. The shape constructor has “defaults” if you give it no arguments, and calling draw on a shape will set the fill and draw a rectangle on the given context corresponding to the measurements of the Shape.

Keeping track of canvas state

We’re going to have a second class (function) called CanvasState. We’re only going to make one instance of this function and it will hold all of the state in this tutorial that is not associated with Shapes themselves.

CanvasState is going to foremost need a reference to the Canvas and a few other field for convenience. We’re also going to compute and save the border and padding (if there is any) so that we can get accurate mouse coordinates.

In the CanvasState constructor we will also have a collection of state relating to the objects on the canvas and the current status of dragging. We’ll make an array of shapes to keep track of whats been drawn so far, a flag “dragging” that will be true while we are dragging, a field to keep track of which object is selected and a “valid” flag that will be set to false will cause the Canvas to clear everything and redraw.

I’m going to add a bunch of variables for keeping track of the drawing and mouse state. I already added shapes[] to keep track of each object, but we’ll also need a var for the canvas, the canvas’ 2d context (where all drawing is done), whether the mouse is dragging, width/height of the canvas, and so on.

function CanvasState(canvas) {
  
  // ...

  // I removed some setup code to save space
  // See the full source at the end


  // **** Keep track of state! ****
  
  this.valid = false; // when set to true, the canvas will redraw everything
  this.shapes = [];  // the collection of things to be drawn
  this.dragging = false; // Keep track of when we are dragging
  // the current selected object.
  // In the future we could turn this into an array for multiple selection
  this.selection = null;
  this.dragoffx = 0; // See mousedown and mousemove events for explanation
  this.dragoffy = 0;

Mouse events

We’ll add events for mousedown, mouseup, and mousemove that will control when an object starts and stops dragging. We’ll also disable the selectstart event, which stops double-clicking on canvas from accidentally selecting text on the page. Finally we’ll add a double-click (dblclick) event that will create a new Shape and add it to the CanvasState’s list of shapes.

The mousedown event begins by calling getMouse on our CanvasState to return the x and y position of the mouse. We then iterate through the list of Shapes to see if any of them contain the mouse position. We go through them backwards because they are drawn forwards, and we want to select the one that appears topmost, so we must find the potential shape that was drawn last.

If we find a shape then we want to select it. We save the offset, save a reference to that shape as the CanvasState’s this.selection, set this.dragging to true and set the this.valid flag to false. Already we’ve used most of our state! Finally if we didn’t find any objects we need to see if there was a selection saved from last time. Since we clicked on nothing, we obviously didn’t click on the already-selected object, so we want to “deselect” and clear the selection reference. Clearing the selection means we will have to clear the canvas and redraw everything without the selection ring, so we set the valid flag to false.


  // ...
  // (We are still in the CanvasState constructor)

  // This is an example of a closure!
  // Right here "this" means the CanvasState. But we are making events on the Canvas itself,
  // and when the events are fired on the canvas the variable "this" is going to mean the canvas!
  // Since we still want to use this particular CanvasState in the events we have to save a reference to it.
  // This is our reference!
  var myState = this;
  
  //fixes a problem where double clicking causes text to get selected on the canvas
  canvas.addEventListener('selectstart', function(e) { e.preventDefault(); return false; }, false);
  // Up, down, and move are for dragging
  canvas.addEventListener('mousedown', function(e) {
    var mouse = myState.getMouse(e);
    var mx = mouse.x;
    var my = mouse.y;
    var shapes = myState.shapes;
    var l = shapes.length;
    for (var i = l-1; i >= 0; i--) {
      if (shapes[i].contains(mx, my)) {
        var mySel = shapes[i];
        // Keep track of where in the object we clicked
        // so we can move it smoothly (see mousemove)
        myState.dragoffx = mx - mySel.x;
        myState.dragoffy = my - mySel.y;
        myState.dragging = true;
        myState.selection = mySel;
        myState.valid = false;
        return;
      }
    }
    // havent returned means we have failed to select anything.
    // If there was an object selected, we deselect it
    if (myState.selection) {
      myState.selection = null;
      myState.valid = false; // Need to clear the old selection border
    }
  }, true);

The mousemove event checks to see if we have set the dragging flag to true. If we have it gets the current mouse positon and moves the selected object to that position, remembering the offset of where we were grabbing it. If the dragging flag is false the mousemove event does nothing.

  canvas.addEventListener('mousemove', function(e) {
    if (myState.dragging){
      var mouse = myState.getMouse(e);
      // We don't want to drag the object by its top-left corner,
      // we want to drag from where we clicked.
      // Thats why we saved the offset and use it here
      myState.selection.x = mouse.x - myState.dragoffx;
      myState.selection.y = mouse.y - myState.dragoffy;   
      myState.valid = false; // Something's dragging so we must redraw
    }
  }, true);

The mouseup event is simple, all it has to do is update the CanvasState so that we are no longer dragging! So once you lift the mouse, the mousemove event is back to doing nothing.

canvas.addEventListener('mouseup', function(e) {
    myState.dragging = false;
  }, true);

The dblclick event we’ll use to add more Shapes to our canvas. It calls addShape on the CanvasState with a new instance of Shape. all addShape does is add the argument to the list of Shapes in the CanvasState.

// double click for making new Shapes
  canvas.addEventListener('dblclick', function(e) {
    var mouse = myState.getMouse(e);
    myState.addShape(new Shape(mouse.x - 10, mouse.y - 10, 20, 20,
                               'rgba(0,255,0,.6)'));
  }, true);

There are a few options I implemented, what the selection ring looks like and how often we redraw. setInterval simply calls our CanvasState’s draw method. Our interval of 30 means that we call the draw method every 30 milliseconds.

// **** Options! ****
  
  this.selectionColor = '#CC0000';
  this.selectionWidth = 2;  
  this.interval = 30;
  setInterval(function() { myState.draw(); }, myState.interval);
}

Drawing

Now we’re set up to draw every 30 milliseconds, which will allow us to continuously update the canvas so it appears like the shapes we drag are smoothly moving around. However, drawing doesn’t just mean drawing the shapes over and over; we also have to clear the canvas on every draw. If we don’t clear it, dragging will look like the shape is making a solid line because none of the old shape-positions will go away.

Because of this, we clear the entire canvas before each Draw frame. This can get expensive, and we only want to draw if something has actually changed within our framework, which is why we have the “valid” flag in our CanvasState.

After everything is drawn the draw method will set the valid flag to true. Then, once we do something like adding a new Shape or trying to drag a Shape, the state will get invalidated and draw() will clear, redraw all objects, and set the valid flag again.

// While draw is called as often as the INTERVAL variable demands,
// It only ever does something if the canvas gets invalidated by our code
CanvasState.prototype.draw = function() {
  // if our state is invalid, redraw and validate!
  if (!this.valid) {
    var ctx = this.ctx;
    var shapes = this.shapes;
    this.clear();
    
    // ** Add stuff you want drawn in the background all the time here **
    
    // draw all shapes
    var l = shapes.length;
    for (var i = 0; i < l; i++) {
      var shape = shapes[i];
      // We can skip the drawing of elements that have moved off the screen:
      if (shape.x > this.width || shape.y > this.height ||
          shape.x + shape.w < 0 || shape.y + shape.h < 0) continue;
      shapes[i].draw(ctx);
    }
    
    // draw selection
    // right now this is just a stroke along the edge of the selected Shape
    if (this.selection != null) {
      ctx.strokeStyle = this.selectionColor;
      ctx.lineWidth = this.selectionWidth;
      var mySel = this.selection;
      ctx.strokeRect(mySel.x,mySel.y,mySel.w,mySel.h);
    }
    
    // ** Add stuff you want drawn on top all the time here **
    
    this.valid = true;
  }
}

We go through all of shapes[] and draw each one in order. This will give the nice appearance of later shapes looking as if they are on top of earlier shapes. After all the shapes are drawn, a selection handle (if there is a selection) gets drawn around the shape that this.selection references.

If you wanted a background (like a city) or a foreground (like clouds), one way to add them is to put them before or after the main two drawing bits. There are often better ways though, like using multiple canvases or a CSS background-image, but we won’t go over that here.

Getting mouse coordinates on Canvas

Getting good mouse coordinates is a little tricky on Canvas. You could use offsetX/Y and LayerX/Y, but LayerX/Y is deprecated in webkit (Chrome and Safari) and Firefox does not have offsetX/Y.

The most bulletproof way to get the correct mouse position is shown below. You have to walk up the tree adding the offsets together. Then you must add any padding or border to the offset. Finally, to fix coordinate problems when you have fixed-position elements on the page (like the wordpress admin bar or a stumbleupon bar) you must add the <html>’s offsetTop and offsetLeft.

Then you simply subtract that offset from the e.pageX/Y values and you’ll get perfect coordinates in almost every possible situation.

// Creates an object with x and y defined,
// set to the mouse position relative to the state's canvas
// If you wanna be super-correct this can be tricky,
// we have to worry about padding and borders
CanvasState.prototype.getMouse = function(e) {
  var element = this.canvas, offsetX = 0, offsetY = 0, mx, my;
  
  // Compute the total offset
  if (element.offsetParent !== undefined) {
    do {
      offsetX += element.offsetLeft;
      offsetY += element.offsetTop;
    } while ((element = element.offsetParent));
  }

  // Add padding and border style widths to offset
  // Also add the offsets in case there's a position:fixed bar
  offsetX += this.stylePaddingLeft + this.styleBorderLeft + this.htmlLeft;
  offsetY += this.stylePaddingTop + this.styleBorderTop + this.htmlTop;

  mx = e.pageX - offsetX;
  my = e.pageY - offsetY;
  
  // We return a simple javascript object (a hash) with x and y defined
  return {x: mx, y: my};
}

At long last

From here its just a few lines to draw some shapes to move around. We make one instance of CanvasState, passing it a reference to the canvas we want to use, then we can add any number of new shapes to it. The code below produces the example at the top of this page:

var s = new CanvasState(document.getElementById('canvas1'));
s.addShape(new Shape(40,40,50,50)); // The default is gray
s.addShape(new Shape(60,140,40,60, 'lightskyblue'));
// Lets make some partially transparent
s.addShape(new Shape(80,150,60,30, 'rgba(127, 255, 212, .5)'));
s.addShape(new Shape(125,80,30,80, 'rgba(245, 222, 179, .7)'));

There are a few little methods I added that are not shown, such as Shape’s method to see if a point is inside its bounds. You can see and download the full demo source here.

Now that we have a basic structure down, it is easy to write code that handles more complex shapes, like paths or images or video. Rotation and scaling these things takes a bit more work, but is quite doable with the Canvas and our selection method is already set up to deal with them.

If you would like to see this code enhanced in future posts (or have any fixes), let me know.

I wrote a book on HTML5, including three chapters on Canvas! Buy it here.

  • Mauro

    I have a question — I can’t find documentation for getMouse() anywhere.  Is this a function you’ve defined or is it part of the language definition?  Thanks!

    • http://www.simonsarris.com simonsarris

      Yes I’ve defined it, search the documentation for “CanvasState.prototype.getMouse”

      • Mauro

        Ahhhh, there it is.  That makes a lot more sense now that I see it; thank you very much!

  • Philippe Suter

    Shouldn’t the ‘return’ in CanvasState.prototype.draw be a ‘continue’ ? 

    • http://www.simonsarris.com simonsarris

      oh shoot, you’re right!

  • http://twitter.com/ludflu Jim

    This is a great tutorial. Thanks!

  • Anonymous

    Nice tutorial, thanks for sharing. I wish canvas made some of these things easier, and had a simpler method for getting the relative mouse position. Having a hard time deciding between it and SVG for my app. 

    • http://www.simonsarris.com simonsarris

      I wrote a bit of analysis on SVG versus Canvas here: http://stackoverflow.com/a/5883032/154112

      Hope that helps. Let me know if you want to discuss your specific requirements and what might be best for your case.

      • Anonymous

        awesome, thanks!

  • Leigh Kaszick

    I can’t see where you’ve defined CanvasState.prototype.addShape = function(shape){ this.shapes.push(shape); return this;};

    • http://www.simonsarris.com simonsarris

      I didn’t include it in the text of the tutorial but it is in the JS file which I link to  on this page: http://simonsarris.com/project/canvasdemo/shapes.html

      You can see the js file in the whole on that page or on its github: https://github.com/simonsarris/Canvas-tutorials/blob/master/shapes.js

  • Avron

    A while back I began developing HTML5 software for maps (GIS) using shapefiles (vector files for map layers), which I have converted into js files 
    (see http://www.avronp.com/nsn/HTML5/default.html).

    The API for selection here is fantastic. How would you select a polygon, a line, a group of these?

    Would you have any interest in doing common development for GIS maps?

    I am mainly a javascript and Silverlight developer working for the last few years with GIS.

    Please communicate by email (avronpolakow@gmail.com)

    • Kevin H

      Thanks very much Simon for the great tutorial!

      If someone is a beginner like me and is having trouble getting the code running make sure your html body tag looks like this:

      and change ‘canvas1′ in the initialize function to ‘canvas.’ (This is a mistake.)

      These two things had me scratching me head for a while.

      • Kevin H

        It edited out my html, its supposed to look like this, just remove the spaces. “

  • Christopher Tipper

    This turns out to be much simpler using Google Web Toolkit. This brings the power of Java and the Java APIs (g2d and gwt-geom). http://www.e-conomist.fsnet.co.uk/app/fondu.html

    • http://shamansir.github.com/ Ulric Wilfred

      As I know, currently there is no some “Interactive Canvas” (no more, no less, no game engines, but drag-n-drop ability) framework for GWT. It’d be cool to have one.

  • Mattinn82

    Thanks for the tutorial, it took me a while to get it to work as it always claimed that the canvas was null..

    the reason was that my canvas id was “canvas” but in the init function its named it “canvas1″ works great after removing the “1″ :D

  • Scott Owen

    Great tutorial! Really helpful. Can you explain where newly drawn/moved shapes are being added to the shapes array? I’m confused about where that’s happening.

    • http://www.simonsarris.com simonsarris

      The dblclick listener calls ‘addShape’

      // double click for making new Shapes
      canvas.addEventListener(‘dblclick’, function(e) {
      var mouse = myState.getMouse(e);
      myState.addShape(new Shape(mouse.x – 10, mouse.y – 10, 20, 20,
      ‘rgba(0,255,0,.6)’));
      }, true);

      The code for addShape is in the whole source file, I didn’t include it in the text of  my tutorial:

      CanvasState.prototype.addShape = function(shape) {
      this.shapes.push(shape);
      this.valid = false;
      }

      You can find the full demo source here: http://simonsarris.com/project/canvasdemo/shapes.html

  • Pingback: wp-cv plugin creation | Guiem

  • http://mitchs.co Mitch Samuels

    Hey, awesome tuturoail. Can this work with images?

    Lets say I have an image, “imgCat” placed on the canvas. How can I put that into the above code so it makes the image draggable?

    • Anonymous

      Yup buddy, I did just that, just replace the fill with img:
      img = new Image();
      img.src = “something/pic.jpg”;

      and then change the draw method to:
      Shape.prototype.draw = function(ctx) {
      ctx.drawImage(this.img, this.x, this.y, this.w, this.h);
      }
      works like a charm.

      • Jason Comparetto

        Hi Neil, based on your postings here it sounds like you know alot about this subject. I am writing a small web app like this and would like to contact you via private message. The app may be a little too complicated for me and I may just pay someone to do it. If you are at all interested, please write to me at jason at pro pixel media dot com (no spaces of course). Thank you

      • Cheri

        Hi nrobin, I tried modifying this for images with no luck- no luck for days. Where did you replace the fill?

  • exor

    Hi 
    Am new to HTML5 , Your tutorial was very helpful. I would also like to know, how can i add text to the movable rectangle .

    Regards
    exor

  • Rico

    hey, how would I apply this to Images imported ?

  • Rico

    have to import images from URL and would like to make them dropable

  • Anonymous

    Thanks buddy, great intro, however it doesn’t seem to work on iOS devices, i had to add listeners for touchstart, touchmove and touchend to get it working.

  • Hidra

    Great post. I have a theoretical question: do you know any better algorithm to find the clicked shape? It’s ok to iterate over all shapes to find the clicked one in a list of 10 shapes, but it can be slow for a list with thousands of objects. And I’m not even talking about mouse hover (my real goal). Thank you.

    • http://www.facebook.com/profile.php?id=736165902 David Talaga

      By “better” I assume you mean faster. You could create an array with the object number of the front-most object at each position in the canvas. It requires more memory and the overhead to build the map, but finding the object once the map is built would be very fast.

    • Zeqrcat

      A quadtree is what you’re looking for.

  • Pingback: canvas draw

  • Vivi

    Great article, just wanted to know if we can set the Background of the canvas
    ?

    • Spidernaut.

      Yes, we can.

  • Guest

    you are a genius man!!

  • http://acidnine.myopenid.com/ Brandon

    I love it!!! Thank you so much. I added an on double click shape remove ability…

    [***Inside CanvasState***]
      canvas.addEventListener(‘dblclick’, function(e) {
        var mouse = myState.getMouse(e);
        // check if we dblclick on existing shape
        for (var i = myState.shapes.length-1; i >= 0; i–) {
          if (myState.shapes[i].contains(mouse.x, mouse.y)) {
            myState.removeShape(i);//found one, remove it
            myState.selection = null;//clear the selection
            return;
          }
        }
        // dblclick on empty space creates new shape
        myState.addShape(new Shape(mouse.x – 10, mouse.y – 10, 20, 20, ‘rgba(0,255,0,.6)’));
      }, true);

    [***Below addShape***]
    CanvasState.prototype.removeShape = function(i) {
      this.shapes.splice(i, 1);
      this.valid = false;
    }

    *Only problem is it will only remove the uppermost object that was dbl clicked on.. but clicking and dragging only drags the top most shape so I’m not worried.

  • ammulder

    Really nice — the bit on finding accurate mouse coordinates was especially helpful

  • Tom Smith

    Though HTML5 has come a long way, it still falls short of having the same kind of interactivity as Flash. It’s a big issue (for me anyway) when I have to code 10x more for something that can easily be done using another language/application/program. Until HTML5 can provide the same amount of functionality as Flash with the same amount of coding (or less) I wouldn’t consider it a viable solution for the future of the web.

    • Anonymous

      Just wait for the APIs to come rolling out.

      In fact, I think there’s already a program that gives an advanced interface like flash, and builds the HTML5 as efficiently as possible.

  • Andrearosani

     Nice tutorial, thanks for sharing. I need to save the mousepositions at some point to a log file, server side. Do you think that could be possible? Thank you!

  • Jonathan Dray

    About the wordpress admin bar, it changes the html element style to

    html {
    margin-top:28px !important;
    }
    and then it is placed in the margin with position: fixed.

    see : http://blogs.valpo.edu/systems/2012/02/02/wordpress-admin-bar-gotcha/

    This case is not catched by the getMouse function.

    Should I try to add the html margin values to make it work ? or is it unlikly to
    have other websites with the same behaviour and I should just ignore it ?

  • Hanzel

    Cool article, is it possible to make the selected item resizeable.

  • Gabriel Doty

    Great post – however I noticed that you did not include the code for the “contains” prototype you added to the shape object, this was essential to get your demonstration working, but great tutorial overall, thank you!!

  • Matt

    Will you able to rotate the elements? I could not get any solution for that, because if I rotate, I would not be able to re-size the elements.

  • Ryan Bennett

    Thanks for this guide. I was confused as heck in the beginning, but after some experimenting with prototypes and methods, it makes allot of sense now. It’s funny because a week ago I thought JavaScript sucked and I hardly knew anything about it.. Now I’m developing a.. well you’ll see in time. :) I’m up to 600 lines of code, but my next draft will probably be much less. This guide has helped a ton in getting me to not only learn how the canvas API works, but also efficient ways to code in JS using prototypes and objects within arrays and such. I’m certainly going to take a look at your book and order it ASAP.

  • Kristoff

    Great tut! Really; combining relevant real world uses of prototyping and objects in one swoop with canvas – good luck with the book – ill b checking it out :)

  • Henrique Sanábio Vilela

    Hi Niels, could you please explain to me how you did that trick using listeners? I’ve tried it on Android and I am facing problems moving the objects. Thank you.

  • http://twitter.com/JSushil sushil bharwani

    Great Tutorial, but i wonder i have to do so much just to achieve such a small functionality. If the complexity scales to even 10x of it drawing redrawing and getting hold of all the objects can become much complex. Wasnt flash much much better than this. Isnt is possible to combine easiness of Flash media development with availabilty, lightness of HTML5 canvas.

  • Antoine

    Thanks for this tutorial. I am amazed at how rubbish Canvas is though… I mean, SVG have been around for what, 10 years? It’s a much better and useful language in my opinion, at least you can easily keep track of what’s going on and add events to the shapes straight away… I really don’t know where “HTML5″ wants to go with this Canvas element, but it surely will need some crazy (and heavy) library implementation before it can match with SVG, not even talking about Flash here!! I don’t get what the Canvas fuss is all about really… It’s a commercial buzz word used by corporations to promote its use, but why???

  • sh069

    hi simon thats cool.. but i am trying to select two shapes at a time,and if selected a line should be drawn between them…Please could you help on that…

  • Guest

    HELLO ! I am a beginner and I can’t run the code. Someone can give me the complete code please. Thank you in advance .

  • Peter

    HELLO ! I am a beginner and I can’t run the code. Can Someone give me the complete code please ???? Thank you in advance .

  • San

    Fantastic tutorial. Loved reading this…You have introduced the topic in a very simple but powerful manner.. Definitely looking forward to your book.

  • millen

    I really enjoyed your post; it was a great way to present the canvas. I have one suggestion. About half way down function myMove(e) (line 296 or so) a loop is created to iterate through the selection handles. Inside that loop is a switch statement determining the current selection handle and setting the cursor value.. Since the current selection handle is already known (from the loop index) and the cursor values are hard coded, why not just create an array of cursor styles (i.e. var cursorStyle = new Array(“nw-resize”, “n-resize”, “ne-resize”,…) outside the loop and inside the loop below the “invalidate()” command do this: this.style.cursor = cursorStyle[i]; Then delete that entire switch command.

  • Andrew

    Thanks for the great tutorial, as you said in the intro you were thinking about adding collision detection. I’m trying to figure this out myself, is there anyway to get the x or y of specific drawn object?

    I would think you could define a box to a variable, ( var foo = s.addShape(new Shape(40,40,50,50));) then call foo.x;

    this does not seem to work. Any ideas? Thanks for the help.

    • http://twitter.com/codefox421 Nick Iaconis

      Try instead:

      var foo = new Shape(40,40,50,50);
      s.addShape(foo);
      alert(foo.x);

      …alternatively, the code you posted should work if you rewrite addShape as such:

      CanvasState.prototype.addShape = function(shape) {
          // current addShape code here
          return shape;
      }

      …but that’s most likely not the best implementation strategy.

  • Jhoanna Marie Robles

    Hello, how do you make this work on smartphones/iphones?

  • jerry mouce

    Where is the souce code ..???

  • http://twitter.com/JoeLannen Joe Lannen

    I wonder how you’d be able to select and drag an edge of a shape to change the size of of it. That would be sweeeeet.

  • http://twitter.com/officefruit_ office fruit

    hey! i’ve done something like this, and i want make zoomscroll, and i did it! but it works wrong:(
    how i can make limits of zoomscroll?
    and i want fix trouble with zoom+scroll together!

    here is my project:
    http://drag.com.hostinghood.com/index.php

    sry for my english;)

  • Sb

    Very useful for me! Thanks!

  • Younex Bekflih

    Thanks for the tutorial, but how to implemet resizing feature ??

  • Andrew

    The second part of this demo is really great too, it describes how to make these shapes resizable:
    http://simonsarris.com/blog/225-canvas-selecting-resizing-shape

    The code for this tutorial got a major overhaul, I went ahead and merged the resizing methods into this code base if anyone is interested:
    https://github.com/amclark/shapesdemo

  • Kevin H

    Thanks very much Simon for the great tutorial!

    If someone is a beginner like me and is having trouble getting the code running make sure your html body tag looks like this:

    ” (remove the spaces)

    and change ‘canvas1′ in the initialize function to ‘canvas.’ (This is a mistake.)

    These two things had me scratching me head for a while.

  • Mark Anthony Muya

    you’ve showed me of what I’m looking for.
    thank you!

  • DC

    Hi,
    thanks for the big help :) Can you tell me how to do this for a round canvas? So that only the part within is shown and not outside…

  • Clack

    How can i rotate each item?

  • sh

    Thanks a lot for the tutorial
    I have a theoretical question, how i can move 2 boxes together? i have an angle in canvas and i want angle moves when click on any line of angle and dragging.

  • jyothy maria

    I read the article,can I get the full code. Also I have an application to drag images from a div to a canvas. I implemented this by drawimage(images,x,y). I am unable to move that image inside canvas. Plase help me. Here is my code

    $(document).ready(function () {

    // get references to the canvas and its context

    var canvas = document.getElementById(“canvas”);

    var ctx = canvas.getContext(“2d”);

    // get the offset position of the kinetic container

    var $canvas = $(“#canvas”);

    var Offset = $canvas.offset();

    var offsetX = Offset.left;

    var offsetY = Offset.top;

    // select all .tool’s

    var $tools = $(“.tool”);

    // make all .tool’s draggable

    $tools.draggable({helper: ‘clone’});

    // assign each .tool its index in $tools

    $tools.each(function (index, element) {

    $(this).data(“toolsIndex”, index);

    });

    // make the canvas a dropzone

    $canvas.droppable({

    drop: dragDrop

    });

    // handle a drop into the canvas

    function dragDrop(e, ui) {

    // get the drop point (be sure to adjust for border)

    var x = parseInt(ui.offset.left – offsetX) – 1;

    var y = parseInt(ui.offset.top – offsetY);

    // get the drop payload (here the payload is the $tools index)

    var theIndex = ui.draggable.data(“toolsIndex”);

    // drawImage at the drop point using the dropped image

    ctx.drawImage($tools[theIndex], x, y, 32, 32);

    }

    });

    Drag from the blue toolbar to the red canvas

  • Maurice Washington

    Can anyone help me understand how to create static images on the canvas along with the draggable images.

    This article has helped a-lot for starting my assignment.

    My goal is to create three goals (unmovable squares that will only react when the draggable item is dropped in its location. I tried creating separate functions that create images (i.e. square 1, 2, and 3) but they disappear when I move the draggable item. I’m assuming that I have to create a ‘canvasstate’ function among others to keep it visible but frankly I’m not sure of how to go about it. Hopefully I typed this in a way you all can understand. Any help would be much appreciated.

    • avron polakow

      Use canvases on top of one another, the base canvas with the unmoveable images and the draggable images on top on a canvas otherwise transparent.

      I have written mapping applications like this with the basemap as the lowest layer and the various polygons and other objects in layers superimposed on the basemap.

      Check an example out at: http://www.avronp.com/nsn/HTML5/default.html
      Click on the light bulb icon and then pass a mouse over the states in the USA

  • hi543216

    Thanks for this great post! I mostly came here for the mouse coordinates part, and this helped alot!

  • frog

    can it be saved as png or jpg?

  • Ryad El-Dajani

    Amazing tutorial, although I needed to figure out some parts for myself, as the demo content seems to be offline.
    Anyway, really good job, thank you so much!