Tuesday, September 28, 2010

Can't Animate Raphaël path Positions

‹prev | My Chain | next›

I made some good progress last night making animate-frames more raphaël.js-like. Before I push those changes back into the main repository, I would like to explore easing in the animate() method.

Ultimately, the animate() method in animate-frames is calling the translate_object() method, which animates movement with animateAlong():
    translate_object: function(frame, x, y, ms, easing) {
// offset between end coordinates and current location
var x_diff = x - frame[frame.length-1].getBBox().x;
var y_diff = y - frame[frame.length-1].getBBox().y;

for (var i=0; i<frame.length; i++) {
var obj = frame[i];
// calculate path starting from absolute coordinates and moving
// relatively from there by the offset
var p = "M " + obj.getBBox().x + " " + obj.getBBox().y +
" l " + x_diff + " " + y_diff;

// animate along that path
if (ms && ms > 50)
obj.animateAlong(p, ms);
obj.translate(x_diff, y_diff);
My first instinct is to add and easing parameter to animateAlong(). Sadly, according to the documentation (and the code), the method signature only takes path, ms, rotate, and callback (invoked after the animation completes).


So maybe I can use animate() instead of animateAlong():
         // animate along that path
if (ms && ms > 50)
obj.animate({x: x + x_diff, y: y + y_diff}, ms, easing);
// ...
But when I do that, my players don't move at all. Grr.. what gives?

To find out, I break the problem down to basics. Using a raphaël paper object, r, I create a circle and animate its center x-coordinate:
c = r.circle(200, 200, 10).attr({stroke: "#333", "stroke-width": 4, "fill": '#333'})
c.animate({cx: 0}, 5000)
That works. The circle moves from 200, 200 to 0, 200 over the course of 5 seconds.

Next I try drawing an object with SVG path information—the same way that the objects inside my animations are being drawn:
o = r.path("m 27.970621,11.503807 c 0.09878,4.794487 -3.625825,9.254299 -8.244694,9.888541 C 15.282994,22.185422 10.583894,19.322284 9.1090954,14.943452 7.4780985,10.605964 9.2934491,5.2861677 13.201573,2.948285 c 3.892493,-2.53638571 9.452051,-1.6375587 12.397375,2.008774 1.523084,1.7954543 2.381825,4.1636216 2.371673,6.546748 z").attr({fill: '#090'})
o.animate({cx: 50}, 5000)
That does not work. The player stays at its original coordinates.

I can animate certain other attributes, such as the fill color:
o.animate({fill: '#00f'}, 5000)
From this, I conclude that it is not possible to animate location elements on objects drawn via paths. That is not too surprising based on my earlier investigation. Not surprising, but still disappointing.

I am not quite ready to abandon easing. I really want to use it to animate collisions (the "bounce" easing is particularly nice). Since I cannot animate-with-easing the SVG paths that make up my frames, I think that I ought to be able to hide the frames, show another object that can be animated. That is not perfect, but it ought to suffice for my purposes.

I will get started on that tomorrow.

Day #240


  1. you can animate the path objects by giving another path as a parameter to the animate function

  2. The particular use-case that I am trying to solve here is to animate paths *and* translate them at the same time:


    AFAIK that's not possible: http://japhr.blogspot.com/2010/07/combining-raphaeljs-animations.html

    Happy to be proven wrong though :)

  3. If you want to animate a path using "translate" - why don't you simply clone the path, translate the clone and then animate the path attribute...


    var paper = Raphael("board", 900, 900);
    var graphic = paper.path("M 0 0 L 20 0 L 10 20 z");
    tempGraphic = graphic.clone();
    graphic.animate({path:this.tempGraphic.attr("path")}, 500);

  4. Or even easier, animate using the "translation" attribute.

    var paper = Raphael("board", 900, 900);
    var graphic = paper.path("M 0 0 L 20 0 L 10 20 z");
    graphic.animate({translation: '40 80'}, 2000);