How to Drag and Drop on an HTML5 Canvas

We explored using keyboard input to move a shape on an HTML5 canvas here http://html5.litten.com/moving-shapes-on-the-html5-canvas-with-the-keyboard/. Now we’ll take a look at using input from the mouse. With a few simple calculations, you can drag and drop shapes on the canvas with your mouse.

UPDATE: I’ve added a companion post for IE compatibility.

IE Compatible Canvas Drag and Drop With jQuery and ExCanvas

This example is coded for readability and not for optimized operation. All you need is a text editor like notepad and an HTML5 friendly browser (I’m using Firefox 3.6).

Here is the code listing that we will be discussing.


<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Canvas Drag and Drop Test</title>
</head>
<body>
<section>

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

<script type="text/javascript">

var canvas;
var ctx;
var x = 75;
var y = 50;
var WIDTH = 400;
var HEIGHT = 300;
var dragok = false;

function rect(x,y,w,h) {
 ctx.beginPath();
 ctx.rect(x,y,w,h);
 ctx.closePath();
 ctx.fill();
}

function clear() {
 ctx.clearRect(0, 0, WIDTH, HEIGHT);
}

function init() {
 canvas = document.getElementById("canvas");
 ctx = canvas.getContext("2d");
 return setInterval(draw, 10);
}

function draw() {
 clear();
 ctx.fillStyle = "#FAF7F8";
 rect(0,0,WIDTH,HEIGHT);
 ctx.fillStyle = "#444444";
 rect(x - 15, y - 15, 30, 30);
}

function myMove(e){
 if (dragok){
  x = e.pageX - canvas.offsetLeft;
  y = e.pageY - canvas.offsetTop;
 }
}

function myDown(e){
 if (e.pageX < x + 15 + canvas.offsetLeft && e.pageX > x - 15 +
 canvas.offsetLeft && e.pageY < y + 15 + canvas.offsetTop &&
 e.pageY > y -15 + canvas.offsetTop){
  x = e.pageX - canvas.offsetLeft;
  y = e.pageY - canvas.offsetTop;
  dragok = true;
  canvas.onmousemove = myMove;
 }
}

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

init();
canvas.onmousedown = myDown;
canvas.onmouseup = myUp;

</script>

</section>
</body>
</html>


You can copy this code and paste it into a new file called something like draganddrop.html and when you open it with an HTML5 friendly browser like Firefox 3.6 it will display the canvas with a draggable square on it.

The details of how we create a canvas and draw our shape on it can be found in this previous post http://html5.litten.com/simple-animation-in-the-html5-canvas-element/.

In this example we draw a 30×30 pixel square which we can click on and drag around the canvas. Let’s look at the draw() function.


function draw() {
  clear();
  ctx.fillStyle = "#FAF7F8";
  rect(0,0,WIDTH,HEIGHT);
  ctx.fillStyle = "#444444";
  rect(x - 15, y - 15, 30, 30);
}

We create our draggable square with this call to rect()


  rect(x-15, y-15, 30, 30);

rect(x, y, width, height)
The x and y parameters define the coordinate of the top left corner of the new rectangular path. width and height define the width and the height of the rectangle.

We want our x and y to be at the center of our square so we use

  rect(x - 15, y - 15, 30, 30); 

instead of

  rect(x, y, 30, 30);

Now we need to add event listeners to detect when the mouse button is pressed down so we can begin dragging and when the mouse button is released so we can drop.

After calling init(), in which we create our canvas object, we attach event listeners to the new canvas object with


init();
canvas.onmousedown = myDown;
canvas.onmouseup = myUp;

When the user clicks their mouse on the canvas the canvas.onmousedown event is triggered and calls the function myDown(). myDown receives an event object e that has properties about the event. This event has, amongst others, the properties pageX and pageY which hold the values of the mouse’s current x and y coordinates on the page (not the window but the whole page).


function myDown(e){
if (e.pageX < x + 15 + canvas.offsetLeft && e.pageX > x - 15 +
canvas.offsetLeft && e.pageY < y + 15 + canvas.offsetTop &&
e.pageY > y -15 + canvas.offsetTop){

  x = e.pageX ;
  y = e.pageY ;
  dragok = true;
  canvas.onmousemove = myMove;
 }
}

Our first question to ask when the mouse is clicked on the canvas is, “Is this click on our draggable square?”.

We check with this test.


if (e.pageX < x + 15 + canvas.offsetLeft && e.pageX > x - 15 +
canvas.offsetLeft && e.pageY < y + 15 + canvas.offsetTop &&
e.pageY > y -15 + canvas.offsetTop)

Remember that we created our square so that (x,y) is at the center. To determine if the click was on the x axis inside the square, we look at the x value of the mouse click e.pageX and see if it is within 15 pixels of our square’s x value using the canvas objects offset since e.pageX is the value of the mouses x coordinate on the page not on the canvas.

We also test the e.pageY value against our square’s y value. If our point is within the x and y values of our square then we set the variable dragok to true. Setting our square’s central (x,y) to (e.pageX - canvas5.offsetLeft, e.pageY - canvas5.offsetTop) makes it easy for us to move it (this is a very simple example).


  x = e.pageX - canvas5.offsetLeft;
  y = e.pageY - canvas5.offsetTop;
  dragok = true;

Now that we know the mouse button is down while it is over our draggable object, we can start listening for mouse movement (dragging).


  canvas.onmousemove = myMove;

Here’s our myMove() function


function myMove(e){
 if (dragok){
  x = e.pageX - canvas.offsetLeft;
  y = e.pageY - canvas.offsetTop;
 }
}

We check to make sure that we are dragging something dragok = true and if we are, we update the x and y coordinates of our square with e.pageX and e.pageY adjusted with the offset values of our canvas.

Now we need to be able to drop the object by releasing the mouse button. When we do this we trigger our listener for the onmouseup event of our canvas (try moving your mouse off the canvas while dragging and observe the behavior of onmouseup. You will see that if you release the mouse while off of the canvas, it does not trigger the onmouseup event listener on the canvas. This can be useful in designing games.)


canvas.onmouseup = myUp;

The myUp() function simply sets dragok back to false and removes the onmousemove listener (we only need it when we are dragging).


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

Here is our canvas in action…


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

Have fun with the code as that is the easiest way to learn.

Please leave a comment if you have any questions or corrections.


13 Replies to “How to Drag and Drop on an HTML5 Canvas”

  1. I know your goal of this demo, but I don’t think this is a good demo for drag-and-drop of objects on a canvas. Could we still use this method with many objects?

  2. Few nitpicks:

    Neither dx nor dy variable is used.

    And also the square centers itself on the mousepointer when it is being dragged. Objects being dragged usual maintain relative position to the mousepointer when dragged.

    Just wanted to point these things out.
    -Non

  3. Non, you are absolutely correct. Somehow, an old draft of this post has partially replaced what should be here. It may have happened when I did some updates about a month ago. I will look into this and try to fix it as soon as I can.

    I would not know about this problem without your comment. Thank you very much.

  4. I know the goal of the code readable not optimized but the using setInterval() to do the animation is OTT – no? Deleting that line and adding a call to draw() in myMove() and myUp() is both healthier and intuitive.

  5. I have an issue :'(

    My canvas is inside a table, and when I use your script and your example in it, it drags the rectangle without cleaning the trace.

    Do you know which could be the error?

    Thank you!!!

  6. What if I dont have any boundaries for the canvas and I want to move the object off the screen, i.e., when we move the object off the screen, a scroll bar should show up in what ever direction we are moving the object. I tried out in LTR, the default mode, the width automatically extends and the scroll bar keeps moving towards the right. But when we change the direction to RTL, now, we move the object towards the left side of the screen, the width doesn’t increase in the left side. How can we solve this problem?

  7. Thanks for this! I’ve been searching for a simple example like this, and now I finally found it. Works like a charm 😉

Leave a Reply to Pedro Cancel reply

Your email address will not be published. Required fields are marked *