如何逐步绘制矢量路径?(Raphael.js)

31

如何实现矢量路径的逐步绘制动画效果?换句话说,就是逐像素缓慢显示路径。

我正在使用Raphaël.js但是如果您的答案不特定于某个库——比如可能有一些通用的编程模式来完成这种事情(我对矢量动画还比较新)——那么它也是可以接受的!


对于直线路径,这很容易实现,例如在该页面的示例中所示:

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

但是试着修改那个页面上的代码,比如这样:

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

你会明白我的意思的。路径从其初始状态(点“M114 26”)动画到结束状态(以点“M114 26”为起点,曲线“C 24 23 234 253 234 253”开始的曲线),但不是问题中指定的方式,也不像正在被绘制。

我不知道animateAlong怎么做到这一点。它可以沿着路径动画一个对象,但我怎样才能使这条路径在对象沿着它移动时逐渐显示出来呢?


解决方案?

(通过peteorpeter的答案获得。)

目前似乎最好的方法是使用原始SVG中的“虚假”破折号来实现。有关说明,请参见此演示文稿此文档,第4页。

 

如何进行渐进式绘图?

   

我们必须使用stroke-dasharraystroke-dashoffset,并知道要绘制的曲线的长度。   对于圆形、椭圆形、折线、多边形或路径,此代码不会在屏幕上绘制任何内容:

<[element] style="stroke-dasharray:[curve_length],[curve_length]; stroke-dashoffset:[curve_length]"/>
如果在动画元素中,stroke-dashoffset 减少到 0,我们就会得到曲线的逐步绘制效果。
<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>
如果您知道更好的方法,请留下答案。
更新(2012年4月26日):发现一个很好地说明这个想法的例子,请参见动画贝塞尔曲线
11个回答

27

也许有人正在寻找答案,就像我已经两天了:

// 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);
    }
});

我遇到的问题通过使用RaphaelJS方法就得以解决。

以下是在评论中所要求的jsFiddle示例:http://jsfiddle.net/eA8bj/


是的 - 这实际上就是我在回答中所说的“绘制路径,使其不可见,将其分成多个子路径,并逐个显示这些子路径”,但感谢您用代码进行了解释。 =)我并不太喜欢这种解决方案,因为它并不完全平滑,并且可能需要大量资源。 - Anton Strogonoff
在我的情况下,它非常流畅。但我还没有在不同的浏览器中进行过太多测试...结合一些 jQuery 中的缓动方程,它非常强大(对于我的单路径...)。 - davidenke
干得好!我在寻找一个解决方案,多亏了你,我只用了一个小时! - Tosh
如果有人能把这个放到jsfiddle上就太好了,因为我很难让它工作,我承认我对Raphael还很陌生。 - cwd
Raphael的解决方案得到了重大的认可。 - Chris Wilson
2
我稍微修改了你的jsFiddle,使其仅限于单个raphael路径元素:http://jsfiddle.net/eA8bj/63/ - bspoel

17

哇,看着这些演示...我想目前这是最好的方法!我现在有困难理解所有那些破折号的操作,所以我还需要自己尝试一下。此外,第一个链接的网站看起来像是一个关于SVG的很棒的资源。附言:Raphael领域不仅友好,而且可以生成在IE6上运行(虽然性能较差)的图形。有时候这也很重要,可悲啊。 - Anton Strogonoff
1
看起来SVGweb可能支持这个SVG功能,这意味着通过Flash可以获得更好的IE支持和性能:http://code.google.com/p/svgweb/source/browse/trunk/src/org/svgweb/nodes/SVGAnimateNode.as?r=1181 - peteorpeter

11

我想提供一种Raphael+JS-only的替代方案,这是我在自己的工作中广泛使用的。它与davidenke的解决方案相比有几个优点:

  1. 不会在每个周期清除纸张,使得动画路径可以与其他元素很好地共存;
  2. 重复使用单个路径,使用Raphael自己的渐进式动画,使动画更加平滑;
  3. 资源占用明显较少。

以下是这种方法(很容易改装为扩展程序):

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;
}

这是我网站上使用该插件的两个示例:一个用于路径变换,另一个用于渐进式字母书写


感谢您在答案末尾提供的两个演示,它们非常有帮助。 - Tommi
这是一个不错的解决方案,Kevin。在这里需要一些建议,当逐渐绘制线条时,即使我已经将strokelinecap和strokelinejoin设置为round,最后绘制的线条仍然是butt Cap。如果我尝试在数组的最后一个索引中使用z来关闭线条,则线条的最后一个点与线条的起始点相连。请告诉我是否有任何解决方案。 - PCA

7

哇,太棒了。谢谢你分享! - Anton Strogonoff
欢迎 @AntonStrogonoff ;) 如果您认为它有助于他人并且可以推广它,请随意 答案 ;) - abernier
是的,我认为它至少现在值得被选为一个现成的解决方案。 - Anton Strogonoff

6
使用 "pathLength" 属性,我们可以为路径设置虚拟长度。从此,我们可以在 "stroke-dasharray" 中使用这个虚拟长度。因此,如果我们将 "pathLength" 设置为 100 单位,那么我们就可以将 "stroke-dasharray" 设置为 "50,50",这将恰好是路径的 50%,50%!
但是,这种方法存在一个问题:唯一支持该属性的浏览器是 Opera 11。 这里 是一个平滑曲线绘制动画的示例,无需 javascript 或硬编码长度。(只在 Opera 11 中正常工作)

支持pathLength现在已经在Firefox中(自v6起)。Chrome尚未支持。 - Doug Miller

3
Anton和Peteorpeter的解决方案在路径变得复杂时很遗憾在Chrome中崩溃了。它对于链接演示中的公交地图来说是可以的。查看我创建的这个动画“花瓣”jsfiddle,它在FF10和Safari5中绘制正确,但在Chrome中闪烁不止:http://jsfiddle.net/VjMvz/(这全部都是HTML和内联SVG,没有JavaScript。)我仍在寻找一个非Flash的解决方案。AnimateAlong显然不能满足我的需求。Raphael.js可能会起作用,尽管它很快就会威胁到变成回调面条。Davidenke,你能发布一个带有你的解决方案的工作jsfiddle吗?我只是无法让它工作。在Chrome 18中,使用你的“.hide”设置为“display:none”的节点没有“getTotalLength”的方法,我正在收到错误。

感谢提供演示,可以确认闪烁问题。看起来这个问题仍然没有解决。 (但是,虽然您的帖子很有帮助,我应该指出它实际上不是答案,更像是对@davidenke的纠正和问题,所以也许您应该将其作为评论发布到他的答案中。) - Anton Strogonoff
我当时是个彻头彻尾的新手,无法发表评论。很抱歉!现在Webkit中已经解决了闪烁问题。(我在发现问题时进行了反馈。)请参见:http://trac.webkit.org/changeset/116030 。Anton的解决方案现在完全可行。问题解决了! - Ben
只是一条注释:在我的旧 MacBook 上,Chrome 44.0.2403.157(64 位)中的此 jsfiddle 中没有闪烁。 - Dave Everitt

2
很遗憾,正如您所认同的那样,您可能无法在Raphael中优雅地完成此操作。然而,如果某种幸运降临,您不需要为此特定功能支持IE浏览器,您可以放弃Raphael API并直接操纵SVG。然后,也许您可以设置一个掩模沿着路径移动,并以自然的速度显示线条。您可以在IE中优雅地退化,只需使用Raphael显示路径,而不进行动画。

那似乎非常接近了...感谢你的建议和链接。我会仔细阅读规格说明,看看在没有Raphael的情况下还能做些什么。⁋ 但是遮罩,如果我选择使用它,那么它将是一个覆盖整个路径的大矩形,然后在沿着路径前进时逐渐显示出剩余部分,对吗?但是如果路径会转向(想象一下迷宫),那么遮罩将再次开始隐藏,并且这样做就没有意义了。或者我理解错了什么吗? - Anton Strogonoff
不,你提出了一个关于遮罩场景的好观点。即使忽略重叠部分,在一个简单的封闭路径上,比如一个圆形,你也会遇到麻烦,因为遮罩覆盖了已经显示的内容...这个解决方案可能行不通了 :( - peteorpeter

1

我刚好在做这件事。我尝试的第一件事是Anton的解决方案,但性能很差。

最后,获得我想要的结果最简单的方法是使用动画函数的替代“关键帧”语法。

将最终路径隐形绘制,然后使用循环中的getSubpath生成一堆关键帧。

创建一个新的可见路径,等于第一个关键帧。

然后执行以下操作:

path.anmimate({ keyFrameObject, timeframe });

您不需要为要绘制的每个像素都设置关键帧。调整参数后,我发现每个关键帧100px的值适用于我尝试“绘制”的复杂性/大小。


1

看起来它完成了这件事!我会简要解释一下它似乎是如何工作的,尽管如果您在答案中包含它会更好。无论如何:您将SVG(必须没有填充和封闭路径)拖放到现场转换器中,它会给您返回一些JS代码。您将该代码与jQuery、Raphaёl和Lazy Line Painter库一起包含在页面的JS中。在使用该JS代码初始化Lazy Line Painter之后,您只需调用一个jQuery方法即可开始绘制路径。 - Anton Strogonoff

0

我对animateAlong有一个想法,但不知道它如何实现。它可以沿着路径动画一个对象,但是当对象沿着路径被动画时,如何使这条路径逐渐显示出来呢? - Anton Strogonoff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接