1

I want to animate a path (actually a set of paths, but I'll get to that) along a curved path.

RaphaelJS 2 removed the animateAlong method, for reasons I haven't been able to discern. Digging into the Raphael documentation's gears demo as abstracted by Zevan, I have got this far:

//adding a custom attribute to Raphael
(function() {
  Raphael.fn.addGuides = function() {
    this.ca.guide = function(g) {
      return {
        guide: g
      };
    };
    this.ca.along = function(percent) {
      var g = this.attr("guide");
      var len = g.getTotalLength();
      var point = g.getPointAtLength(percent * len);
      var t = {
        transform: "t" + [point.x, point.y]
      };
      return t;
    };
  };
})();

var paper = Raphael("container", 600, 600);

paper.addGuides();

// the paths
var circ1 = paper.circle(50, 150, 40);
var circ2 = paper.circle(150, 150, 40);
var circ3 = paper.circle(250, 150, 40);
var circ4 = paper.circle(350, 150, 40);

var arc1 = paper.path("M179,204c22.667-7,37,5,38,9").attr({'stroke-width': '2', 'stroke': 'red'});

// the animation

// works but not at the right place
circ3.attr({guide : arc1, along : 1})
         .animate({along : 0}, 2000, "linear");

http://jsfiddle.net/hKGLG/4/

I want the third circle to animate along the red path. It is animating now, but at a distance from the red path equal to the third circle's original coordinates. The weird thing is that this happens whether the transform translate in the along object is relative (lowercase "t") or absolute (uppercase "T"). It also always animates in the same spot, even if I nudge it with a transform translation just before the animate call.

Any help very appreciated. I just got off the boat here in vector-land. Pointers are helpful--a working fiddle is even better.

Ben
  • 11,082
  • 8
  • 33
  • 47

1 Answers1

5

You're just a hop, skip, and jump away from the functionality that you want. The confusion here concerns the interaction between transformations and object properties -- specifically, that transformations do not modify the original object properties. Translating simply adds to, rather than replaces, the original coordinates of your circles.

The solution is extremely straightforward. In your along method:

this.ca.along = function(percent) {
  var box = this.getBBox( false );  // determine the fundamental location of the object before transformation occurs
  var g = this.attr("guide");
  var len = g.getTotalLength();
  var point = g.getPointAtLength(percent * len);
  var t = {
    transform: "...T" + [point.x - ( box.x + ( box.width / 2 ) ), point.y - ( box.y + ( box.height / 2 ) )]  // subtract the center coordinates of the object from the translation offset at this point in the guide.
  };
  return t;

Obviously, there's some room for optimization here (i.e., it might make sense to create all your circles at 0,0 and then translate them to the display coordinates you want, avoiding a lot of iterative math). But it's functional... see here.

One other caveat: the ...T translation won't effect any other transforms that have already been applied to a given circle. This implementation is not guaranteed to play nicely with other transforms.

Kevin Nielsen
  • 4,413
  • 21
  • 26
  • Brilliant, Kevin. So simple. Thanks! One thing: can you explain the "..." `append`/`prepend` syntax for transforms? The docs skim over this with hardly a whisper. I can't really tell what it's for. – Ben Nov 08 '12 at 22:26
  • Raphael's reference does not need serious overhauling, I'm afraid. The append/prepend is really incredibly useful, especially when you're dealing with paths that may be transformed separately in different locations. Essentially, it lets you insert specific transforms into an existing chain of transforms without wiping the others out. For instance, if you have an element that already has a transform of "R45 0,0 S2,2", prepending "T100,100..." will result in a cumulative transform of "T100,100 R45 0,0 S2,2", and appending it will result in "R45 0,0 S2,0 T100,100" -- different outcomes. – Kevin Nielsen Nov 08 '12 at 23:01
  • 1
    Excellent, thanks. Yeah, I saw Dmitri just bragged on twitter last week that he turned down two offers to write a book on Raphael. "Don't like to write". Maybe you should write that book? I'd buy a copy :) – Ben Nov 08 '12 at 23:36