Making and Moving Selectable Shapes on an HTML5 Canvas: A Simple Example

I’m writing a book on HTML5, including Canvas! Click here for more information.

Wait! I’ve given this tutorial a major overhaul and you should really read the new one here instead.

This is part one in a series. Part 2 can be found 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 boxes. Double click to add new boxes.


This article’s code is written primarily to be easy to understand. It isn’t optimized for performance, though a little bit of the drawing is set up so that more complex shapes can easily be added in the future.

We’ll be going over a few things that are also essential to game-development (drawing loop, hit testing), and in later tutorials I will probably turn this example into a small game.

The HTML5 Canvas

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

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

A canvas isn’t smart: it’s just a place for drawing pixels. If you ask it to draw something it will do so and then immediately forget everything about what you have just done. Because of this we have to keep track ourselves of all the things we want to draw (and re-draw) each frame.

So we’ll need to add:

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

Keeping track of what we draw

To keep things simple for this example we will just make a rectangular object called Box. We’ll also make a method for creating Boxes a little easier.

// holds all our rectangles
var boxes = []; 

//Box object to hold data for all drawn rects
function Box() {
  this.x = 0;
  this.y = 0;
  this.w = 1; // default width and height?
  this.h = 1;
  this.fill = '#444444';
}

//Initialize a new Box, add it, and invalidate the canvas
function addRect(x, y, w, h, fill) {
  var rect = new Box;
  rect.x = x;
  rect.y = y;
  rect.w = w
  rect.h = h;
  rect.fill = fill;
  boxes.push(rect);
  invalidate();
}

Initialization

I’m going to add a bunch of variables for keeping track of the drawing and mouse state. I already added boxes[] to keep track of each object, but we’ll also need a var for the canvas, the canvas’ 2d context (where wall drawing is done), whether the mouse is dragging, width/height of the canvas, and so on. We’ll also want to make a second canvas, for selection purposes, but I’ll talk about that later.

var canvas;
var ctx;
var WIDTH;
var HEIGHT;
var INTERVAL = 20;  // how often, in milliseconds, we check to see if a redraw is needed

var isDrag = false;
var mx, my; // mouse coordinates

 // when set to true, the canvas will redraw everything
 // invalidate() just sets this to false right now
 // we want to call invalidate() whenever we make a change
var canvasValid = false;

// The node (if any) being selected.
// If in the future we want to select multiple objects, this will get turned into an array
var mySel; 

// The selection color and width. Right now we have a red selection with a small width
var mySelColor = '#CC0000';
var mySelWidth = 2;

// we use a fake canvas to draw individual shapes for selection testing
var ghostcanvas;
var gctx; // fake canvas context

// since we can drag from anywhere in a node
// instead of just its x/y corner, we need to save
// the offset of the mouse when we start dragging.
var offsetx, offsety;

// Padding and border style widths for mouse offsets
var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;

// initialize our canvas, add a ghost canvas, set draw loop
// then add everything we want to intially exist on the canvas
function init() {
  canvas = document.getElementById('canvas');
  HEIGHT = canvas.height;
  WIDTH = canvas.width;
  ctx = canvas.getContext('2d');
  ghostcanvas = document.createElement('canvas');
  ghostcanvas.height = HEIGHT;
  ghostcanvas.width = WIDTH;
  gctx = ghostcanvas.getContext('2d');
  
  //fixes a problem where double clicking causes text to get selected on the canvas
  canvas.onselectstart = function () { return false; }
  
  // fixes mouse co-ordinate problems when there's a border or padding
  // see getMouse for more detail
  if (document.defaultView && document.defaultView.getComputedStyle) {
    stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingLeft'], 10)      || 0;
    stylePaddingTop  = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingTop'], 10)       || 0;
    styleBorderLeft  = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderLeftWidth'], 10)  || 0;
    styleBorderTop   = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderTopWidth'], 10)   || 0;
  }

  // make draw() fire every INTERVAL milliseconds.
  setInterval(draw, INTERVAL);
  
  // add our events. Up and down are for dragging,
  // double click is for making new boxes
  canvas.onmousedown = myDown;
  canvas.onmouseup = myUp;
  canvas.ondblclick = myDblClick;
  
  // add custom initialization here:
  
  // add an orange rectangle
  addRect(200, 200, 40, 40, '#FFC02B');
  
  // add a smaller blue rectangle
  addRect(25, 90, 25, 25, '#2BB8FF');
}

Drawing

Since our canvas is animated (boxes move over time), we have to set up a draw loop as I did in the init() function.

We have to draw at a frame rate, maybe every 20 milliseconds or so. However, redrawing doesn’t just mean drawing the shapes over and over; we also have to clear the canvas before every redraw. If we don’t clear it, dragging will look like the box is making a solid line because none of the old box-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, so we will consider the canvas to be either valid or invalid.

If everything just got drawn, the canvas is valid and there’s no need to draw again. However, if we do something like add a new Box or try to move a box by dragging it, the canvas will get invalidated and draw() will do a clear-redraw-validate.

This isn’t the only way to optimize drawing, after all clearing and redrawing the entire canvas when one little box moves is excessive, but canvas invalidation is the only optimization we’re going to use for now.

// While draw is called as often as the INTERVAL variable demands,
// It only ever does something if the canvas gets invalidated by our code
function draw() {
  if (canvasValid == false) {
    clear(ctx);
    
    // Add stuff you want drawn in the background all the time here
    
    // draw all boxes
    var l = boxes.length;
    for (var i = 0; i < l; i++) {
        drawshape(ctx, boxes[i], boxes[i].fill);
    }
    
    // draw selection
    // right now this is just a stroke along the edge of the selected box
    if (mySel != null) {
      ctx.strokeStyle = mySelColor;
      ctx.lineWidth = mySelWidth;
      ctx.strokeRect(mySel.x,mySel.y,mySel.w,mySel.h);
    }
    
    // Add stuff you want drawn on top all the time here
    
    
    canvasValid = true;
  }
}

As you can see, we go through all of boxes[] and draw each one, in order from first to last. This will give the nice appearance of later boxes looking as if they are on top of earlier boxes. After all the boxes are drawn, a selection handle (if there's a selection) gets drawn around the box that mySel 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 better ways though, like using multiple canvases, but we won't go over that here.

Mouse events

Now we have objects, initialization, and a loop that will constantly re-draw when needed. All thats left is to make the mouse do things upon pressing, releasing, and double clicking.

With our MouseDown event we need to see if there are any objects we could have clicked on. And we don't want just any object; selections make the most sense when we only grab the top-most object.

Now we could do something very easy and just check the bounds of each of our boxes - see if the mouse co-ordinates lie within the boxes width and height range - but that isn't as extendable as I'd like. After all, What if later we want to select lines instead of boxes? Or select triangles? Or select text?

So we're going to do selection in a more general way: We will draw each shape, one at a time, onto a "ghost" canvas, and see if the mouse co-ordinates lie on a drawn pixel or not.

A ghost canvas (or fake canvas, or temporary canvas) is a second canvas that we created in the same size and shape as our normal one. Only nothing from it will ever get seen, because we only created it in code and never added it to the page. Go back and look at ghostcanvas and its context (gctx) in the init() function to see how it was made.

// Happens when the mouse is clicked in the canvas
function myDown(e){
  getMouse(e);
  clear(gctx); // clear the ghost canvas from its last use

  // run through all the boxes
  var l = boxes.length;
  for (var i = l-1; i >= 0; i--) {
    // draw shape onto ghost context
    drawshape(gctx, boxes[i], 'black');
    
    // get image data at the mouse x,y pixel
    var imageData = gctx.getImageData(mx, my, 1, 1);
    var index = (mx + my * imageData.width) * 4;
    
    // if the mouse pixel exists, select and break
    if (imageData.data[3] > 0) {
      mySel = boxes[i];
      offsetx = mx - mySel.x;
      offsety = my - mySel.y;
      mySel.x = mx - offsetx;
      mySel.y = my - offsety;
      isDrag = true;
      canvas.onmousemove = myMove;
      invalidate();
      clear(gctx);
      return;
    }
    
  }
  // havent returned means we have selected nothing
  mySel = null;
  // clear the ghost canvas for next time
  clear(gctx);
  // invalidate because we might need the selection border to disappear
  invalidate();
}

myMove and myUp are pretty self explanatory. the var isDrag becomes true if myDown found something to select, and it becomes false again when the mouse is released (myUp).

// Happens when the mouse is moving inside the canvas
function myMove(e){
  if (isDrag){
    getMouse(e);
    
    mySel.x = mx - offsetx;
    mySel.y = my - offsety;   
    
    // something is changing position so we better invalidate the canvas!
    invalidate();
  }
}

function myUp(){
  isDrag = false;
  canvas.onmousemove = null;
}

There are a few little methods I added that are not shown, such as one to correctly get the mouse position in a canvas. 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 how in the comments.

Part 2 of this tutorial is about resizing the shapes and can be found here.

  • http://twitter.com/urssur Urs Sur

    thanks dude , I was looking for something on canvas.

    Way to go !

  • http://eeyorexd.imperiousdeceit.com Terence Ponce

    Dude, may I know what plugin you’re using for whatever you call that code block is? I’m trying to look for a code snippet plugin. Thanks for posting this article by the way. I’ve been trying to familiarize myself with the HTML5 canvas.

  • http://www.simonsarris.com simonsarris

    I tried a few and decided to go with wp-syntax:

    http://wordpress.org/extend/plugins/wp-syntax/

    So far I really like it.

  • Marcel H.

    Very interesting read, especially the ghost canvas trick. I’d love to see you work this out more. I’ve recently built a flow-diagram configurator using canvases – you draw on your canvas (make a selection) and upon releasing your mouse button, it creates an absolute positioned DIV of the selected dimensions with an equally sized new canvas inside that DIV. The shapes you draw are determined by whatever setting (plain HTML buttons) you’ve preselected. The advantage of this technique is that you can use jQuery UI’s resize, rescale and draggable functionality on said DIVs.

    One disadvantage is that you don’t end up with one canvas where everything happens. So I’m quite interested in seeing how you’ll handle resizing, rotating and scaling.

  • http://www.simonsarris.com simonsarris

    Rotation is super easy from this point onwards. Since my second tutorial wont be up for a few weeks, I’ll write a few quick changes to show you how little needs to be changed.

    I just added a few changes to my original code to showcase rotation. I added a this.rotation field to my Box class.

    In the draw loop I made it so that on each redraw it rotates everything a little. This is silly but it lets you see rotation quickly and easily.

    Also see drawshape() for the real changes. I’m now saving/loading a context on the draw of each shape that has a rotation.

    Here’s the code: http://jsfiddle.net/G4eNp/6/

    Resizing and scaling are not as easy because we have to make some big decisions about how precisely we want them to work. (well, scaling is pretty easy, but how to let the user scale is always up for grabs.) I may do complex shapes before we get to those, but we’ll see.

  • Pingback: SysAdmins Log » A great introduction to HTML 5 canvas

  • massimo

    Great tutorial! Thank you very much Simon!

  • Pingback: Over 150 of the Best HTML5 & CSS3 Resources

  • Janghang

    Thank you for this great tutorial. I have a question about the ghost canvas. You said the ghost canvas is “ghost,” but I think technically it is not. Because you created an actual canvas by document.createElement(‘canvas’), even though it was only used to help selecting objs. But my questions is how this ghost canvas was able to display nothing on the screen. It is supposed to be display something even though it would be during an extremely short moment. Could you please explain why the ghost canvas was able to display nothing? For example, the myDown function draws a shape on the ghost canvas:
    drawshape(gctx, boxes[i], ‘black’, ‘black’);
    but it doesn’t display anything. How? is it because you cleared the ghost canvas right after drawing this?

    Thank you in advance!

  • http://www.simonsarris.com simonsarris

    I did use

    document.createElement(‘canvas’)

    and that creates a new Canvas but it is only created in memory, it is not placed into the page.

    If I did:

    document.body.appendChild(ghostcanvas);

    you would see the ghost canvas on the page.

  • http://eeyorexd.imperiousdeceit.com Terence Ponce

    Thanks a bunch man. Really helped.

  • Anonymous

    Thanks for the example!

    What about resizing and rotating objects on the canvas?

  • Janghang

    Thank you! I think your idea is unique to drag and drop objects. However, I am wonder if just creating a new Canvas doesn’t impact on the canvas app performance, even though it uses some memory. When we create a large sized web app with a number of objects in a scene, this method still works? What do you think about that?

  • http://www.simonsarris.com simonsarris

    Rotation is super easy from this point onwards. Since my second tutorial wont be up for a few weeks, I’ll write a few quick changes to show you how little needs to be changed.

    I just added a few changes to my original code to showcase rotation. I added a this.rotation field to my Box class.

    In the draw loop I made it so that on each redraw it rotates everything a little. This is silly but it lets you see rotation quickly and easily.

    Also see drawshape() for the real changes. I’m now saving/loading a context on the draw of each shape that has a rotation.

    Here’s the code: http://jsfiddle.net/G4eNp/6/

    Resizing and scaling are not as easy because we have to make some big decisions about how precisely we want them to work. (well, scaling is pretty easy, but how to let the user scale is always up for grabs.) I may do complex shapes before we get to those, but we’ll see.

  • http://www.simonsarris.com simonsarris

    If there are 50,000+ objects on the screen, and you pick the object that is on top, picking is very fast, because only one object has to be darwn on the ghost canvas, then it completes.

    But if you click the object on the bottom, it will be much slower, because the ghost canvas has to draw every single object until it finds the last one!

    But for most uses (10,000 objects or less) it works pretty well.

    If this was a real project, there would be many optimizations we would want to do to speed things up. If we know an object is a rectangle, we don’t need a ghost canvas at all, we should just check its x,y,width,height bounds instead. But for things like triangles or paths or text, the ghost works out pretty well.

    Also in large scale applications we will probably have far more than just two canvases. We’ll want a background canvas (an optimized layer of things in the background that stay the same so we never redraw it with the normal canvas), the normal canvas, maybe several foreground and control canvases (for instance if we want to implement scrollbars, each of them will have their own canvas)

    • pv

      Dear simonsarris..wonderful.thanks for posting.you saying “large scale applications we will probably have far more than just two canvases”.How can we do that??.Have u ever tried to implement that.

  • Pingback: Playing with the canvas « clabbers

  • gmtosh

    Great example. Thank you for posting.

    How difficult would it be to make the boxes resizable?

  • http://www.simonsarris.com simonsarris

    It really depends on how you want to do it. Programmatic? Pretty easy. By making selection handles so you can drag at the edges? A little harder, and you gotta make a choice of whether the box is resizing to be bigger or scaling when its done.

    Is there a particular one you’d like to see?

  • gmtosh

    I would really like to see an example with selection handles so that the selected box’s width and height could be adjusted to make the box bigger or smaller without scaling. Thanks!

    • http://www.simonsarris.com simonsarris

      Sure thing, I just finished part two where I made selection handles to resize the boxes. Take a look at it here: http://simonsarris.com/blog/225-canvas-selecting-resizing-shape

      • gmtosh

        Hey Simon. Thank you very much. Heading over right now. I started on expanding your original tutorial to add selection handles, set custom colors with rgba and allow for boxes to be drawn on the canvas. Next part is the actual resizing. I’m now really curious to see how you implemented it. Thanks again!

  • http://twitter.com/tomtubbs tomtubbs

    I’m just wondering – could you have html5 video tag videos on top of each of those boxes (obviously they need to be bigger boxes!) – and could you get the boxes to move of their own accord – or be linked as in a node graph like visualisation – to use the rectangles to draw things on and show relationships?

  • Pingback: Selectable Shapes Part 2: Resizable, Movable Shapes on HTML5 Canvas - simonsarris.com

  • Isidoreony

    This is really awsem Dude!!!!!!!!!!!!!

  • Silvast

    Small typo

    • http://www.simonsarris.com simonsarris

      You’re right!

      It was from an older version where drawShape took both a fill and optional stroke.

      I’ll amend the code to fix that.

  • Pingback: Making and Moving Selectable Shapes on an HTML5 Canvas | Josh Hammock

  • Olivercortinas

    I’m trying to do almost the same but with images instead of rectangles but i couldn’t how do you do that?

  • Pingback: Selectable Shapes Part 2: Resizable, Movable Shapes on HTML5 Canvas | Josh Hammock

  • Cpup22

    I want to do something similar but with Paths. I have a bunch of random shapes on a page and need to get able to know which one was clicked on.

  • SomersetSontrolsGuy

    hi Simon; this was exactly what i was looking for; finally found a great and simple tutorial on canvas! please keep it going!

    more features perhaps?:-

    * add different shapes (inc. paths, texts and images)
    * delete the selected object
    * save and load canvas object array as a single file to disc

  • Eric Rowell

    You can also find some other great tutorials regarding mouse events over the canvas, including path detection, at http://www.html5canvastutorials.com in the advanced section. Cheers!

  • Maqroll1986

    I don´t understand because you have put a 3 instead of the var index. Can you explain to me?

    Thanks you!

    var imageData = gctx.getImageData(mx, my, 1, 1);
    var index = (mx + my * imageData.width) * 4;

    if (imageData.data[3] > 0) {
    mySel = boxes[i];

    • http://www.simonsarris.com simonsarris

      Oops, good catch.

      I don’t need index for this (I should have removed that line).

      The imageData.data here represents only a single pixel, so its just imageData.data[0] to imageData.data[3]. 0,1,2,3 in this array are the red,green,blue,alpha values.

      So all I am doing is looking at the alpha value of the one pixel to see if it is not transparent (greater than zero)

      When you are dealing with imageData that is more than 1 pixel making an ‘index’ is useful since each pixel is 4 numbers in the array. But since I just have one pixel here that index is left-over from a much older version.

      Thanks for noticing!

  • CM12

    Hey. Thanks a lot for the tutorial! It helped me a lot! But for my project, I wish to make it so that if one box is selected, all boxes move with it. Can you please direct me on how to do this?

    Thanks!

  • Flora

    Great tutorial! Almost exactly what I was looking for, thanks so much for writing it!

  • Pingback: Fun at the London Web Standards Meetup | A learning curve

  • Pingback: HTML5 Canvas: scrolling background and selecting/dragging shapes « software excogitation

  • http://sozonov-alexey.ru Sozonov Alexey

    cool tutorial!

  • Dave

    Thanks for posting, been looking for a tutorial just like this for a while – very well written and very useful :)

  • Jj

    thank you for writing this article. I have now started to understand the thing with canvas…… thanks you again.

  • Rajusanal

    what is the need for ghost canvas??

    • http://www.simonsarris.com simonsarris

      Its not needed for this tutorial, but if in the future you wanted to do something complex like hit-test a semi-transparent image, you would need to use a ghost canvas.

      • Rajusanal

        thanks

      • Steve Hannity

        Can’t you just do the hit-testing on the same canvas ?

        • Gijsbert Peijs

          This would

  • AbdulSattar Mohammed

    Couldn’t you have used canvas.isPointInPath for hit testing?

  • http://www.facebook.com/profile.php?id=100000820292329 Juraj Kundrik

    Thank you for tutorial, works great. I have my own modification on http://www.recid.sk/_sub/erd/html5/demo1.html for ERD drawing. Have problem with references between “tables”  (green) lines. this lines are still on canvas. What I’m doing wrong?

    • http://www.facebook.com/profile.php?id=100000820292329 Juraj Kundrik

       I found problem, it calls beginPath();

  • Pingback: JS exercise, day 5 – Drag Square | Software Studies for Media Designers

  • Krisohary

    If I have a complex background, say grid, which I need to re-draw for every 20millisecs, I see the mouse movement after a very big delay.  What do you say?

    • http://www.simonsarris.com simonsarris

      You could do a few things. You could set the grid as the css “background-image” of the Canvas.

      Or you could put two canvases on top of each other and have the background in the back one. Then only redraw it when its absolutely needed instead o f every 20ms

  • Trelanera77

    great job! Thank you

  • kkonweb

    Hi Simon,
    Thanks a lot for the tutorial! How easy would it to be to incorporate line selection using your concept of using ghost canvas and getImageData? I have a canvas with multiple lines drawn using drawLine(). I am looking for ways to perform line selection. 

  • Guest

    How would I add a background image in? I have tried placing code in the spots where you have commented that backgrounds should go, but it always sits on top of the squares.

    var ctx = document.getElementById(‘canvas’).getContext(‘2d’);
                    var img = new Image();
                    img.onload = function () {
                        ctx.drawImage(img, 0, 0);
                    };
                    img.src = ‘titleimage.jpg';

    • Guest

      Note: That is the code I am putting after
      // Add stuff you want drawn in the background all the time here

      that you have in the code

    • Galatiapartheniou

      try making a css file.. and write this in it:
      canvas {background-image: “picture.jpg”}
      and call the css file from the part as follows:

  • http://twitter.com/imakgaems david pucko

    Awesome tutorial, helps a ton!

  • Balaji

    Nice Tutorial, it help me lot, i’m new to html 5, 
    Please say how can i join one box with another box like atom bonding,

  • zeljko Banovic

    This is sad at the very least. So this is the future of the web. Over 200 lines of code to move 2 rectangles …  Good for the user, killer for the programmer. I see where this is going. you can do this whit 10 lines of code in Flash

    • http://www.facebook.com/umeju Roberto Orlando

      probably if someone wants to move only 2 rectangles needs less then 10 lines of code with jquery. i’m quite sure that many jquery plugins will help to solve problems like this very fast

      • Murilo Mielke

        “needs less then 10 lines of code with jquery”. Protip: You may need less than 10 lines of code, but the jquery itself may need 300 lines to do it. If someone get the code above and make a library out of it, you’ll be able to keep “your” code at less than 10 lines. Not a big deal.

    • http://profiles.google.com/macmee David Miller

      Never develop a desktop application, once it reaches 300 lines of code to do something similar, you just might have a heart attack.

  • Marcelo Duende

    That’s pretty cool! I have a question for ya, What if I wanted to rotate the square, only one of those squares? Can I assign the square do a variable and rotate it without update all the values?

  • Htun Lin Aung

    How can i delete the green.

  • PITrelli

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

  • mahendra

    really good work ,
    i have image over canvas and i am drawing angle over image,i can draw multiple angles over image but unable to redraw angle which is already there,means how can i redraw same angle or how to edit angle
    can you help me ragarding this

  • mahendra

    What is drawshape in mydown function ,i want to implement it but i got error that is Error: ‘drawshape’ is undefined,can you help me…

  • mahendra

    I have used same code ,but while i select rectangle it don’t get select ,only double clicking is working in this code,while mydown,mymove and myup not working ,can you help me regarding this…

  • mahendra

    finally it works for me its great one ,but how what about other annotation like line,ruler and angle,means how can we use same code for this…,please help me regarding this

  • jay

    how i can set value of coordinates in textfield when mouse event of drawing a line has drawn the line on canvas

  • Tom

    Hi,

    One issue I am having is the selection of the box. I have 5 boxes on my canvas and within..

    if (imageData.data[3] > 0) {
    mySel = items[i];
    console.log(mySel)
    }

    This logs all of the boxes that come before the selected one as well as the selected box. Do you know why this might be?
    Thanks,
    T

  • Franklin Howitzer

    What about drawing the shape, then running:

    if (context.isPointInPath(context, x, y)) {
    *** STUFF HERE ***
    }

    You’d need a method to translate x and y to the mouse position relative to the canvas, but that’s not hard. Is your method easier? Better?

  • Anirudh Sriram

    I am done with both tutorials…great code ! However, I wanted to know if there was any way I could resize a rectangle to a specified dimension? Thanks !

  • nothingisnecessary

    Thanks! I had not yet played with Canvas but was able to easily modify your example to use the keyboard to move shapes around using the keyboard. Here is link if anybody is interested: http://jsfiddle.net/z4ckpcLc/