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.
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.
Share Improve this question asked Feb 4, 2014 at 21:30 gikygikgikygik 4219 silver badges27 bronze badges2 Answers
Reset to default 3Demo: http://jsfiddle/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./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./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 };
}
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 ...