D3多个SVG对象遵循路径

时间:2017-02-05 08:43:36

标签: javascript d3.js

我是D3的新手,我想制作一条线,沿着所述线运行多个均匀间隔的箭头,以指示电路的流动。

这基本上就是我想要的(在无限循环中沿着线条动画的箭头) (链接评论,声誉不够高)

我发现了一个动画图像的好例子,它遵循正确旋转的路径。 http://bl.ocks.org/KoGor/8163268

我的问题是我不知道如何将所有额外的箭头放在我的路径上。我考虑将我的路径分成一组端到端的许多相等长度的路径并同时为它们设置动画,但这似乎比它需要的更复杂。

知道我该怎么办?

以下是我目前的情况:https://jsfiddle.net/singerbradley/wcfg2mec/16/

代码

var points = [
  [480, 200],
  [580, 400],
  [680, 100],
  [780, 300],
  [180, 300],
  [280, 100],
  [380, 400]
];

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

var path = svg.append("path")
  .data([points])
  .attr("d", d3.svg.line()
    .tension(1) // Catmull–Rom
    .interpolate("linear")); //basis-open

var arrow = svg.append("polygon")
  .attr("points", "0,24, 15,12, 0,0") // x,y points
	.attr("transform", "translate(" + points[3] + ")");

transition();

function transition() {
  arrow.transition()
    .duration(10000)
    .ease("linear")
    .attrTween("transform", translateAlong(path.node()))
    .each("end", transition); //infinite loop
}

// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
  var l = path.getTotalLength();
  var t0 = 0;
  return function(d, i, a) {
    return function(t) {
    	var p0 = path.getPointAtLength(t0 * l); //previous point
      var p = path.getPointAtLength(t * l); //current point
      var angle = Math.atan2(p.y - p0.y, p.x - p0.x) * 180 / Math.PI;//angle for tangent
      t0 = t;
      //Shifting center to center of arrow
      // xoffset and yoffset should be half the original width and height
      var xoffset = 12, yoffset = 12;
      var centerX = p.x - xoffset;
      var centerY = p.y - yoffset;
      return "translate(" + centerX + "," + centerY + ")rotate(" + angle + " " + xoffset + " " + yoffset + ")";
    };
  };
}
path {
  fill: none;
  stroke: #000;
  stroke-width: 1px;
}

polygon {
  fill: steelblue;
  stroke: #fff;
  stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

d3.js

1 个答案:

答案 0 :(得分:1)

有几种方法可以做到这一点。这是我的解决方案。

首先,如果你想要10个箭头,让我们用10个元素创建数据数组:

var arrowData = d3.range(10);

并相应地附加箭头:

var arrow = svg.selectAll(".arrow")
    .data(arrowData)
    .enter()
    .append("polygon")
    .attr("points", "0,24, 15,12, 0,0");

然后,每隔一秒,我们会使用带有transition()的IIFE向setTimeout拨打另一个箭头:

(function loop() {
    if (counter++ > 8) return;
    setTimeout(function() {
        var thisPolygon = d3.selectAll("polygon").filter(function(d, i) {
            return i == counter;
        });
        transition(thisPolygon);
        loop()
    }, 1000)
}());

为此,我们稍微修改transition函数:

function transition(elem) {
    elem.transition()
        .duration(10000)
        .ease("linear")
        .attrTween("transform", translateAlong(path.node()))
        .each("end", function() {
            return transition(elem)
        }); 
}

这是您更新的小提琴:https://jsfiddle.net/3o7vzvfa/。这是另一个,有50个箭头:https://jsfiddle.net/buLjg7d3/

这里是Stack片段中的相同代码:

var points = [
    [480, 200],
    [580, 400],
    [680, 100],
    [780, 300],
    [180, 300],
    [280, 100],
    [380, 400]
];

var arrowData = d3.range(10);

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

var path = svg.append("path")
    .data([points])
    .attr("d", d3.svg.line()
        .tension(1) // Catmull–Rom
        .interpolate("linear")); //basis-open

var arrow = svg.selectAll(".arrow")
    .data(arrowData)
    .enter()
    .append("polygon")
    .attr("points", "0,24, 15,12, 0,0");

var counter = -1;

(function loop() {
    if (counter++ > 8) return;
    setTimeout(function() {
        var thisPolygon = d3.selectAll("polygon").filter(function(d, i) {
            return i == counter;
        });
        transition(thisPolygon);
        loop()
    }, 1000)
}())

function transition(elem) {
    elem.transition()
        .duration(10000)
        .ease("linear")
        .attrTween("transform", translateAlong(path.node()))
        .each("end", function() {
            return transition(elem)
        }); //infinite loop
}

// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
    var l = path.getTotalLength();
    var t0 = 0;
    return function(d, i, a) {
        return function(t) {
            var p0 = path.getPointAtLength(t0 * l); //previous point
            var p = path.getPointAtLength(t * l); //current point
            var angle = Math.atan2(p.y - p0.y, p.x - p0.x) * 180 / Math.PI; //angle for tangent
            t0 = t;
            //Shifting center to center of arrow
            // xoffset and yoffset should be half the original width and height
            var xoffset = 12,
                yoffset = 12;
            var centerX = p.x - xoffset;
            var centerY = p.y - yoffset;
            return "translate(" + centerX + "," + centerY + ")rotate(" + angle + " " + xoffset + " " + yoffset + ")";
        };
    };
}
path {
  fill: none;
  stroke: #000;
  stroke-width: 1px;
}

polygon {
  fill: steelblue;
  stroke: #fff;
  stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>