Unknown Kadath

How to Drag and Drop on an HTML5 Canvas

Posted on July 7th, 2010 by James Litten

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.


Tags: , ,

This entry was posted on Wednesday, July 7th, 2010 at 7:34 pm and is filed under Canvas Element, HTML5. You can follow any responses to this entry through the RSS 2.0 feed. You can skip to the end and leave a response. Pinging is currently not allowed.

7 Responses to “How to Drag and Drop on an HTML5 Canvas”

How to Drag and Drop on an HTML5 Canvas « HTML5 TalkJuly 7th, 2010 at 7:50 pm

[...] mouse. With a few simple calculations, you can drag and drop shapes on the canvas with your mouse. Full post here … Tags canvas element, HTML5, javascript Categories [...]

» IE Compatible Canvas Drag and Drop With jQuery and ExCanvasJuly 20th, 2010 at 6:25 pm

[...] This post is about making drag and drop work on a canvas in Internet Explorer versions 6, 7 and 8 . The details of how to drag and drop on the canvas can be found in this previous post. How to Drag and Drop on an HTML5 Canvas [...]

HAsAugust 15th, 2010 at 6:44 am

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?

NonMarch 19th, 2011 at 10:40 pm

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

adminMarch 19th, 2011 at 11:18 pm

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.

QuitNovember 21st, 2013 at 9:21 am

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.

Leave a Response