Skip to content

Instantly share code, notes, and snippets.

@thebinarypenguin
Created January 4, 2012 02:43
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save thebinarypenguin/1558194 to your computer and use it in GitHub Desktop.
Save thebinarypenguin/1558194 to your computer and use it in GitHub Desktop.
Movable and re-sizable rectangle using Raphael SVG
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Movable and Re-sizable Raphael JS Shape</title>
</head>
<body>
<div id="paper"></div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://github.com/DmitryBaranovskiy/raphael/raw/master/raphael-min.js"></script>
<script>
(function() {
var dragStart = function() {
// Save some starting values
this.ox = this.attr('x');
this.oy = this.attr('y');
this.ow = this.attr('width');
this.oh = this.attr('height');
this.dragging = true;
};
var dragMove = function(dx, dy) {
// Inspect cursor to determine which resize/move process to use
switch (this.attr('cursor')) {
case 'nw-resize' :
this.attr({
x: this.ox + dx,
y: this.oy + dy,
width: this.ow - dx,
height: this.oh - dy
});
break;
case 'ne-resize' :
this.attr({
y: this.oy + dy ,
width: this.ow + dx,
height: this.oh - dy
});
break;
case 'se-resize' :
this.attr({
width: this.ow + dx,
height: this.oh + dy
});
break;
case 'sw-resize' :
this.attr({
x: this.ox + dx,
width: this.ow - dx,
height: this.oh + dy
});
break;
default :
this.attr({
x: this.ox + dx,
y: this.oy + dy
});
break;
}
};
var dragEnd = function() {
this.dragging = false;
};
var changeCursor = function(e, mouseX, mouseY) {
// Don't change cursor during a drag operation
if (this.dragging === true) {
return;
}
// X,Y Coordinates relative to shape's orgin
var relativeX = mouseX - $('#paper').offset().left - this.attr('x');
var relativeY = mouseY - $('#paper').offset().top - this.attr('y');
var shapeWidth = this.attr('width');
var shapeHeight = this.attr('height');
var resizeBorder = 10;
// Change cursor
if (relativeX < resizeBorder && relativeY < resizeBorder) {
this.attr('cursor', 'nw-resize');
} else if (relativeX > shapeWidth-resizeBorder && relativeY < resizeBorder) {
this.attr('cursor', 'ne-resize');
} else if (relativeX > shapeWidth-resizeBorder && relativeY > shapeHeight-resizeBorder) {
this.attr('cursor', 'se-resize');
} else if (relativeX < resizeBorder && relativeY > shapeHeight-resizeBorder) {
this.attr('cursor', 'sw-resize');
} else {
this.attr('cursor', 'move');
}
};
// Create drawing area
var paper = Raphael("paper", 500, 500);
// Add a rectangle
var shapes = paper.add([{
'type' : 'rect',
'x' : 150,
'y' : 150,
'width' : 100,
'height' : 80,
'fill' : '#759dcd',
'stroke' : '#3b5068',
'stroke-width' : 10
}]);
// Attach "Mouse Over" handler to rectangle
shapes[0].mousemove(changeCursor);
// Attach "Drag" handlers to rectangle
shapes[0].drag(dragMove, dragStart, dragEnd);
})();
</script>
</body>
</html>
@rawsonl
Copy link

rawsonl commented Mar 13, 2012

This works pretty good but I have changed the events a little because of jumping and shifting of objects.

var dragStart = function() {
this.ox = this.attr('x');
this.oy = this.attr('y');
this.ow = this.attr('width');
this.oh = this.attr('height');
this.dragging = true;
};
var dragMove = function(dx, dy) {
if (this.state === 'movable') {
this.attr({ x: this.ox + dx, y: this.oy + dy });
}
else {
switch (this.attr('cursor')) {
case 'nw-resize':
this.attr({
x: this.ox + dx,
y: this.oy + dy,
width: this.ow - dx,
height: this.oh - dy
});
break;
case 'n-resize':
this.attr({
y: this.oy + dy,
height: this.oh - dy
});
break;
case 'ne-resize':
this.attr({
y: this.oy + dy,
width: this.ow + dx,
height: this.oh - dy
});
break;
case 'w-resize':
this.attr({
x: this.ox + dx,
width: this.ow - dx
});
break;
case 'e-resize':
this.attr({
width: this.ow + dx
});
break;
case 'sw-resize':
this.attr({
x: this.ox + dx,
width: this.ow - dx,
height: this.oh + dy
});
break;
case 's-resize':
this.attr({
height: this.oh + dy
});
break;
case 'se-resize':
this.attr({
width: this.ow + dx,
height: this.oh + dy
});
break;
default:
this.attr({
x: this.ox + dx,
y: this.oy + dy
});
break;
}
}
};
var changeCursor = function(e, mouseX, mouseY) {
if (this.dragging) {
return;
}
var side = sideDection(e, this);
// if the user's mouse is along the edge we want resize
if (side) {
this.state = 'resizable';
}
// else it's towards the middle and we want to move
else {
this.state = 'movable';
}
var cursor = (side) ? side + '-resize' : 'move';
this.attr('cursor', cursor);
};
var sideDection = function(event, ct) {
var ctFactor = 2;
var directions = {
n: Math.abs(event.offsetY - ct.attr('y')) <= ctFactor,
s: Math.abs(event.offsetY - (ct.attr('height') + ct.attr('y'))) <= ctFactor,
e: Math.abs(event.offsetX - (ct.attr('width') + ct.attr('x'))) <= ctFactor,
w: Math.abs(event.offsetX - ct.attr('x')) <= ctFactor
},side = '';
for (var key in directions) { // loop through all 4 sides and concate the ones that are true
if (directions.hasOwnProperty(key)) {
if (directions[key]) {
side = side + key;
}
}
}
return side;
};

Thanks again!
Linda Rawson
http://www.sensorytech.net

@jquindlen
Copy link

What license is this code under? Would prefer MIT/BSD or LGPL. I'm looking to use in an LGPL project myself. Very nice code.

@thebinarypenguin
Copy link
Author

The Raphaël library (and my code) both use the MIT license.

@jquindlen
Copy link

You my friend, are amazing! Thank you so much for this excellent code and for releasing it under the MIT license.

@ganzuul
Copy link

ganzuul commented Feb 20, 2013

Beautiful. Thank you. I will treat your code with love and respect.

@lucindom
Copy link

lucindom commented Jan 3, 2014

Hello, with the version 2.1.2, it´s could be an plugin:

(function(){
/* Necesita Jquery y Raphael */
Raphael.el.movible=function (paper) {
this.mousemove(this.dragMoving);
this.drag(this.dragMove, this.dragStart, this.dragEnd);
};
Raphael.el.dragStart = function() {
// Save some starting values
this.ox = this.attr('x');
this.oy = this.attr('y');
this.ow = this.attr('width');
this.oh = this.attr('height');
this.dragging = true;
};
Raphael.el.dragMove = function(dx, dy) {
switch (this.attr('cursor')) {
case 'nw-resize' : this.attr({ x: this.ox + dx, y: this.oy + dy, width: this.ow - dx, height: this.oh - dy }); break;
case 'ne-resize' : this.attr({ y: this.oy + dy , width: this.ow + dx, height: this.oh - dy}); break;
case 'se-resize' : this.attr({ width: this.ow + dx, height: this.oh + dy }); break;
case 'sw-resize' : this.attr({ x: this.ox + dx, width: this.ow - dx, height: this.oh + dy});break;
default : this.attr({ x: this.ox + dx, y: this.oy + dy});break;
}
};
Raphael.el.dragEnd = function() { this.dragging = false;};
Raphael.el.dragMoving = function(e, mouseX, mouseY) {
if (this.dragging === true) {return;}
// X,Y Coordinates relative to shape's orgin
var relativeX = mouseX - this.paper._left - this.attr('x');
var relativeY = mouseY - this.paper._top - this.attr('y');
var shapeWidth = this.attr('width');
var shapeHeight = this.attr('height');
var resizeBorder = 10;
if (relativeX < resizeBorder && relativeY < resizeBorder) { this.attr('cursor', 'nw-resize');}
else if (relativeX > shapeWidth-resizeBorder && relativeY < resizeBorder) {this.attr('cursor', 'ne-resize');}
else if (relativeX > shapeWidth-resizeBorder && relativeY > shapeHeight-resizeBorder) {this.attr('cursor', 'se-resize');}
else if (relativeX < resizeBorder && relativeY > shapeHeight-resizeBorder) {this.attr('cursor', 'sw-resize');}
else { this.attr('cursor', 'move');}
};
})();

Comments?

thank's!!!!

@odegraciajr
Copy link

Hoy can I apply this on paper.text?

@surferxo3
Copy link

surferxo3 commented May 3, 2018

To prevent rectangle resize and drag out of paper border, use the following snippet below:

/**
 *  GETTER / SETTER METHOD(s)
 * */
getX(rect, ddx) {
	var width = rect.paper.width,
		thisBox = rect.getBBox();

	if (ddx < 0) {
		ddx = 0;
	} else if (ddx > width - thisBox.width) {
		ddx = width - thisBox.width;
	}

	return ddx;
}

getY(rect, ddy) {
	var height = rect.paper.height,
		thisBox = rect.getBBox();

	if (ddy < 0) {
		ddy = 0;
	} else if (ddy > height - thisBox.height) {
		ddy = height - thisBox.height;
	}

	return ddy;
}

getWidth(rect, ddw) {
	var width = rect.paper.width,
		thisBox = rect.getBBox();

	if (ddw < 45) {
		ddw = 45;
	} else if (ddw > width - thisBox.x) {
		ddw = width - thisBox.x;
	}

	return ddw;
}

getHeight(rect, ddh) {
	var height = rect.paper.height,
		thisBox = rect.getBBox();

	if (ddh < 45) {
		ddh = 45;
	} else if (ddh > height - thisBox.y) {
		ddh = height - thisBox.y;
	}

	return ddh;
}

/**
 * RAPHAEL EVENT(s)
 * */
dragStart() {
	this.ox = this.attr('x');
	this.oy = this.attr('y');
	this.ow = this.attr('width');
	this.oh = this.attr('height');
	this.dragging = true;
}

dragMove(dx, dy) {
	var ddx = this.ox + dx;
	var ddy = this.oy + dy;

	switch (this.attr('cursor')) {
		case 'nw-resize':
			this.attr({
				x: getX(this, ddx),
				y: getY(this, ddy),
				width: getWidth(this, this.ow - dx),
				height: getHeight(this, this.oh - dy)
			});
			break;
		case 'ne-resize':
			this.attr({
				y: getY(this, ddy),
				width: getWidth(this, this.ow + dx),
				height: getHeight(this, this.oh - dy)
			});
			break;
		case 'se-resize':
			this.attr({
				width: getWidth(this, this.ow + dx),
				height: getHeight(this, this.oh + dy)
			});
			break;
		case 'sw-resize':
			this.attr({
				x: getX(this, ddx),
				width: getWidth(this, this.ow - dx),
				height: getHeight(this, this.oh + dy)
			});
			break;
		case 'w-resize':
			this.attr({
				x: getX(this, ddx, this.ow - dx),
				width: getWidth(this, this.ow - dx)
			});
			break;
		case 'e-resize':
			this.attr({
				width: getWidth(this, this.ow + dx)
			});
			break;
		case 's-resize':
			this.attr({
				height: getHeight(this, this.oh + dy)
			});
			break;
		case 'n-resize':
			this.attr({
				y: getY(this, ddy),
				height: getHeight(this, this.oh - dy)
			});
			break;
		default:
			this.attr({
				x: getX(this, ddx),
				y: getY(this, ddy)
			});
			break;
	}
}

dragEnd(e) {
	this.dragging = false;
}

changeCursor(e, mouseX, mouseY) {
	if (this.dragging === true) {
		return;
	}

	var relativeX = mouseX - ($('#paper').offset().left) - this.attr('x');
	var relativeY = mouseY - ($('#paper').offset().top) - this.attr('y');
	var shapeWidth = this.attr('width');
	var shapeHeight = this.attr('height');
	var resizeBorder = 10;

	if (relativeX < resizeBorder && relativeY < resizeBorder) {
		this.attr('cursor', 'nw-resize');
	} else if (relativeX > shapeWidth - resizeBorder && relativeY < resizeBorder) {
		this.attr('cursor', 'ne-resize');
	} else if (relativeX > shapeWidth - resizeBorder && relativeY > shapeHeight - resizeBorder) {
		this.attr('cursor', 'se-resize');
	} else if (relativeX < resizeBorder && relativeY > shapeHeight - resizeBorder) {
		this.attr('cursor', 'sw-resize');
	} else if (relativeX < resizeBorder && relativeY < shapeHeight - resizeBorder) {
		this.attr('cursor', 'w-resize');
	} else if (relativeX > shapeWidth - resizeBorder && relativeY < shapeHeight - resizeBorder) {
		this.attr('cursor', 'e-resize');
	} else if (relativeX > resizeBorder && relativeY > shapeHeight - resizeBorder) {
		this.attr('cursor', 's-resize');
	} else if (relativeX > resizeBorder && relativeY < resizeBorder) {
		this.attr('cursor', 'n-resize');
	} else {
		this.attr('cursor', 'move');
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment