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

发布于 2024-10-10 14:45:20 字数 1873 浏览 8 评论 0 原文

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

我正在使用 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-dasharray和Stroke-dashoffset并知道要绘制的曲线的长度。 此代码在屏幕上不绘制任何圆、椭圆、折线、多边形或路径:

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

如果在动画元素中Stroke-dashoffset减小到0,我们就会得到曲线的渐进式绘制。

<圆 cx="200" cy="200" r="115"
    样式=“填充:无;笔画:蓝色;笔画-dasharray:723,723;笔划-dashoffset:723”>
    <动画开始=“0”属性名称=“笔划-dashoffset”
        从=“723”到=“0”dur=“5s”填充=“冻结”/>

如果您知道更好的方法,请留下答案。


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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

风向决定发型 2024-10-17 14:45:20

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

// 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/

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/

眸中客 2024-10-17 14:45:20

尤里卡! (也许 - 假设您愿意走出 Raphael 的友好领域,进入纯粹的 SVG 领域……)

您可以使用 SVG keyTimeskeySplines

这是一个工作示例:

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

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

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

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

冷清清 2024-10-17 14:45:20

我想提供另一种仅 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转换,另一个用于渐进式字体

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.

多孤肩上扛 2024-10-17 14:45:20

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

只需在一堆 SVG 上实例化它即可:

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

--

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

尽情享受;)

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

偷得浮生 2024-10-17 14:45:20

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

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

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

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)

糖粟与秋泊 2024-10-17 14:45:20

安东&遗憾的是,当路径变得复杂时,Peteorpeter 的解决方案在 Chrome 中就会崩溃。对于该链接演示中的公交车地图来说,这很好。看看我创建的这个动画“花瓣”jsfiddle,它在 FF10 和 Safari5 中正确绘制,但在 Chrome 中无法控制地闪烁:

http: //jsfiddle.net/VjMvz/

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

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

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

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:

http://jsfiddle.net/VjMvz/

(This is all HTML and inline SVG, no 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.

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'.

耳钉梦 2024-10-17 14:45:20

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

但是,如果%deity% 您不需要支持 IE 来实现此特定功能,您可以放弃 Raphael API 和 直接操作 SVG。然后,也许您可​​以装备一个面罩沿着路径骑行,以自然的速度展现线条。

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

Unfortunately, as you seem to agree, you probably can't do this elegantly in 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 mask to ride along the path and reveal the line at a natural pace.

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

jJeQQOZ5 2024-10-17 14:45:20

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

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

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

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

然后执行类似的操作:

path.anmimate({ keyFrameObject, timeframe });

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

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

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

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

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

then do something like:

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"

烈酒灼喉 2024-10-17 14:45:20

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

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

人间☆小暴躁 2024-10-17 14:45:20

您尝试过拉斐尔的 animateAlong 吗?您可以在演示页面上看到它的实际效果。

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

年华零落成诗 2024-10-17 14:45:20

好吧,这是我对此的想法……解决方案离理想还很远。

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

唯一的方法是以某种方式将路径转换为多个点并一一显示它们。

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

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

Alright, here's my thoughts on this… The solution's too far from ideal.

To gradually show the path mean we should show it, like, dot by dot. And vector paths consist not of dots, but of curves, so it appears to me there's no ‘natural’ way to gradually ‘draw’ the path in vector graphics. (Though I'm fairly new to this and may be mistaken.)

The only way would be to somehow convert a path to a number of dots and show them one by one.

Currently my workaround is to draw a path, make it invisible, break it into a number of subpaths, and show that subpaths one by one.

This isn't hard to do with Raphael, but it's not elegant either, and quite slow on a large paths. Not accepting my answer, hoping there's a better way…

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文