随着D3.js连续路径插值 [英] Interpolating along consecutive paths with D3.js

查看:165
本文介绍了随着D3.js连续路径插值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我适应沿着轨迹插补迈克·博斯托克的模型接受的 N 的个别路径和插补沿每个连续的。作为比较新的到D3​​低于code,据我已经得到了展示,这是运行两个路径的指向插补同时的。现在我有点卡住了如何重组这使流程连续的(只有一个移动物体)。我真的需要能够路径之间停下来听一个鼠标点击,但我可以弄清楚,code,一旦结构是存在的。最感激的帮助。

这里的的jsfiddle

code为后人:

 <!DOCTYPE HTML>
  <间的charset =UTF-8>
  <身体GT;
  <风格>  路径{
    补:无;
    行程:#000;
    笔画宽度:的3px;
  }  圈{
    行程:#FFF;
    笔画宽度:的3px;
  }  < /风格>
  <脚本类型=文/ JavaScript的SRC =htt​​p://d3js.org/d3.v3.js>< / SCRIPT><脚本>VAR pathdata = [
   [240,100],
    [290,200]
    [340,50]]
   [340,50],
    [90,150]
    [140,50],
    [190,200]]
];  VAR SVG = d3.select(身体)。追加(SVG)
      .attr(宽度,960)
      .attr(高度,500);  VAR路径= svg.selectAll(路径)
      。数据(pathdata)
      。输入()
      .append(路径)
      .attr(D,d3.svg.line())
      .attr(ID,功能(D,I){返回路径+ I});  //情节路径顶点
  svg.selectAll(点)
      。数据([]。concat.apply([],pathdata))
      。进入()。追加(圆)
      .attr(R,5)
      .attr(补,红)
      .attr(改造,功能(D){返回翻译(+ D +);});  //沿PATH0插值
  VAR圈= svg.append(圆)
      .attr(R,10)
      .attr(补,钢青)
      .attr(转换,翻译(+ pathdata [0] [1] +))
      。过渡()
      .duration(4000)
      .attrTween(改造,translateAlong(d3.select(#PATH0)[0] [0]));  //沿着插PATH1
  VAR圈= svg.append(圆)
      .attr(R,10)
      .attr(补,钢青)
      .attr(转换,翻译(+ pathdata [1] [1] +))
      。过渡()
      .duration(4000)
      .attrTween(改造,translateAlong(d3.select(#PATH1)[0] [0]));  功能translateAlong(路径){
    的console.log(路径);
    变种L = path.getTotalLength();
    返回功能(D,I,A){
      复位功能(T){
        VAR P = path.getPointAtLength(T * L);
        返回翻译(+ p.x +,+ p.y +);
      };
    };
  }  < / SCRIPT>  < /身体GT;
  < / HTML>

我也想知道,如果它可能是更好的输入数据大意如下的一个格式?

  //为路径标识第三场
VAR points_alt1 = [
  [240,100,0],
  [290,200,0],
  [340,50,0],
  [340,50,1],
  [90,150,1],
  [140,50,1],
  [190,200,1]
]

或..

  //插值终点第3场
VAR points_alt2 = [
  [240,100,0],
  [290,200,0],
  [340,50,1],
  [340,50,0],
  [90,150,0],
  [140,50,0],
  [190,200,1]
]


解决方案

创建一个函数,如沿PARAMS要动画的路径选择D3和路径的整数指数(选择范围内)。此功能找到合适的路径选择中,启动沿线圆的过渡,并订阅过渡的结束事件,此时它触发下一个动画。

这里的工作小提琴

 函数animateSinglePath(选择,indexOfAnimated){
    indexOfAnimated = indexOfAnimated || 0;    //创建圈子,如果不存在
    //(通过结合单元素阵列ac​​hived)
    圈= svg.selectAll('动画圈)。数据([空])
    circle.enter()
        .append(圆)
        .attr(类,动画圈)
        .attr(R,10)
        .attr(补,钢青)    selection.each(功能(D,I){
        //找到我们想要的动画路径
        如果(我== indexOfAnimated){
            //在此背景下,这对象是在DOM
            //其索引的路径的元素,我等于
            //所需indexOfAnimated
            PATH = d3.select(本)
            //对于风格,使之破灭
            path.attr('冲程dasharray','5,5')            //将圈来启动POS机,开始动画
            的console.log('开始的动画',我)
            圈
                .attr(转换,翻译(+ D [0] +))
                。过渡()
                .duration(2000)
                .attrTween(改造,translateAlong(path.node()))
                。每个('端',函数(){
                    的console.log('结束的动画',我);                    //对于风格,恢复中风非虚
                    path.attr('冲程dasharray','')                    //通过调用这个触发下一个动画
                    再次//功能
                    animateSinglePath(选择,indexOfAnimated + 1);
                });
        }
    });
}

P.S

你提出我不会重组数据,因为你需要有不同的SVG <路径> 元素 - 一个序列中的每个章节。有每个路径的独特数组,你现在要做的,就是让你来创建这些<路径> 虏通过数据()绑定。

根据你想达到什么样的,你甚至会想进一步鸟巢每个路径数组,对象包装它 {} ,保存有关元数据路径:

  VAR pathData = [
  {
    名称:跨越涅曼
    分:[[240,100],[290,200],[340,50]]
  },
  {
    名称:三月维尔纽斯
    分:[[340,50],[90,150],[140,50],[190,200]]
  },
  {
    名字:三月莫斯科,
    分:[[190,200],[70,180],[30,30],[350,160]]
  }
];

I'm adapting Mike Bostock's point along path interpolation model to accept an array of n individual paths and interpolate along each consecutively. Being relatively new to D3 the code below shows as far as I've got, which is to run the point interpolation for both paths concurrently. Now I'm a bit stuck over how to restructure this to make the process consecutive (with just one moving object). Really I need to be able to stop between paths to listen for a mouseclick, but I can figure out that code once the structure is there. Most grateful for assistance.

Here's the jsfiddle.

Code for posterity:

  <!DOCTYPE html>
  <meta charset="utf-8">
  <body>
  <style>

  path {
    fill: none;
    stroke: #000;
    stroke-width: 3px;
  }

  circle {
    stroke: #fff;
    stroke-width: 3px;
  }

  </style>
  <script type="text/javascript" src="http://d3js.org/d3.v3.js"></script><script>

var pathdata = [
   [[240, 100],
    [290, 200],
    [340, 50]],
   [[340, 50],
    [90, 150],
    [140, 50],
    [190, 200]]
];

  var svg = d3.select("body").append("svg")
      .attr("width", 960)
      .attr("height", 500);

  var paths = svg.selectAll("path")
      .data(pathdata)
      .enter()
      .append("path")
      .attr("d", d3.svg.line())
      .attr("id",function(d, i) { return "path" + i });

  // plot path vertices
  svg.selectAll(".point")
      .data([].concat.apply([], pathdata))
      .enter().append("circle")
      .attr("r", 5)
      .attr("fill", "red")
      .attr("transform", function(d) { return "translate(" + d + ")"; });

  // interpolate along path0
  var circle = svg.append("circle")
      .attr("r", 10)
      .attr("fill", "steelblue")
      .attr("transform", "translate(" + pathdata[0][1] + ")")
      .transition()
      .duration(4000)
      .attrTween("transform", translateAlong(d3.select("#path0")[0][0]));

  // interpolate along path1
  var circle = svg.append("circle")
      .attr("r", 10)
      .attr("fill", "steelblue")
      .attr("transform", "translate(" + pathdata[1][1] + ")")
      .transition()
      .duration(4000)
      .attrTween("transform", translateAlong(d3.select("#path1")[0][0]));

  function translateAlong(path) {
    console.log(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 + ")";
      };
    };
  }

  </script>

  </body>
  </html>

I'm also wondering if it might be better to format the input data along one of the following lines?

// 3rd field for path id
var points_alt1 = [
  [240, 100, 0],
  [290, 200, 0],
  [340, 50,  0],
  [340, 50,  1],
  [90, 150,  1],
  [140, 50,  1],
  [190, 200, 1]
]    

or..

// 3rd field for interpolation end-points
var points_alt2 = [
  [240, 100, 0],
  [290, 200, 0],
  [340, 50,  1],
  [340, 50,  0],
  [90, 150,  0],
  [140, 50,  0],
  [190, 200, 1]
]

解决方案

Create a function that takes as params a d3 selection of paths and an integer index of the path (within the selection) along which you want to animate. This function finds the appropriate path within that selection, starts up a transition of a circle along it, and subscribes to the 'end' event of the transition, at which point it triggers the next animation.

Here's the working fiddle

function animateSinglePath(selection, indexOfAnimated) {
    indexOfAnimated = indexOfAnimated || 0;

    // Create circle if doesn't already exist
    // (achived by binding to single element array)
    circle = svg.selectAll('.animated-circle').data([null])
    circle.enter()
        .append("circle")
        .attr("class", "animated-circle")
        .attr("r", 10)
        .attr("fill", "steelblue")    

    selection.each(function(d, i) {
        // find the path we want to animate
        if(i == indexOfAnimated) {
            // In this context, the "this" object is the DOM
            // element of the path whose index, i equals the
            // desired indexOfAnimated
            path = d3.select(this)
            // For style, make it dashed
            path.attr('stroke-dasharray', '5, 5')

            // Move circle to start pos and begin animation
            console.log('Start animation', i)
            circle
                .attr("transform", "translate(" + d[0] + ")")
                .transition()
                .duration(2000)
                .attrTween("transform", translateAlong(path.node()))
                .each('end', function() {
                    console.log('End animation', i);

                    // For style, revert stroke to non-dashed
                    path.attr('stroke-dasharray', '')

                    // trigger the next animation by calling this
                    // function again
                    animateSinglePath(selection, indexOfAnimated + 1);
                });
        }
    });
}

P.S

I wouldn't restructure the data as you proposed, because you need to have distinct SVG <path> elements – one for each "chapter" in the sequence. Having a distinct array for each of path, as you do now, is what enables you to create these <path>s via data() binding.

Depending on what you're trying to achieve, you may even want to further nest each path array, wrapping it with an object {}, to hold meta data about the path:

var pathData = [
  {
    name: "Crossing the Niemen",
    points: [ [240, 100], [290, 200], [340, 50] ]
  },
  {
    name: "March on Vilnius",
    points: [ [340, 50], [90, 150], [140, 50], [190, 200] ]
  },
  {
    name: "March on Moscow",
    points: [ [190, 200], [70, 180], [30, 30], [350, 160] ]
  }
];

这篇关于随着D3.js连续路径插值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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