Draggables and Resizables in SVG
Asked Answered
B

4

35

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?

Broida answered 1/9, 2010 at 0:25 Comment(0)
J
56

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;
};
Johore answered 7/10, 2010 at 2:58 Comment(6)
Hi, Great examples above, but I'm looking to extend it a little further. I would like to be able to use re-sizable with circles. I have managed to get the rect's working but struggling with this. I think my issue stems from the rmove method, as when I set this.box width and height there is no alternative for circles. Any ideas, or any possible tutorials you know of. Thanks.Witter
@Adam - For circles and ellipses, you just use the center point, cx and cy and the radius. Here is an example of how to handle both rectangles and ellipses for draggging ==> jsfiddle.net/vPyjc (look at the "dragger" function, it uses if shapes[ind].type == "rect" to check for the type of shape and handles each one separately. ------ This means you'll essentially have to write separate code for handling the dragging and resisizing of ellipses (using radius and center coords instead of the box).Johore
Sorry, maybe I didnt make myself clear. I dont have any issues dragging the shapes around. It is the resizing that has issues. I can resize rects like in your example above, but I cannot resize circles/elipses using a draggable shape, even though I am selecting the correct attributes. I think the issue is with the .box as there is no width and height attribute for circles.Witter
@Adam - I understood. That's why I said to use cx, cy and radius for circles. The radius attribute is accessed with r, and you use that instead of the box to resize a circle - Here's a quick circle resizing example: jsfiddle.net/EqrQZ (of course you'd want to add a handle for resizing in a more polished example) - Note that I'm using r to resize the circle. - The box is just a copy of the bigger object, you have to create it yourself... look at the last line of code ==> s.box = c;Johore
@Adam - Putting the handles on a circle is a little trickier due to how the handle would move, but here's a quick start ==> jsfiddle.net/Pxbrf - You could also figure it out for an ellipse with the ability to move the handle in order to change the shape of the ellipse.Johore
Ahh it all makes sense now and I have it working. Thanks for all the help. I probably should have asked it in a question so that you could of had the Rep. Thanks :)Witter
D
4

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

Distribute answered 7/12, 2011 at 3:58 Comment(0)
L
3

Try Graphiti here is the link : Draw2d and Graphiti

It is based on Raphael and very easy to use.

Lahnda answered 23/8, 2012 at 13:34 Comment(0)
C
1

There is also this plugin for SVG.js.

https://github.com/svgdotjs/svg.resize.js

Cato answered 11/12, 2018 at 12:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.