Reputation: 721
I have tool similar to paint where i allow user to draw different shapes on canvas using mouse events.I want to allow the user to drag the shapes(same as paint) once they are drawn on canvas. Did anyone done like this before? I have already tried using OOPDragging but that didn't work in my case.Also my tool include all kind of shapes like Line,elbow connector,oval,text,image and not just circles and rectangles. Can anyone please suggest some easy to achieve solution for this as i need it ASAP. Thanks in advance.
Upvotes: 2
Views: 3942
Reputation: 105015
A Demo: http://jsfiddle.net/m1erickson/JrzM2/
Assume you've created this triangle in your paint program
And the points for that triangle are in an array like this:
[{x:0,y:20},{x:30,y:0},{x:70,y:45}]
To move that triangle to [20,35], you need to first offset the triangle by x:20 & y:35
var myTriangle={
x:20,
y:35,
points:[{x:0,y:20},{x:30,y:0},{x:70,y:45}]
}
Then you can draw the triangle at [20,35] like this:
Note that the offsets (20,35) are added to each point in the triangle's position
function draw(myTriangle){
var x=myTriangle.x;
var y=myTriangle.y;
var points=myTriangle.points;
ctx.beginPath();
ctx.moveTo( x+points[0].x, y+points[0].y );
for(var i=1;i<points.length;i++){
ctx.lineTo( x+points[i].x, y+points[i].y );
}
ctx.closePath();
ctx.fill();
}
To drag the triangle, you listen for mouse events
In mousedown
Canvas has a nice built-in function to test if any specified point is inside a path like the triangle.
This function is context.isPointInPath(mouseX,mouseY) and it tests if mouseX/mouseY is inside the last drawn path.
If the mouse was pressed over the triangle, we set the isSelected
flag to indicate the triangle should be dragged with every mousemove.
So the mousedown function looks like this:
function handleMouseDown(e){
// tell the browser we're using mousedown,
// so don't bother doing any browser stuff with this event
e.preventDefault();
// get the current mouseX,mouseY position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// test if mouseX,mouseY is inside the triangle
if(ctx.isPointInPath(startX,startY)){
// if yes, set the "isSelected" flag
// which indicates that the triangle should
// move with the mouse
isSelected=true;
}
}
In mousemove
The mousemove event is triggered about 20-30 times per second as the user moves the mouse.
In mousemove, if the triangle isSelected, we want to calculate how far the mouse has moved since the last mousemove event.
Then we want to change the x,y position of the triangle by the distance that the mouse has moved.
So the mousemove function looks like this:
function handleMouseMove(e){
// if the triangle wasn't selected during mousedown
// there's nothing to do, so just return
if(!isSelected){return;}
e.preventDefault();
// get the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// calculate how far the mouse has moved since the last mousemove
var dx=mouseX-startX;
var dy=mouseY-startY;
// for next mousemove, reset the starting XY to the current XY
startX=mouseX;
startY=mouseY;
// move the triangle by the change in mouse position
myTriangle.x+=dx;
myTriangle.y+=dy;
// clear the canvas and
// redraw the triangle at its new position
context.clearRect(0,0,canvas.width,canvas.height);
draw(myTriangle);
}
In mouseup
In mouseup, the isSelected
flag is cleared since the drag is over:
function handleMouseUp(e){
e.preventDefault();
isSelected=false;
}
Here's code for a more complex example with multiple shapes:
<!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(){
// vars related to canvas
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 cw=canvas.width;
var ch=canvas.height;
// vars related to dragging
var isDown=false;
var startX;
var startY;
// save your shape-points in
var shapes=[];
var selectedShape=null;
// test shapes
addShape(50,50,[{x:0,y:20},{x:30,y:0},{x:70,y:45}],"blue","red");
addShape(100,100,
[{x:0,y:10},{x:30,y:10},{x:30,y:0},
{x:45,y:15},{x:30,y:30},{x:30,y:20},{x:0,y:20}],
"green","red");
// begin...
drawAll();
function addShape(x,y,points,fill,stroke){
shapes.push({x:x,y:y,points:points,fill:fill,stroke:stroke});
}
function define(shape){
var x=shape.x;
var y=shape.y;
var points=shape.points;
ctx.beginPath();
ctx.moveTo(x+points[0].x,y+points[0].y);
for(var i=1;i<points.length;i++){
ctx.lineTo(x+points[i].x,y+points[i].y);
}
ctx.closePath();
}
function draw(shape){
define(shape);
ctx.fillStyle=shape.fill;
ctx.fill();
ctx.strokeStyle=shape.stroke;
ctx.stroke();
}
function drawAll(){
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<shapes.length;i++){
draw(shapes[i]);
}
}
function handleMouseDown(e){
e.preventDefault();
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
for(var i=0;i<shapes.length;i++){
define(shapes[i]);
if(ctx.isPointInPath(startX,startY)){
selectedShape=shapes[i];
isDown=true;
}
}
}
function handleMouseUp(e){
e.preventDefault();
isDown=false;
selectedShape=null;
}
function handleMouseOut(e){
e.preventDefault();
isDown=false;
selectedShape=null;
}
function handleMouseMove(e){
if(!isDown){return;}
e.preventDefault();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
selectedShape.x+=dx;
selectedShape.y+=dy;
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 the shapes around the canvas</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Upvotes: 4
Reputation: 10776
I had the same problem some time ago, but I only needed lines, but I think you can use my solution for all the different shapes.
Basically you'll need to overlapping canvases, one that listens to the mouse events and draws the temporary shape, once the user releases the mouse button, you get the values and draw the shape on the original canvas. Here's a link to what I did: http://www.zhereicome.com/experiments/statics/drawtogether/
and the main js file: http://www.zhereicome.com/experiments/statics/drawtogether/js/main.js
The important part is: temp_canvas.onmousemove = function(e) { //console.log(e); if ( self.mouseDown ) { self.drawTempLine(e.clientX, e.clientY); } }
temp_canvas.onmousedown = function(e) {
start = {
x: e.clientX,
y: e.clientY
}
self.mouseDown = true;
}
temp_canvas.onmouseup = function(e) {
end = {
x: e.clientX,
y: e.clientY
}
self.mouseDown = false;
self.sendToServer(); //When the user has finished drawing the line, send it to the server
}
I have a function sendToServer() because my project was a realtime multiplayer drawing app. In your case you'll have to replace it with a drawOnFinalCanvas();
Upvotes: 0