Decodal
Decodal

Reputation: 323

Making paths and images draggable in Raphael js

Is it possible to be able to drag and drop objects other than just circles and rectangles around a page using Raphael js?

I want to add in paths and images which you can then move around but its proving tricky. I would like to work this out with Raphael because of its support with touch interfaces.

Here is the code

<script>
    window.onload = function () {
        var R = Raphael(0, 0, "100%", "100%"),
            r = R.circle(100, 100, 50).attr({fill: "hsb(0, 1, 1)", stroke: "none", opacity: .5}),
            g = R.circle(210, 100, 50).attr({fill: "hsb(.3, 1, 1)", stroke: "none", opacity: .5}),
            b = R.circle(320, 100, 50).attr({fill: "hsb(.6, 1, 1)", stroke: "#fff", "fill-opacity": 0, "stroke-width": 0.8, opacity: .5}),
            p = R.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z") .attr({fill: "hsb(.8, 1, 1)", stroke: "none", opacity: .5});
        var start = function () {
            this.ox = this.attr("cx");
            this.oy = this.attr("cy");
            this.animate({r: 70, opacity: .25}, 500, ">");
        },
        move = function (dx, dy) {
            this.attr({cx: this.ox + dx, cy: this.oy + dy});
        },
        up = function () {
            this.animate({r: 50, opacity: .5}, 500, ">");
        };
        R.set(r, g, b, p).drag(move, start, up);
    };
</script>

Upvotes: 19

Views: 14481

Answers (7)

Rick Westera
Rick Westera

Reputation: 3300

As translate is being deprecated in Raphael, I've modified Nathan's answer to work with transform:

var paper = Raphael(10, 50, 320, 200);

var tri = paper.path("M0 0L0 20L25 10L0 0Z").attr("fill", "#ff0");

var start = function () {
  this.lastdx ? this.odx += this.lastdx : this.odx = 0;
  this.lastdy ? this.ody += this.lastdy : this.ody = 0;
  this.animate({"fill-opacity": 0.2}, 500);
},
move = function (dx, dy) {
  this.transform("T"+(dx+this.odx)+","+(dy+this.ody));
  this.lastdx = dx;
  this.lastdy = dy;
},
up = function () {
  this.animate({"fill-opacity": 1}, 500);
};

tri.drag(move, start, up);

I'm relatively new to Raphael and came up with this through trial and error, so someone out there might have an explanation of why it works or a cleaner way of doing it ;)

Upvotes: 4

Nathan Colgate
Nathan Colgate

Reputation: 141

The key here (that I found) is to convert the x and y deltas into translate values, which the path object understands.

http://www.nathancolgate.com/post/2946823151/drag-and-drop-paths-in-raphael-js

Effectively the same approach:

var paper = Raphael(10, 50, 320, 200);

var tri = paper.path("M0 0L0 20L25 10L0 0Z").attr("fill", "#ff0");
var rex = paper.rect(10, 20, 50, 50).attr("fill", "#ff0");

var start = function () {
  this.odx = 0;
  this.ody = 0;
  this.animate({"fill-opacity": 0.2}, 500);
},
move = function (dx, dy) {
  this.translate(dx - this.odx, dy - this.ody);
  this.odx = dx;
  this.ody = dy;
},
up = function () {
    this.animate({"fill-opacity": 1}, 500);
};

tri.drag(move, start, up);
rex.drag(move, start, up);

Upvotes: 14

Elbert Alias
Elbert Alias

Reputation: 1790

In case anyone is still looking for a solution, here's a plugin that scales, rotates and drags all shapes including paths.

https://github.com/ElbertF/Raphael.FreeTransform

Upvotes: 0

cabreracanal
cabreracanal

Reputation: 934

I would recommend you raphael.draggable library, that makes the trick for you. I used it with a map application that allows the user to use zoom over the map and then drag it.

I had a problem with this library in IE8 because in the function events refering to mousedown, mousemove, etc. IE drops an exception, telling the user that event is null. You can solve it by replacing the event by e and adding e = e || event in the raphael.draggable.js script. This fix doesn't affect other browsers.

So, the method mousemove in the startDragger is:

function startDragger() {
  document.onmousemove = function(e) {
    e = e || event
    if (paper.draggable.current()) {
      var transX = e.clientX - lastDragX;
      var transY = e.clientY - lastDragY;

      paper.draggable.current().translate(transX, transY);
      lastDragX = e.clientX;
      lastDragY = e.clientY;
    }
  };
}

And the link: https://github.com/mephraim/raphael.draggable

Hope this could help you.

Upvotes: 1

Chielus
Chielus

Reputation: 632

it's not that hard if you understand the usual dragging functions Chris Butler gave you. I use this:

var start = function () {
  //storing original coordinates
  this.xSource = this.attrs.path[0][1];
  this.ySource = this.attrs.path[0][2];
  this.xDest = this.attrs.path[1][1];
  this.yDest = this.attrs.path[1][2];
  this.attr({opacity: 0.5});
  },
  move = function (dx, dy) {
  //move will be called with dx and dy
  var xS = this.xSource+dx;
  var xD = this.xDest+dx;
  var yS = this.ySource+dy;
  var yD = this.yDest+dy;
  this.attr({path: "M"+ xS +" "+ yS +"L"+ xD +" "+yD});
  },
  drag = function(){
  this.node.drag(this.move,this.start,this.up);
  };

You can also know which sort of figure you're dragging in the functions with this.type, so that you can make these functions work for all sort of figures.

Upvotes: 0

Chris Butler
Chris Butler

Reputation: 11

Try this for non-circles. Circles attributes are different than images, text, etc, I think.

    var start = function () {
        this.ox = this.attr("x");
        this.oy = this.attr("y");
        this.animate({r: 70, opacity: .25}, 500, ">");
    },
    move = function (dx, dy) {
        this.attr({x: this.ox + dx, y: this.oy + dy});
    },
    up = function () {
        this.animate({r: 50, opacity: .5}, 500, ">");
    };

Upvotes: 1

ciw1973
ciw1973

Reputation: 11

I experimented with this a little while ago, and got it working using the following approach:

  • Add an initially hidden, styled, absolutely positioned div with a transparent background and suitable border styling to your page, and using jQuery/UI make it draggable.
  • Add a click event to each of the Rapahel/SVG elements you wish to be draggable, and in this event add code to resize and reposition the div over the element which has just been clicked and then make it visible.
  • Add code to the div which updates the position of the Raphael element when it is dragged.

I extended this to add resizing capabilities, and this also worked well, but going forward it would be great to see drag, drop and resize capabilities (ideally properly integrated into the library rather than using jQuery) built into Raphael, as these features would open up a whole bunch of possibilities for in-browser designers using pure Raphael.

Upvotes: 1

Related Questions