Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draggables and Resizables in SVG

I want to make an svg element (path, rect, or circle) be able to be draggable and give it resize handles.

But unlike HTML DOM, not all elements have an upper left hand corner x,y coordinate and a width and height for a box surrounding the content. This makes it inconvenient to make a generic resize or drag procedure.

Is it a good idea to have each path or circle be drawn inside its own svg object to give me a box to play with?

How is draggable/resizable typically implemented in SVG?

like image 917
Boris Yeltz Avatar asked Sep 01 '10 00:09

Boris Yeltz


People also ask

Can SVG be draggable?

One of the most common forms of interaction on a computer is clicking and dragging. I use it a lot for interactive demos, such as those in my SVG tutorial, and have a built a library for making simple draggable SVG diagrams.

How do you drag elements in SVG?

Using the mouse to drag an SVG element (or group of elements) can be accomplished by: Adding mousedown handler to starts the drag: adding a translation on the element to use during dragging (if needed), tracking mousemove events, and adding a mouseup handler to end the drag.

How drag and drop works in JavaScript?

The HTML Drag and Drop API relies on the DOM's event model to get information on what is being dragged or dropped and to update that element on drag or drop. With JavaScript event handlers, you can turn any element into a draggable item or an item that can be dropped into.

What is interact JS?

interact. js is a JavaScript library for drag and drop, resizing and multi-touch gestures for modern browsers. Its free and open source version comes with powerful options like inertia and modifiers for snapping and restricting.


2 Answers

Note: For both drag and resize, you'll have to make separate cases for certain different types of elements. Take a look in the example I provide later on that handles the dragging of both ellipses and rectangles in the same set of functions.


To make an element dragable you use:

element.drag(move, start, up); 

The three arguments are references to the functions that handle moving (dragging), starting (mouse down), and the stopping (mouseup).

For example to make a draggable circle (from the documentation):

window.onload = function() { var R = Raphael("canvas", 500, 500);     var c = R.circle(100, 100, 50).attr({     fill: "hsb(.8, 1, 1)",     stroke: "none",     opacity: .5 }); var start = function () {     // storing original coordinates     this.ox = this.attr("cx");     this.oy = this.attr("cy");     this.attr({opacity: 1}); }, move = function (dx, dy) {     // move will be called with dx and dy     this.attr({cx: this.ox + dx, cy: this.oy + dy}); }, up = function () {     // restoring state     this.attr({opacity: .5}); }; c.drag(move, start, up);     };​ 

jsFiddle example


In the above example, the ox and oy properties are tacked on to the element to keep track of its location, and these properties in conjunction with dx and dy are used to change the location of the element as it's being dragged.

A more complicated drag and drop to answer this question.

To make an object resizeable, you would simply create a second set of drag and drop methods for the resizer and just adjust the target elements height and width based on dragging the resizer.

Here's a full of one drag and drop and resizeable box I wrote up:

jsFiddle example of drag and drop and resizeable box

window.onload = function() { var R = Raphael("canvas", 500, 500),     c = R.rect(100, 100, 100, 100).attr({             fill: "hsb(.8, 1, 1)",             stroke: "none",             opacity: .5,             cursor: "move"         }),     s = R.rect(180, 180, 20, 20).attr({             fill: "hsb(.8, .5, .5)",             stroke: "none",             opacity: .5         }),     // start, move, and up are the drag functions     start = function () {         // storing original coordinates         this.ox = this.attr("x");         this.oy = this.attr("y");         this.attr({opacity: 1});          this.sizer.ox = this.sizer.attr("x");         this.sizer.oy = this.sizer.attr("y");         this.sizer.attr({opacity: 1});     },     move = function (dx, dy) {         // move will be called with dx and dy         this.attr({x: this.ox + dx, y: this.oy + dy});         this.sizer.attr({x: this.sizer.ox + dx, y: this.sizer.oy + dy});             },     up = function () {         // restoring state         this.attr({opacity: .5});         this.sizer.attr({opacity: .5});             },     rstart = function () {         // storing original coordinates         this.ox = this.attr("x");         this.oy = this.attr("y");          this.box.ow = this.box.attr("width");         this.box.oh = this.box.attr("height");             },     rmove = function (dx, dy) {         // move will be called with dx and dy         this.attr({x: this.ox + dx, y: this.oy + dy});         this.box.attr({width: this.box.ow + dx, height: this.box.oh + dy});     };        // rstart and rmove are the resize functions;     c.drag(move, start, up);     c.sizer = s;     s.drag(rmove, rstart);     s.box = c; };​ 

The included event handlers (you can use more of course in conjunction with .node()) and the drag and drop description is at the bottom of the page in the documentation.

You would simply make one Raphael canvas, and then each item would be a different element. Just assign them to variables so you can handle them, like in the example above ( c was used to refer to the created circle element ).

In response to comments here is a simple drag and drop + resize able circle. The trick is that circles use the attributes cx and cy for positioning and r for size. The mechanics are pretty much the same... an ellipse would be slightly more complicate, but again it's just a question of working with the right attributes.

jsFiddle example of drag and drop and resizeable circle

window.onload = function() {     var R = Raphael("canvas", 500, 500),         c = R.circle(100, 100, 50).attr({             fill: "hsb(.8, 1, 1)",             stroke: "none",             opacity: .5         }),         s = R.circle(125, 125, 15).attr({             fill: "hsb(.8, .5, .5)",             stroke: "none",             opacity: .5         });     var start = function () {         // storing original coordinates         this.ox = this.attr("cx");             this.oy = this.attr("cy");          this.sizer.ox = this.sizer.attr("cx");             this.sizer.oy = this.sizer.attr("cy")          this.attr({opacity: 1});         this.sizer.attr({opacity: 1});     },     move = function (dx, dy) {         // move will be called with dx and dy         this.attr({cx: this.ox + dx, cy: this.oy + dy});         this.sizer.attr({cx: this.sizer.ox + dx, cy: this.sizer.oy + dy});     },     up = function () {         // restoring state         this.attr({opacity: .5});         this.sizer.attr({opacity: .5});     },     rstart = function() {         // storing original coordinates         this.ox = this.attr("cx");         this.oy = this.attr("cy");                  this.big.or = this.big.attr("r");     },     rmove = function (dx, dy) {         // move will be called with dx and dy         this.attr({cx: this.ox + dy, cy: this.oy + dy});         this.big.attr({r: this.big.or + Math.sqrt(2*dy*dy)});     };     c.drag(move, start, up);         c.sizer = s;     s.drag(rmove, rstart);     s.big = c; }; 
like image 121
Peter Ajtai Avatar answered Sep 27 '22 23:09

Peter Ajtai


Have a look at Raphael.FreeTransform which seems to do what you're after.

like image 20
Elbert Alias Avatar answered Sep 28 '22 00:09

Elbert Alias