Welcome to the first of what I hope will be many tutorials at RectangleWorld! For the inaugural tutorial we will look at a very simple example for beginners: drawing objects to the canvas which can be dragged around with the mouse. We will extend this basic example in subsequent tutorials. Click here or on the screenshot below to see the demo.
Download
Download the complete, well-commented source code here: SimpleDragging.zip
About the Code
I won’t attempt to explain every line of the HTML and JavaScript code used in this example; instead you may find it instructive to simply download the commented source and study it carefully. The basics of creating a canvas application can be found in several books; the basic setup I am using here is covered very carefully in HTML5 Canvas by Fulton & Fulton. If you are unfamiliar with JavaScript you may also find it necessary to do a little reading on the topic; for example see JavaScript: The Definitive Guide (and by the way, no, I’m not affiliated with O’Reilly publishing!). A good on-line introduction to the HTML5 Canvas can be found at the Mozilla Developer Network site here.
Mouse Events
This example features mouse interactivity. The code has to set up listeners for mouse events. For example, to listen for a mouse down event we include the code
theCanvas.addEventListener("mousedown", mouseDownListener, false);
Here, theCanvas
was defined earlier in the code and references a canvas which has been placed in the html page. The line of code above establishes an action to take if any mousedown events occur over the canvas: the function mouseDownListener
will be called (the Boolean parameter false
means that the mousedown event will not be captured).
Note that once a mousedown event occurs, there are lines of code added in the listener function to take the appropriate action when the mouse moves or if the mouse button is released. For example:
window.addEventListener("mouseup", mouseUpListener, false);
Note that the mousemove and mouseup event listeners are attached to the whole document window, because if the user drags the mouse outside of the canvas area while holding the mouse button down, we still want mouse actions to have an effect.
The Display Objects
If you’re coming from a Flash and ActionScript background, you’re accustomed to working with a display list with separate display objects, each of which can have its own mouse event listeners. But in the HTML5 Canvas, there is one display object which essentially works like a big bitmapdata object, and all display objects have to be drawn to this display. Mouse events can only be detected as occuring over the whole display – not on individual objects. So we will have to listen for mouse down events occuring over the whole canvas, then check if the mouse location was over any of the positions where we have drawn an object.
The display objects in this example will all be filled circles, and will in fact be represented by abstract JavaScript Objects, each of which simply records the position, radius, and color of the shape. We define one of these shapes with the code:
tempShape = {x:tempX, y:tempY, rad:tempRad, color:tempColor, layer:i};
where the variables tempX
, tempY
, etc., were defined in previous lines by randomization. These Objects are stored in the array called shapes
. When it comes time to draw these objects to the canvas, we use the code
context.fillStyle = shapes[i].color;
context.beginPath();
context.arc(shapes[i].x, shapes[i].y, shapes[i].rad, 0, 2*Math.PI, false);
context.closePath(); context.fill();
Since the objects in this example will be filled in circles, it will be easy to check if the mouse coordinates are over one of our objects by a simple test to see whether the distance from the center is less than the radius of the circle. We will check each circle to see if the mouse coordinates are over that object, and by paying attention to the layering order of the objects we will only move the topmost object.
Checking each object for “hit test” with the mouse coordinate position could be very inefficient in a situation where we would have a very long list of objects. In such a situation, we would have to make use of a data structure like a quadtree to pre-sort the objects as they are placed on the canvas, in order to avoid checking all objects for a hit. But with a small number of objects, checking all objects for a hit will work fine.
You will notice that in this example when dragging the objects, the layering order is preserved. This is because the objects are always drawn in the same order based on their array order. We might wish to have the currently dragged object appear in front of all the other objects so that we can see it while dragging. We will add some code in our next tutorial to create this effect.
Acknowledgments:
- Checking to see whether the browser supports the canvas tag is done with the Modernzr JavaScript library, available at http://www.modernizr.com
- Code for the basic setup used here is copied from HTML5 Canvas by Fulton & Fulton. This book is an excellent resource for anyone looking to get started in HTML5 Canvas development.
how to get the object coordinate to trigger an action ?
December 23, 2013 @ 5:47 am
|Hi Dydrax,
I’m not sure what you mean exactly, but have a look at the code within the
mouseDownListener
function. Here is the relevant code which determines mouse position over the canvas, and which accounts for possible positioning and scaling of the canvas that may have occured in the browser:December 30, 2013 @ 6:02 pm
|Can I save the re arranged circles using a save button? Please reply.
December 20, 2014 @ 8:11 am
|I’m not sure what you mean…do you want to save the state of the circles so that they are in the same place when the page is visited again? This could be done with cookies but I don’t really know how to do it. If you want to just save the image to your computer, you need to use the toDataURL JavaScript method to convert the canvas to an image, and then write some code to put that image in a page so it can be saved. Or you could save the image on the host server and provide a download link. I’m afraid I don’t have time to explain the details, but maybe you could do a web search for some solutions!
December 20, 2014 @ 3:29 pm
|Is it possible with other shapes such rectangular,square,triangle,etc.?
January 6, 2015 @ 8:56 am
|Hi Vijay,
I’m sorry I overlooked this comment from a while ago!
Yes, you can certainly program it to drag different kinds of shapes. You just need to figure out a way to determine if a mouse press happens inside a shape. The more complicated the shape, the harder this can be, although there are well-known algorithms for determining this. Here is a simple example with squares and circles: http://rectangleworld.com/blog/archives/129
March 18, 2015 @ 5:08 am
|Hey dan,
Is it possible that, i am dragging some text and dropping on canvas. When text get dropped on canvas then it get convert to bouncing ball like in above ex.?
Means we are dragging balls but behind that some text and that was dropped from some list or some div
????
Please do suggest?
January 25, 2017 @ 2:00 pm
|Hi Bharat,
Sorry for the delay in my reponse. I’m afraid I don’t understand your question. But it seems you want to draw text in the canvas, which is certainly possible. Have a look at this reference which may get you started: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text
January 30, 2017 @ 4:51 am
|I noticed that the background color can be changed here:
function drawScreen() {
context.fillStyle = “#FFFFFF”;
context.fillRect(0,0,theCanvas.width,theCanvas.height);
drawShapes();
}
But does anybody know how the background could be changed to a .png picture?
Thank you for your help.
Jim Adrian
jim@futurebeacon.com
February 22, 2018 @ 9:10 pm
|To follow up on my question just above, I find that changing the color number (#FFFFFF or any other color) to transparent allows the circles to change within the canvas whenever you refresh the browser, but then the circles leave their color in their path on the canvas as it is dragged.
Thank you for your help.
Jim Adrian
jim@futurebeacon.com
February 25, 2018 @ 1:12 pm
|Can the code be reorganized so that it contains two layers, one definitely place on the screen after the completion of the other? The second layer, consisting of the circles, would need to place the circles without otherwise changing the first layer. Is this possible?
I think that the background in the current code seems to be tied to the colors of the circles in some way. When the color of the background is not specified, it comes out to be a random color, like the circles do.
Thank you for your help.
Jim Adrian
jim@futurebeacon.com
February 25, 2018 @ 9:48 pm
|Hi Jim,
Sorry I never responded. I am not actively updating this blog lately!
You can certainly use a transparent background in the canvas, and to clear it out (instead of drawing the background color over it), use the function clearRect(). You can put your image behind this clear canvas using proper CSS to position the image and canvas in the same spot. Use the z-index attribute to make sure the canvas is above the image.
Sorry I don’t have time to give more explicit instructions!
March 19, 2018 @ 10:54 pm
|Hey Dan, Can I have whole code in one file?
March 20, 2018 @ 6:17 pm
|Hi CK, I’m afraid I don’t have time to put together a file for you, but here’s what you can do: Download the source code here, and delete the Modernizr stuff because it is really no longer needed. Delete lines 44-47 where the canvasSupport() function is defined, and also lines 49-51 where the function is used to check for canvas support. Then everything should work fine without the Modernizr code.
March 22, 2018 @ 3:13 pm
|