【问题标题】:How to draw a vector path progressively? (Raphael.js)如何逐步绘制矢量路径? (拉斐尔.js)
【发布时间】:2011-06-05 14:03:25
【问题描述】:

如何将矢量路径设置为动画,就像它正在被绘制一样,渐进式的?换句话说,逐个像素地慢慢显示路径。

我正在使用Raphaël.js但是如果你的答案不是特定于库的——比如可能有一些通用的编程模式可以做这种事情(我对矢量动画还很陌生) ——不客气!


使用直线路径很容易,就像 that page:: 上的示例一样简单。

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”)到结束状态(曲线“C 24 23 234 253 234 253”从点“M114 26”开始)动画,但不是以问题中指定的方式,不像它正在绘制中。

我不明白animateAlong 是如何做到这一点的。它可以沿路径为对象设置动画,但是如何使该路径在对象沿其设置动画时逐渐显示出来?


解决办法?

(通过peteorpeter's answer。)

似乎目前最好的方法是通过使用原始 SVG 的“假”破折号。解释见this demothis document,第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 日):找到一个很好地说明这个想法的例子,请参阅 Animated Bézier Curves

【问题讨论】:

    标签: javascript animation vector svg bezier


    【解决方案1】:

    我为此创建了一个脚本:Scribble.js,基于这个伟大的dasharray/dashoffset technique

    只需在一堆 SVG &lt;path&gt;s 上实例化它:

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

    --

    注意:完整的USAGE 代码:https://gist.github.com/abernier/e082a201b0865de1a41f#file-index-html-L31

    享受;)

    【讨论】:

    • 哇,太棒了。感谢分享!
    • 欢迎@AntonStrogonoff ;) 如果您认为它可以帮助他人并推广它,请随时 回答;)
    • 是的,我认为至少现在应该选择它,作为现成的解决方案。
    【解决方案2】:

    只是一个更新,你可以试试Lazy Line Painter

    【讨论】:

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

    也许有人正在寻找答案,就像我这两天一样:

    // 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 方法。

    这是 cmets 中要求的 jsFiddle 示例,http://jsfiddle.net/eA8bj/

    【讨论】:

    • 是的——这实际上是我在回答中“绘制路径,使其不可见,将其分解为多个子路径,并逐个显示子路径”的意思,但感谢您解释代码。 =) 我不太喜欢这个解决方案,因为它不是很流畅,而且可能需要资源。
    • 在我的情况下非常顺利。但我还没有在不同的浏览器中测试过那么多......结合jquery中的一些缓动方程,它非常强大(对于我的单一路径......)。
    • 干得好!正在寻找解决方案,多亏了你,我只花了一个小时!
    • 如果有人能把它放在 jsfiddle 中会很好,因为我很难让它工作,我承认我是 Raphael 的新手。
    • 我已经对您的 jsFiddle 进行了一些调整,使其与单个 raphael 路径元素隔离:http://jsfiddle.net/eA8bj/63/
    【解决方案4】:

    我想提供一种替代方案,仅限 Raphael+JS 的解决方案,我在自己的工作中大量使用了该解决方案。与 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;
    }
    

    这里有两个在我的网站上的使用示例:一个用于Path Transformation,另一个用于Progressive Lettering

    【讨论】:

    • 感谢后面的两个演示,帮助很大。
    • 这是一个很好的解决方案凯文。这里需要一个建议,当逐步绘制线条时,最后绘制的线条在对接帽中,即使我已经将 strokelinecap 和 strokelinejoin 指定为圆形。如果您对此有任何解决方案,请告诉我。如果我尝试用数组中最后一个索引中的 z 来关闭该行,则该行的最后一个点与该行的起点连接
    【解决方案5】:

    当路径变得复杂时,Anton & Peteorpeter 的解决方案在 Chrome 中令人遗憾地崩溃了。该链接演示中的巴士地图很好。看看我创建的这个动画“花瓣”jsfiddle,它在 FF10 和 Safari5 中正确绘制,但在 Chrome 中无法控制地闪烁:

    http://jsfiddle.net/VjMvz/

    (这是所有 HTML 和内联 SVG,没有 javascript。)

    我仍在为此寻找非 Flash 解决方案。 AnimateAlong 显然不会因为我正在做的事情而削减它。 Raphael.js 可以工作,尽管它可能会很快变成回调意大利面条。

    Davidenke,你能用你的解决方案发布一个有效的 jsfiddle 吗?我只是无法让它工作。我在 Chrome 18 中收到一个错误,即使用“.hide”设置为“显示:无”的节点没有方法“getTotalLength”。

    【讨论】:

    • 感谢演示,可以确认闪烁。似乎这个问题仍然没有解决。 (但是,虽然您的帖子很有帮助,但我应该注意这实际上并不是一个答案,更像是对@davidenke 的更正和问题,所以也许您应该将其作为对他的回答的评论发布。)
    • 我是个新手,当时无法发表评论。对不起!现在在 Webkit 中解决了闪烁问题。 (当我找到它时,我发现了它。)请参阅:trac.webkit.org/changeset/116030。 Anton 的解决方案现在运行良好。走远了!
    • 请注意:在我的旧 MacBook 上的 Chrome 44.0.2403.157(64 位)中,这个 jsfiddle 没有闪烁。
    【解决方案6】:

    我就是这样做的。我尝试的第一件事是 Anton 的解决方案,但性能很糟糕。

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

    不可见地绘制最终路径,然后循环使用getSubpath生成一大堆关键帧。

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

    然后执行以下操作:

    path.anmimate({ keyFrameObject, timeframe });

    您不应该为要绘制的每个像素都设置一个关键帧。在玩弄了这些参数之后,我发现每个关键帧 100px 的值适用于我试图“绘制”的复杂性/大小

    【讨论】:

      【解决方案7】:

      使用“pathLength”属性我们可以设置路径的虚拟长度。从那时起,我们可以在“stroke-dasharray”中使用这个虚拟长度。 因此,如果我们将“pathLength”设置为 100 个单位,我们就可以将“stroke-dasharray”设置为“50,50”,这恰好是路径的 50%、50%!

      这种方法存在一个问题:支持该属性的唯一浏览器是 Opera 11。

      Here 是没有 javascript 或硬编码长度的平滑曲线绘制动画示例。(仅在 Opera 11 中正常工作)

      【讨论】:

      • 现在在 FF (as of v6) 中支持 pathLength。 Chrome 还没有。
      【解决方案8】:

      尤里卡! (也许——假设你愿意走出拉斐尔的友好领域进入纯 SVG 领域......)

      您可以使用 SVG keyTimeskeySplines

      这是一个工作示例:

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

      ...这里有一些可能有用的解释:

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

      【讨论】:

      • 哇,看看演示...我想目前这是最好的方法!我现在很难理解所有用破折号进行的操作,所以我自己还没有尝试过。此外,第一个链接中的站点看起来像是 SVG 上的一个很好的资源。 PS。 Raphael 的领域不仅友好,而且可以生成在 IE6 上运行的图形(尽管性能很差)。可悲的是,这有时很重要。
      • 看起来 SVGweb 可能支持这个 SVG 功能,这意味着通过 flash 获得更好的 IE 支持和性能:code.google.com/p/svgweb/source/browse/trunk/src/org/svgweb/…
      【解决方案9】:

      不幸的是,正如您似乎同意的那样,您可能无法在 Raphael 中优雅地做到这一点。

      然而,如果通过%deity% 的某些笔触,您不需要支持 IE 来实现此特定功能,您可以放弃 Raphael API 和manipulate the SVG directly。然后,也许,你可以安装一个mask 沿着小路骑行,并以自然的速度显示线路。

      您可以在 IE 中优雅地降级以使用 Raphael 简单地显示路径,而无需动画。

      【讨论】:

      • 这似乎非常接近......感谢您的建议和链接。将更仔细地阅读规范,以了解在没有 Raphael 的情况下我还能用 SVG 做什么。 ⁋ 但是面具,说我去。所以它会是一个大的矩形,最初覆盖整个路径,然后,在前进的过程中,揭示后面剩下的东西,对吧?但是如果路径会转向(想象某种迷宫),那么面具无论如何都会再次开始隐藏,这将没有意义。还是我误会了什么?
      • 不,你提出了一个关于面具场景的好观点。把重叠放在一边,即使是在一个简单的封闭路径上,比如一个圆圈,你会遇到麻烦,因为面具掩盖了已经显示的东西......这个解决方案可能已经死在水中了:(
      【解决方案10】:

      好的,这是我的想法……解决方案离理想太远了。

      逐渐显示路径意味着我们应该逐点显示它。而且矢量路径不是由点组成,而是由曲线组成,所以在我看来,没有“自然”的方式可以在矢量图形中逐渐“绘制”路径。 (虽然我对此很陌生,可能会弄错。)

      唯一的方法是将路径转换为多个点并一个一个地显示。

      目前我的解决方法是绘制一条路径,使其不可见,将其分解为多个子路径,并逐个显示子路径。

      这对 Raphael 来说并不难,但它也不优雅,而且在大路径上相当慢。不接受我的回答,希望有更好的方法……

      【讨论】:

        【解决方案11】:

        你试过拉斐尔的animateAlong吗?你可以在on a demo page看到它。

        【讨论】:

        • 我想到了animateAlong,但不知道它是如何做到的。它可以沿路径为对象设置动画,但是如何使该路径在对象沿其设置动画时逐渐显示出来?
        猜你喜欢
        • 2011-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-20
        • 2013-10-21
        • 2011-08-16
        • 1970-01-01
        相关资源
        最近更新 更多