Javascript 如何逐步绘制矢量路径?(拉斐尔.js)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4631019/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 13:21:10  来源:igfitidea点击:

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

javascriptanimationvectorsvgbezier

提问by Anton Strogonoff

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, butif 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!

我正在使用Rapha?l.js如果您的答案不是特定于库的——比如可能有一些通用的编程模式来做这种事情(我对矢量动画相当陌生)——欢迎!



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.

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

I don't see how animateAlongcan 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?

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



The solution?

解决方案?

(Via peteorpeter's answer.)

(通过peteorpeter 的回答。)

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

似乎目前最好的方法是通过使用原始 SVG 的“假”破折号。有关说明,请参阅此演示此文档,第 4 页。

How produce progressive drawing?

We have to use stroke-dasharrayand stroke-dashoffsetand 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>

如何制作渐进式绘图?

我们必须使用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>

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.

更新(2012 年 4 月 26 日):找到一个很好地说明这个想法的例子,请参阅动画贝塞尔曲线

采纳答案by abernier

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

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

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

只需在一堆 SVG 上实例化它<path>

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

--

——

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

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

Enjoy ;)

享受 ;)

回答by davidenke

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.

这对我有用,只有使用 RaphaelJS 方法。

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

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

回答by peteorpeter

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

尤里卡!(也许 - 假设你很舒服地走出拉斐尔的友好领域进入纯 SVG 领域......)

You can use SVG keyTimesand keySplines.

您可以使用 SVG keyTimeskeySplines

Here's a working example:

这是一个工作示例:

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

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

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

回答by Kevin Nielsen

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:

我想提供一个替代的、仅限 Raphael+JS 的解决方案,我在自己的工作中大量使用了该解决方案。与 davidenke 的解决方案相比,它有几个优点:

  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.
  1. 不会在每个循环中清除纸张,允许动画路径与其他元素很好地共存;
  2. 使用 Raphael 自己的渐进动画重复使用单个路径,使动画更流畅;
  3. 资源密集程度大大降低。

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.

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

回答by Brazhnyk Yuriy

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!

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

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

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

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

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

回答by Ben

Anton & Peteorpeter's solution sadly breaks down in Chrome when paths get complicated. It's fine for the bus map in that linked demo. Check out this animated "flower petals" jsfiddle I created, which draws correctly in FF10 and Safari5, but flickers uncontrollably in Chrome:

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

http://jsfiddle.net/VjMvz/

http://jsfiddle.net/VjMvz/

(This is all HTML and inline SVG, no javascript.)

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

I'm still looking for a non-Flash solution for this. AnimateAlong obviously won't cut it for what I'm doing. Raphael.js could work, though it threatens to turn into callback spaghetti really fast.

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

Davidenke, can you post a working jsfiddle with your solution? I just can't get it to work. I'm getting an error in Chrome 18 that nodes that are set to "display: none" with your ".hide" have no method 'getTotalLength'.

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

回答by peteorpeter

Unfortunately, as you seem to agree, you probably can't do this elegantly in Raphael.

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

However, if, by some stroke of %deity%you don't need to support IE for this particular feature, you could forgo the Raphael API and manipulate the SVG directly. Then, perhaps, you could rig a maskto ride along the path and reveal the line at a natural pace.

但是,如果%deity%您在某些方面不需要为这个特定功能支持 IE,您可以放弃 Raphael API 并直接操作 SVG。然后,也许,您可以戴上面具沿着路径骑行并以自然的速度显露线条。

You could degrade gracefully in IE to simply show the path using Raphael, without animation.

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

回答by HymanSD

Just an update to this, you could try Lazy Line Painter

只是对此的更新,您可以尝试Lazy Line Painter

回答by David Meister

i was just doing exactly this. The first thing i tried was Anton's solution but the performance sucks.

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

In the end the easiest way to get the result i wanted was to use the alternative "keyframe" syntax for the animate function.

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

draw the final path invisibly, then generate a whole bunch of key frames by using getSubpath in a loop.

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

create a new path that is visible and equals the first keyframe.

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

then do something like:

然后做类似的事情:

path.anmimate({ keyFrameObject, timeframe });

path.anmimate({ keyFrameObject, timeframe });

you shouldn't need a keyframe for every pixel that you want to draw. After playing around with the parameters, i found that a value of 100px per keyframe worked for the complexity/size of what i was trying to "draw"

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

回答by user123444555621

Have you tried Raphael's animateAlong? You can see it in action on a demo page.

你试过拉斐尔的animateAlong吗?您可以在演示页面上看到它的运行情况。