简体   繁体   中英

D3 Multiple SVG Objects Following Path

I am very new to D3, and I wanted to make a line with multiple, evenly spaced arrows running along said line to indicate the flow of a circuit.

This is basically what I'm going for (with the arrows animated along the line in an infinite loop) (link in comment, not high enough reputation)

I found this great example of an image animated to follow a path with correct rotation. http://bl.ocks.org/KoGor/8163268

My problem is that I don't know how to place all the extra arrows on to my path. I considered breaking up my path into many equal length paths in a group end to end and animating them all at once, but that seemed more complicated than it needed to be.

Any idea how I should proceed?

Here is what I have so far: https://jsfiddle.net/singerbradley/wcfg2mec/16/

Code

 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(py - p0.y, px - 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 = px - xoffset; var centerY = py - 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

There are several ways for doing this. Here is my solution.

First, if you want 10 arrows, let's create the data array with 10 elements:

var arrowData = d3.range(10);

And append the arrows accordingly:

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

Then, at every second, we'll call transition() for a different arrow, using an IIFE with a 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)
}());

For this to work, we slightly modify the transition function:

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

Here is your updated fiddle: https://jsfiddle.net/3o7vzvfa/ . Here is another one, with 50 arrows: https://jsfiddle.net/buLjg7d3/

And here the same code in a Stack snippet:

 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(py - p0.y, px - 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 = px - xoffset; var centerY = py - 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM