使用 DOM 对象跟踪路径 [英] Trace path with DOM object

查看:17
本文介绍了使用 DOM 对象跟踪路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 javascript 和 d3js 的新手.我想要一个 DOM 对象来追踪由参数化曲线 (x(t),y(t)) 指定的路径.以下是此类参数化的示例:

I'm new to javascript and d3js. I would like a DOM object to trace out a path specified by a parametrized curve (x(t),y(t)). Here is an example of such a parametrization:

var theta = [];
        for(var i = 0; i <= N; i++){
        theta.push(2*Math.PI*i/N);
    }
var points = [];
    for(var i = 0; i <= N; i++){
        points.push([Math.cos(theta[i]),Math.sin(theta[i])]);
    }

以上是曲线的参数化——在这种情况下,也是一个圆——我希望我的 DOM 对象遵循这条曲线的轨迹.[旁白:有没有更好的方法来定义points?运行 for 循环似乎很荒谬.]

The above is the parametrization of a curve -- in this case, also a circle -- and I would like my DOM object to follow the trajectory of this curve. [Aside: is there any better way to define points? It seems ridiculous to run a for loop.]

实现我正在寻找的那种效果的粗略方法是在 d3 的 update() 部分运行 for 循环.首先,我只是在 svg 变量后附加一个圆圈,这样它就不需要链接到任何数据.然后无需输入/退出即可选择和更新它.

A crude way to achieve the sort of effect I'm looking for is to run a for loop in the update() part of d3. First, I simply append a circle to the svg variable, so that it need not be linked to any data. It is then selected and updated without required enter/exit.

        for (var i = 0; i <= N; i++){
        svg.selectAll("circle")
                .transition()
                .attr("cx",points[i][0]+w/2) // w: width
                .attr("cy",points[i][1]+h/2) // h: height
                .duration(dt) // 
                .delay(dt*i);
            }

[旁白:我听说 queue() 会更好,而不是计算总延迟.评论?] 然而,过渡的缓动属性使它以不稳定的方式运行.我想我可以指定没有缓动,但我确信必须有更好的方法来实现我想要的,这只是让初始 DOM 对象(圆)沿着特定轨迹平滑移动.

[Aside: I've heard queue() would be better, as opposed to calculating the total delay. Comments?] However, the easing property of the transition makes it run in a choppy fashion. I imagine I could specify no easing, but I'm sure there must be a better way to achieve what I want, which is simply for the initial DOM object (the circle) to move smoothly along a specific trajectory.

最后,我想为最终将链接到数据的多个 DOM 对象执行此操作,每个对象都有一条特定的曲线要遵循.关于我如何做到这一点的任何提示?

In the end, I would want to do this for multiple DOM objects which will eventually be linked to data, each with a specific curve to follow. Any tips on how I would do this?

在此先感谢您的帮助,我很乐意接受任何建议,包括参考资料.

Thanks in advance for any help, and I will gladly take any advice, including references.

推荐答案

有趣但不太实用的方法

SVG 规范实际上有许多动画选项,包括沿路径移动对象的能力.路径以与 元素相同的形式定义,因此您可以使用 d3.svg.arc 函数来创建路径.

Interesting but not terribly practical approach

The SVG spec actually has a number of animation options, including the ability to move an object along a path. The path is defined in the same form as for a <path> element, so you could use the d3.svg.arc functions to create the path.

一旦定义了路径,就很容易使用 d3 来添加动画:
http://fiddle.jshell.net/RnNsE/1/
尽管您需要阅读 SVG 动画元素和属性.

Once you have a path defined, it is easy to use d3 to add in the animation:
http://fiddle.jshell.net/RnNsE/1/
although you'll want to read up on SVG animation elements and attributes.

然而,这个精彩的动画有一个限制:浏览器支持不佳.因此,如果这是用于网站,您将需要使用 d3 和 Javascript 制作动画.

However, there is a limitation to this wonderful animation: poor browser support. So if this is for a website, you're going to need to do the animation with d3 and Javascript.

让 d3 为您创建流畅动画的关键是使用自定义tween"函数 在过渡上.

The key to getting d3 to create smooth animations for you is to use a custom "tween" function on a transition.

当您进行转换时,d3 会为每个元素的每次更改初始化一个补间函数,并启动计时器函数以触发更新.在计时器的每个滴答"处,d3 调用适当的tween"函数,并提供有关它沿过渡多远的信息.因此,如果刻度发生在 2000 毫秒过渡中的 500 毫秒,则补间函数将给出值 0.25(假设是线性缓动函数,其他缓动函数会使经过的时间与沿过渡的预期距离"之间的关系复杂化).

When you do a transition, d3 initializes a tween function for each change on each element, and starts up timer functions to trigger the updates. At each "tick" of the timer, d3 calls the appropriate "tween" function with the information about how far along the transition it is. So if the tick occurs 500ms into a 2000ms transition, the tween function will given the value 0.25 (assuming a linear easing function, other easing functions complicate the relationship between time elapsed and the expected "distance" along the transition).

现在,对于大多数更改,补间函数相当简单,d3 会自动为您找出一个.如果将cx"值从 100 更改为 200,那么当过渡值为 25% 时,补间函数将返回 125,当过渡值为 50% 时返回 150,依此类推.如果将填充"值从红色更改为黄色,它将计算这些颜色的数值并在它们之间进行转换.

Now, for most changes the tween function is fairly straightforward, and d3 will figure one out for you automatically. If you change a "cx" value from 100 to 200, then the tween function is going to return 125 when the transition value 25%, 150 when the transition is 50%, and so on. If you change a "fill" value from red to yellow, it will calculate the numerical values of those colours and convert between them.

tween 函数在每个刻度处返回的值然后用于更新元素的属性或样式.由于更新每秒发生多次,因此通常会产生平滑的动画.以改变圆的cx"值的简单例子,圆从起点到终点沿直线移动.

The value returned by the tween function at each tick is then used to update the attribute or style of the element. Since the updates happen many times a second, it usually results in a smooth animation. For the simple example of changing the "cx" value of a circle, the circle moves in a straight line from the starting point to the end point.

但是您不希望它沿直线移动.您希望它沿圆圈移动(或沿着您选择的任何路径).因此,您将需要创建一个自定义函数,该函数告诉圆圈它应该在过渡的 25% 的位置,以及过渡的 50% 的位置,依此类推.

But you don't want it to move in a straight line. You want it to move in a circle (or along any path you choose). So you're going to need to create a custom function that tells the circle where it should be 25% of the way through the transition, and where it should be 50% through the transition, and so on.

如果您担心必须自己解决这个问题,请不要害怕.像这样,所以 D3 非常出色,Mike Bostock 为您完成了艰苦的工作.但即使是他也不必做艰苦艰苦的工作.他的方法对 SVG 路径使用了两个内置的 Javascript 函数,getTotalLength()getPointAtLength().第一个告诉您路径的总长度,第二个为您提供距路径起点一定距离的点的坐标.

And if you're worried you have to figure that out on your own, never fear. Like so, so much with D3, Mike Bostock has done the hard work for you. But even he didn't have to do the hard hard work. His approach uses two built-in Javascript functions for SVG paths, getTotalLength() and getPointAtLength(). The first tells you the total length of the path, the second gives you the coordinates of the point a certain distance from the start of the path.

有了这两个值,如果你想在路径上的某个百分比处,很容易找出你应该在的坐标:在 25% 处,你想在 path.getPointAtLength(0.25*path.getTotalLength()).

With those two values, it is straightforward to figure out the coordinates you should be at if you want to be a certain percent of the way along the path: at 25%, you want to be at path.getPointAtLength(0.25*path.getTotalLength() ).

这是迈克实现这一目标的功能:

Here's Mike's function that makes that happen:

// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
  var l = path.getTotalLength();

  return function(d, i, a) {
    return function(t) {
      var p = path.getPointAtLength(t * l);
      return "translate(" + p.x + "," + p.y + ")";
    };
  };
}

有点混乱,不是吗?一个函数返回一个函数,一个函数返回一个函数.

A little confusing, no? A function that returns a function that returns a function.

那是因为当您为过渡指定tween"时,您实际上必须指定一个tween factory"——该函数将为您选择的每个元素返回一个适当的tween函数.

That's because when you specify a "tween" for a transition, what you actually have to specify is a "tween factory" -- the function that will return an appropriate tween function for each element in your selection.

现在,在他的示例中,他只有一条路径和一个沿其移动的对象,因此不会使用这些额外的层.但在一般情况下,您的补间工厂函数将采用参数 d(选择中该元素的数据对象)、i(该元素的索引)和a(您要更改的属性或样式的初始值).使用这些值,您必须返回自定义 tween 函数,该函数采用转换状态值 t(0 或 1 之间的数字,或者对于某些缓动函数可能会超过 1)并计算属性值在转换中的那个状态.

Now, in his example he only has one path and one object moving along it, so those extra layers don't get used. But in the general case, your tween factory function would take the arguments d (the data object for that element in the selection), i (the index of that element) and a (the initial value of the attribute or style that you're changing). With those values, you have to return the custom tween function which take a transition state value t (a number between 0 or 1, or possibly a bit beyond 1 for certain easing functions) and computes the attribute value at that state in the transition.

你会注意到这个函数返回一个翻译指令.与使用 cxcy 相比,这通常是一种更容易移动对象的方法,因为您可以在一个转换属性调用中指定水平和垂直移动,所以你只需要一个 tween 函数就可以做到这两者.

You'll note that this function returns a translation instruction. That's generally going to be an easier way to move an object around, compared to using cx and cy, since you can specify both horizontal and vertical movement in one transform attribute call, so you only need the one tween function to do both.

这是我上面的示例,更新为使用 d3 补间沿着路径移动圆圈:
http://fiddle.jshell.net/RnNsE/2/

Here's my example from above, updated to use a d3 tween to move the circles along the path:
http://fiddle.jshell.net/RnNsE/2/

关键代码:

circles.transition().ease("linear")
    .duration(5000)
    .delay(function(d,i){return i*5000;})
    .attrTween("transform", createPathTween);

//creates a tween function to translate an element
//along the path that is a sibling to the element
function createPathTween(d, i, a) {
  var path = this.parentNode.getElementsByTagName("path")[0];
  //i.e., go from this <circle> -> parent <g> -> array of child <path> elements 
               -> first (and only) element in that array

  var l = path.getTotalLength();

  return function(t) {
      var p = path.getPointAtLength(t * l);
      return "translate(" + p.x + "," + p.y + ")";
    };    
}

我的版本从 Mike 的版本中去掉了最外层的嵌套函数,但它添加了一些 Javascript 来为每个圆元素找到正确的 元素.

My version strips out the outermost layer of nested functions from Mike's version, but it adds in a bit of Javascript to find the correct <path> element for each circle element.

请注意,为了使用 getTotalLength()getPointAtLength() 函数,您需要一个 SVG 路径元素;但是,如果您不希望该路径显示在屏幕上,则该路径可以不可见(CSS 中的 fill:none;stroke:none;).同样,虽然我的路径定义是硬编码的,但您可以使用 d3 的 arcline 生成器为您构建它.

Note that you need an SVG path element in order to use the getTotalLength() and getPointAtLength() functions; however, this path can be invisible (fill:none; stroke:none; in CSS) if you don't want it to show on the screen. And again, while my path definitions are hard-coded, you could use one of d3's arc or line generators to construct it for you.

只是为了好玩,这是我的示例,使用不同的缓动函数:
http://fiddle.jshell.net/RnNsE/3/
请注意,我没有更改关于补间函数的任何内容——所有更改的是随着过渡的进行 d3 传递给该函数的 t 值.

And just for fun, here's my example with a different easing function:
http://fiddle.jshell.net/RnNsE/3/
Note that I didn't change anything about the tweening function -- all that's changed is the t values that d3 passes in to that function as the transition progresses.

附言这是关于 d3 自定义补间函数的另一个很好的资源:http://blog.safaribooksonline.com/2013/07/11/reusable-d3-js-using-attrtween-transitions-and-mv/

P.S. Here's another good resource on d3 custom tween functions: http://blog.safaribooksonline.com/2013/07/11/reusable-d3-js-using-attrtween-transitions-and-mv/

这篇关于使用 DOM 对象跟踪路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆