Is it possible to have an array of coordinates and drag and drop an image only along those coordinates? I would like to use only javascript and not use a javascript library. I keep scratching my head and googling this forever and can't find how to do this or if it is possible.
2 Answers
Demo: http://jsfiddle.net/m1erickson/7vmML/
Example code:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();
var isDown=false;
var startX;
var startY;
var points=[];
points.push({x:10,y:10});
points.push({x:75,y:100});
points.push({x:150,y:125});
points.push({x:125,y:200});
var imageX=-200;
var imageY=-200;
var img=new Image();
img.onload=start;
img.crossOrigin="anonymous";
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house32x32transparent.png";
function start(){
drawAll();
}
function drawAll(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.moveTo(points[0].x+4,points[0].y+4)
for(var i=1;i<points.length;i++){
var pt=points[i];
ctx.lineTo(pt.x+4,pt.y+4);
}
ctx.stroke();
//
for(var i=0;i<points.length;i++){
var pt=points[i];
ctx.fillRect(pt.x,pt.y,8,8);
}
//
ctx.drawImage(img,imageX,imageY);
}
function handleMouseDown(e){
e.preventDefault();
isDown=true;
}
function handleMouseUp(e){
e.preventDefault();
isDown=false;
}
function handleMouseOut(e){
e.preventDefault();
isDown=false;
}
function handleMouseMove(e){
if(!isDown){return;}
e.preventDefault();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
var minDistance=1000;
var minPoint=-1;
for(var i=0;i<points.length;i++){
var pt=points[i];
var dx=mouseX-pt.x;
var dy=mouseY-pt.y;
var distance=Math.sqrt(dx*dx+dy*dy);
if(distance<minDistance){
minDistance=distance;
imageX=pt.x-img.width/2;
imageY=pt.y-img.height/2;
}
}
drawAll();
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag mouse. Image will snap to nearest point.</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Note that this example code uses jQuery to listen for mouse events. If you prefer pure javascript you can use these event bindings instead:
canvas.onmousedown=handleMouseDown;
canvas.onmouseup=handleMouseUp;
canvas.onmouseout=handleMouseOut;
canvas.onmousemove=handleMouseMove;
And you can calculate mouse position like this:
function getMousePos(canvas,e) {
var rect=canvas.getBoundingClientRect();
return{ x:e.clientX-rect.left, y:e.clientY-rect.top };
}

- 102,905
- 11
- 164
- 176
-
I tried to make a fiddle to show you what i'm about to ask and what I have done but for some reason what works perfectly fine online doesn't work at all on the fiddle and I don't know why. But my question is what if I want two images moving at separate times. What would I do for that? For instance, you click one image to move it and the image moves and the second image stays in place until it is clicked. I could potentially have many images on the line, i've done a lot of work to mine but completely stuck on how to do that :( – gikygik Sep 18 '14 at 18:34
-
Well lets see...You could 'select' an image by clicking in the bounding box of the image. Then you can move just that selected image. To do that: A mousedown inside an image is a **selection** and would not trigger `isDown=true`. A mousedown outside an image is a **movement** and would trigger `isDown=true`. Cheers! – markE Sep 18 '14 at 18:56
-
And this is my last question I promise! Right now I have a bunch of pictures next to the canvas (buttons) when the user clicks the button it adds the image to the line on the canvas. How can I make it so that if the user clicks a second image it also adds a second image rather than just writing over the previous image? – gikygik Sep 18 '14 at 19:17
-
I assume the second image is over-drawing the first image--rather than both buttons accidentally drawing the same image. So...when you click an image button, send it to a 'neutral' part of the canvas. For example, to keep images separate you can keep a variable `nextX` that determines the X-position where any new image will be initially placed:`var nextX=0` and `drawImage(image,nextX,0)`. When you've added an image you increase nextX by the width of the added image: `nextX+=image.width`. Good luck with your project! – markE Sep 18 '14 at 19:27
-
First off I have to say MarkE you are a huge reason I have been able to learn canvas on my own. I was able to take this and other things that you have shown other people to actually learn it. Is there any reason that click and drag with canvas wouldn't work in chrome correctly but works fine in every other browser? – gikygik Sep 26 '14 at 05:52
-
You're welcome...Always glad to help anyone who enjoys learning! My demo above is working fine for me in Chrome Version 37.0.2062.124 m. Do you have specific code that's misbehaving? – markE Sep 26 '14 at 17:11
-
Would nice to see how you would implement interpolation between points, instead of "snapping" :) – yckart Feb 01 '16 at 10:11
-
@yckart. The question requests "snapping" to specified coordinates. Check out k3n's answer below if you want to travel along the path instead of snapping to waypoints. :-) – markE Feb 01 '16 at 17:17
-
@markE It's just that k3n's solution depends on snapping too (he's just *smoothing* the points) ;) – yckart Feb 01 '16 at 19:38
-
@yckart. Check out [this previous post](http://stackoverflow.com/questions/17083580/i-want-to-do-animation-of-an-object-along-a-particular-path/17096947#17096947). If you need different, just ask a new SO question and you'll likely get what you need. :-) – markE Feb 01 '16 at 21:33
Yes, calculate the shortest distance to each of the points and override the mouse position with the point that is closest.
Lets use a simple point array as an example:
var points = [10,200, 50,250, 100,100, 150,120, 200,240,
250,200, 300,120, 350,180, 400,150];
Now, when you move you item over you get the closest point this way:
Lets first get the mouse x and y position (for demo - in you code you would use the position of the item):
var rect = canvas.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top,
Now you can iterate the point array and get the point with shortest distance to mouse x and y:
var i = 0,
pIndex = -1,
minDist = 999999999,
dist;
/// get shortest distance
for(; i < points.length; i+=2) {
/// get distance from current point to mouse x,y
dist = getDistance(x, y, points[i], points[i+1]);
/// if less than previous distance, update
if (dist < minDist) {
minDist = dist; /// current low
pIndex = i; /// point index
}
}
pointX = points[pIndex];
pointY = points[pIndex+1];
The function for calculating distance is simple trigonometry calculating the hypotenuse:
function getDistance(x1, y1, x2, y2) {
var dx = x2 - x1,
dy = y2 - y1;
return Math.abs(Math.sqrt(dx * dx + dy * dy));
}
Now you can draw your item at (pointX, pointY)
.
Live demo
The example here uses a very rough point array. If you need finer resolution you can use a line smoother such as this one or interpolate the point on the line. Here is a version where line smoothing is applied to increase its resolution and smoothness (you can drop the smoothness by setting tension to 0, and you can adjust resolution by changing number of segments, see following demo):
Here is a demo where line smoothing is used
Also have in mind that you may need to optimize the check range by for example doing a rough iteration first then use the 3-4 closest points and do a finer iteration between the range those represents.
Update
To allow snapping only when is held down and moved add these lines to code:
var allowMove = false;
canvas.onmousedown = function() {
allowMove = true;
}
canvas.onmouseup = function() {
allowMove = false;
}
canvas.onmousemove = function(e) {
if (!allowMove) return;
... as before ...

- 1
- 1
-
-
do you know on the line smoother one how to only allow it to move onmousedown...i tried something like this and it did not work at all... – gikygik Feb 04 '14 at 22:40
-
@ShawnaMacNabb ok, to move it *while* mouse is down just add a flag to the mix set when mousedown/up and read in mousemove: http://jsfiddle.net/AbdiasSoftware/XfGwS/3/ – Feb 04 '14 at 22:52
-
Thank you so much..this is exactly what i need but i'm having troubles implementing it into my project...i'm a noob when it comes to web stuff i'm just trying to get better. here is my fiddle http://jsfiddle.net/smacnabb/7gZ2a/ – gikygik Feb 05 '14 at 22:47
-
i just updated a little not that i did much http://jsfiddle.net/smacnabb/7gZ2a/1/ – gikygik Feb 05 '14 at 22:48
-
@ShawnaMacNabb you're mixing context and ctx; just stick to one of them in all the code. And, you didn't mention circle as your path in the Q - that would have changed things a bit as circles can be handled differently and doesn't need to involve a point array. Just calculate the angle from your mouse to to the center of the circle, then plot the image at radius at the same angle. – Feb 05 '14 at 23:17
-
@ShawnaMacNabb ok, there are a few issues, one was that you stored the points as objects (which is fine) and the example above store them as plain array. This is why you should always include code in the question. Here's a intermediate fix: http://jsfiddle.net/AbdiasSoftware/7gZ2a/2/ You will of course need to update the re-drawings etc. yourselves. Hope this helps! – Feb 05 '14 at 23:28
-
just what i needed thank you! i'm gonna work on getting image to move instead! – gikygik Feb 06 '14 at 02:32
-
This doesn't seem to work on phones, any idea why? Sorry if this is a stupid question... I am very new to canvas. – smj2393 Mar 06 '15 at 16:25
-
@smj2393 could be the browser, it may require prefix, it could also be that it doesn't support the features you want to use. Just check with the browser's vendor as well as with http://caniuse.com – Mar 06 '15 at 22:37
-
Great work! However, as mentioned in the comments above: Would nice to see how you would implement interpolation between points, instead of "snapping" :) – yckart Feb 01 '16 at 19:37