【问题标题】:Trace path with DOM object使用 DOM 对象跟踪路径
【发布时间】:2014-02-09 04:21:15
【问题描述】:

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

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 循环似乎很荒谬。]

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

        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 对象(圆圈)沿着特定的轨迹平滑移动。

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

提前感谢您的帮助,我很乐意接受任何建议,包括参考资料。

【问题讨论】:

    标签: javascript dom svg d3.js


    【解决方案1】:

    有趣但不太实用的方法

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

    一旦定义了路径,就可以很容易地使用 d3 在动画中添加:
    http://fiddle.jshell.net/RnNsE/1/
    虽然你会想阅读SVG animation elements and attributes

    但是,这个精彩的动画有一个限制:poor browser support。因此,如果这是针对网站的,您将需要使用 d3 和 Javascript 来制作动画。

    生产就绪方法

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

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

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

    tween 函数在每个刻度处返回的值随后用于更新元素的属性或样式。由于更新每秒发生多次,因此通常会产生流畅的动画。以改变圆的“cx”值为例,圆从起点到终点直线运动。

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

    如果您担心必须自己解决,请不要害怕。就像这样,so 和 D3 一样,Mike Bostock has done the hard work for you。但即使是他也不必做艰苦的艰苦的工作。他的方法为 SVG 路径使用了两个内置的 Javascript 函数,getTotalLength()getPointAtLength()。第一个告诉您路径的总长度,第二个为您提供距离路径起点一定距离的点的坐标。

    有了这两个值,如果您想在路径上达到一定百分比,就可以很容易地计算出您应该在的坐标:在 25% 时,您想在path.getPointAtLength(0.25*path.getTotalLength() )

    下面是 Mike 实现这一目标的函数:

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

    有点混乱,不是吗?函数返回函数返回函数。

    这是因为当您为过渡指定“补间”时,您实际上必须指定的是“补间工厂”——该函数将为您选择的每个元素返回适当的补间函数。

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

    你会注意到这个函数返回一个翻译指令。与使用cxcy 相比,这通常是移动对象的一种更简单的方法,因为您可以在一个变换属性调用中同时指定水平和垂直移动,因此您只需要一个补间函数即可两者都有。

    这是我上面的示例,已更新为使用 d3 补间沿路径移动圆圈:
    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 来为每个圆形元素找到正确的 &lt;path&gt; 元素。

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

    只是为了好玩,这是我的示例,带有不同的 easing function:
    http://fiddle.jshell.net/RnNsE/3/
    请注意,我没有更改补间函数的任何内容——所有更改的是 d3 在过渡过程中传递给该函数的 t 值。

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

    【讨论】:

    • 这太棒了,为文章 +1。我知道写这些东西需要时间,你至少节省了我的 2-3 ,所以非常感谢。我需要一些时间来解析这个并玩弄。我最终想要做的是模拟具有 n 条路径(n“钟摆”)的钟摆效果(例如 youtube.com/watch?v=yVkdfJ9PkRQ),每条路径都有不同的补间函数——理想情况下,就像 sin(c_n * x) 一样移动,其中 c_n 是一个取决于 n 的常数。有意义吗?
    • 编辑到上面(澄清):*moving like sin(c_i * x), i = 1, ..., n.
    • 听起来不错。如果你的钟摆摆动是真正的椭圆弧(我不记得它是否是这样工作的——物理课是很久以前的事了!),你应该能够准确地定义它们。不适用于d3.svg.arc,它只做圆,不做椭圆,但有一个自定义函数来创建SVG arc path。如果您的模型中有其他因素导致路径不是完美的椭圆,那么您计算点的方法应该有效,然后使用适当的曲线插值器将这些点传递给d3.svg.line()
    猜你喜欢
    • 1970-01-01
    • 2020-04-07
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    相关资源
    最近更新 更多