问题
So if you draw a line on an HTML5 canvas, what is the best way to make it draggable onMouseDrag? I know you can do this very easily in SVG, but since the canvas is not as easy to work with, I would like to know a good solution to this.
回答1:
Here's one way create draggable lines on html5 canvas.
A Note about html5 canvas: You can't really move anything that has already been drawn on html5 canvas. Instead you simulate movement by clearing the canvas and redrawing everything in it's new position.
This is how to let the user "drag" a line on the canvas:
Create an array of objects that define some lines:
{x0:,y0:,x1:,y1:}
.On
mousedown
, calculate which line is closest to the mouse position.Set a flag indicating that a line is being dragged.
On
mousemove
, change the dragged line's X,Y by the distance the mouse has moved since the lastmousemove
. Then clear the canvas and redraw all lines in their current position.On
mouseup
ormouseout
, clear the dragging flag.
Here's annotated code and a Demo:
// canvas vars
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
// dragging vars
var isDown=false;
var startX,startY;
// line vars
var nearest;
var lines=[];
lines.push({x0:75, y0:25, x1:125,y1:25});
lines.push({x0:75, y0:100, x1:125, y1:100});
lines.push({x0:50, y0:35, x1:50,y1:85});
lines.push({x0:150,y0:35, x1:150,y1:85});
draw();
// listen for mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUpOut(e);});
$("#canvas").mouseout(function(e){handleMouseUpOut(e);});
// functions
//////////////////////////
// select the nearest line to the mouse
function closestLine(mx,my){
var dist=100000000;
var index,pt;
for(var i=0;i<lines.length;i++){
//
var xy=closestXY(lines[i],mx,my);
//
var dx=mx-xy.x;
var dy=my-xy.y;
var thisDist=dx*dx+dy*dy;
if(thisDist<dist){
dist=thisDist;
pt=xy;
index=i;
}
}
var line=lines[index];
return({ pt:pt, line:line, originalLine:{x0:line.x0,y0:line.y0,x1:line.x1,y1:line.y1} });
}
// linear interpolation -- needed in setClosestLine()
function lerp(a,b,x){return(a+x*(b-a));}
// find closest XY on line to mouse XY
function closestXY(line,mx,my){
var x0=line.x0;
var y0=line.y0;
var x1=line.x1;
var y1=line.y1;
var dx=x1-x0;
var dy=y1-y0;
var t=((mx-x0)*dx+(my-y0)*dy)/(dx*dx+dy*dy);
t=Math.max(0,Math.min(1,t));
var x=lerp(x0,x1,t);
var y=lerp(y0,y1,t);
return({x:x,y:y});
}
// draw the scene
function draw(){
ctx.clearRect(0,0,cw,ch);
// draw all lines at their current positions
for(var i=0;i<lines.length;i++){
drawLine(lines[i],'black');
}
// draw markers if a line is being dragged
if(nearest){
// point on line nearest to mouse
ctx.beginPath();
ctx.arc(nearest.pt.x,nearest.pt.y,5,0,Math.PI*2);
ctx.strokeStyle='red';
ctx.stroke();
// marker for original line before dragging
drawLine(nearest.originalLine,'red');
// hightlight the line as its dragged
drawLine(nearest.line,'red');
}
}
function drawLine(line,color){
ctx.beginPath();
ctx.moveTo(line.x0,line.y0);
ctx.lineTo(line.x1,line.y1);
ctx.strokeStyle=color;
ctx.stroke();
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// find nearest line to mouse
nearest=closestLine(startX,startY);
draw();
// set dragging flag
isDown=true;
}
function handleMouseUpOut(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// clear dragging flag
isDown=false;
nearest=null;
draw();
}
function handleMouseMove(e){
if(!isDown){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// calc how far mouse has moved since last mousemove event
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
// change nearest line vertices by distance moved
var line=nearest.line;
line.x0+=dx;
line.y0+=dy;
line.x1+=dx;
line.y1+=dy;
// redraw
draw();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Drag the lines...</h4>
<canvas id="canvas" width=300 height=300></canvas>
回答2:
On
mousedown
, record your mouse position and line position and register amousemove
handler closed around those starting positions, and register amouseup
handler to remove themousemove
handler.During the
mousemove
handler, find the offset between the current mouse position and the starting mouse position, add this offset to the starting line position and then redraw the canvas using this new position.
You'll probably want to be tracking the transforms of each object independently, so in essence you'll be recreating a retained drawing mode graphics system like SVG or HTML yourself.
来源:https://stackoverflow.com/questions/5559248/how-to-create-a-draggable-line-in-html5-canvas