How to draw a vector path progressively? (Raphael.js)

38,397

Solution 1

I've created a script for this: Scribble.js, based on this great dasharray/dashoffset technique.

Just instantiate it overs a bunch of SVG <path>s:

var scribble = new Scribble(paths, {duration: 3000});
scribble.erase();
scribble.draw(function () {
    // done
});

--

NB: Full USAGE code here: https://gist.github.com/abernier/e082a201b0865de1a41f#file-index-html-L31

Enjoy ;)

Solution 2

Maybe someone is searching for an answer, like me for two days now:

// Draw a path and hide it:
var root = paper.path('M0 50L30 50Q100 100 50 50').hide();
var length = root.getTotalLength();

// Setup your animation (in my case jQuery):
element.animate({ 'to': 1 }, {
    duration: 500,
    step: function(pos, fx) {
        var offset = length * fx.pos;
        var subpath = root.getSubpath(0, offset);
        paper.clear();
        paper.path(subpath);
    }
});

That did the trick for me, only by using RaphaelJS methods.

Here is a jsFiddle example as requested in the comments, http://jsfiddle.net/eA8bj/

Solution 3

Eureka! (Maybe - assuming you're comfortable stepping outside the friendly realm of Raphael into pure SVG land...)

You can use SVG keyTimes and keySplines.

Here's a working example:

http://www.carto.net/svg/samples/animated_bustrack.shtml

...and here's some potentially useful explanation:

http://msdn.microsoft.com/en-us/library/ms533119(v=vs.85).aspx

Solution 4

I'd like to offer an alternative, Raphael+JS-only solution that I have made substantial use of in my own work. It has several advantages over davidenke's solution:

  1. Doesn't clear the paper with each cycle, allowing the animated path to coexist nicely with other elements;
  2. Reuses a single path with Raphael's own progressive animation, making for smoother animations;
  3. Substantially less resource intensive.

Here's the method (which could quite easily be retooled into an extension):

function drawpath( canvas, pathstr, duration, attr, callback )
{
    var guide_path = canvas.path( pathstr ).attr( { stroke: "none", fill: "none" } );
    var path = canvas.path( guide_path.getSubpath( 0, 1 ) ).attr( attr );
    var total_length = guide_path.getTotalLength( guide_path );
    var last_point = guide_path.getPointAtLength( 0 );
    var start_time = new Date().getTime();
    var interval_length = 50;
    var result = path;        

    var interval_id = setInterval( function()
    {
        var elapsed_time = new Date().getTime() - start_time;
        var this_length = elapsed_time / duration * total_length;
        var subpathstr = guide_path.getSubpath( 0, this_length );            
        attr.path = subpathstr;

        path.animate( attr, interval_length );
        if ( elapsed_time >= duration )
        {
            clearInterval( interval_id );
            if ( callback != undefined ) callback();
                guide_path.remove();
        }                                       
    }, interval_length );  
    return result;
}

And here are two samples of its usage on my site: one for Path Transformation, and the other for Progressive Lettering.

Solution 5

Using "pathLength" attribute we can set virtual length to the path. From then we can use this virtual length in "stroke-dasharray". So if we set "pathLength" to 100 units we then can set "stroke-dasharray" to "50,50" wich wuld be exactly 50%, 50% of the path!

There is one problem with this approach: the only browser that supports this attribute is Opera 11.

Here is example of smooth curve drawind animation without javascript or hardcoded length.(Works properly only in Opera 11)

Share:
38,397
Anton Strogonoff
Author by

Anton Strogonoff

You do not need to attribute code snippets from my answers or questions.

Updated on February 05, 2020

Comments

  • Anton Strogonoff
    Anton Strogonoff over 4 years

    How to animate a vector path like it's being drawn, progressively? In other words, slowly show the path pixel by pixel.

    I'm using Raphaël.js, but if your answer is not library specific—like maybe there's some general programming pattern for doing that kind of thing (I'm fairly new to vector animation)—it's welcome!


    It's easy to do with straight paths, as easy as an example on that page::

    path("M114 253").animate({path: "M114 253 L 234 253"});
    

    But try to change code on that page, say, this way::

    path("M114 26").animate({path: "M114 26 C 24 23 234 253 234 253"});
    

    And you'll see what I mean. Path is certainly animated from it initial state (point "M114 26") to the end state (curve "C 24 23 234 253 234 253" starting on point "M114 26"), but not in a way specified in question, not like it's being drawn.

    I don't see how animateAlong can do that. It can animate an object along a path, but how can I make this path to gradually show itself while object is being animated along it?


    The solution?

    (Via peteorpeter's answer.)

    Seems like currently the best way to do it is via 'fake' dashes using raw SVG. For the explanation see this demo or this document, page 4.

    How produce progressive drawing?

    We have to use stroke-dasharray and stroke-dashoffset and know length of curve to draw. This code draw nothing on screen for circle, ellipse, polyline, polygone or path:

    <[element] style="stroke-dasharray:[curve_length],[curve_length]; stroke-dashoffset:[curve_length]"/>
    

    If in animate element stroke-dashoffset decrease to 0, we get progressive drawing of curve.

    <circle cx="200" cy="200" r="115"
        style="fill:none; stroke:blue; stroke-dasharray:723,723; stroke-dashoffset:723">
        <animate begin="0" attributeName="stroke-dashoffset"
            from="723" to="0" dur="5s" fill="freeze"/>
    </circle>
    

    If you know a better way, please leave an answer.


    Update (26 Apr. 2012): Found an example that illustrates the idea well, see Animated Bézier Curves.