Can't make paths draw growing slowly with D3

2019-01-10 16:21发布

问题:

Using the d3 graphics library, I can't seem to make paths draw slowly so they can be seen growing.

This site has a perfect example in the "Line Chart (Unrolling)" section, but no code is given for that section. Could someone please help me with the lines of D3 code that could make that happen?

When I try appending delay() or duration() such as in the following code snippet, the path still draws immediately, And all the SVG code after this segment fails to render.

    var mpath = svg.append ('path');
        mpath.attr ('d', 'M35 48 L22 48 L22 35 L22 22 L35 22 L35 35 L48 35 L48 48')
             .attr ('fill', 'none')
             .attr ('stroke', 'blue')
             .duration (1000);

回答1:

I believe the "D3 way" to do this is with a custom tween function. You can see a working implementation here: http://jsfiddle.net/nrabinowitz/XytnD/

This assumes that you have a generator called line set up with d3.svg.line to calculate the path:

// add element and transition in
var path = svg.append('path')
    .attr('class', 'line')
    .attr('d', line(data[0]))
  .transition()
    .duration(1000)
    .attrTween('d', pathTween);

function pathTween() {
    var interpolate = d3.scale.quantile()
            .domain([0,1])
            .range(d3.range(1, data.length + 1));
    return function(t) {
        return line(data.slice(0, interpolate(t)));
    };
}​

The pathTween function here returns an interpolator that takes a given slice of the line, defined by how far we are through the transition, and updates the path accordingly.

It's worth noting, though, that I suspect you'd get better performance and a smoother animation by taking the easy route: put a white rectangle (if your background is simple) or a clipPath (if your background is complex) over the line, and transition it over to the right to reveal the line underneath.



回答2:

A common pattern when animating lines in svg is setting a stroke-dasharray of the length of the path and then animate stroke-dashoffset:

var totalLength = path.node().getTotalLength();

path
  .attr("stroke-dasharray", totalLength + " " + totalLength)
  .attr("stroke-dashoffset", totalLength)
  .transition()
    .duration(2000)
    .ease("linear")
    .attr("stroke-dashoffset", 0);

You can see a demo here: http://bl.ocks.org/4063326



回答3:

Based on the post that you link to, I came up with the following example:

var i = 0,
    svg = d3.select("#main");

String.prototype.repeat = function(times) {
   return (new Array(times + 1)).join(this);
}

segments = [{x:35, y: 48}, {x: 22, y: 48}, {x: 22, y: 35}, {x: 34, y:35}, {x: 34, y:60}];
line = "M"+segments[0].x + " " + segments[0].y

new_line = line + (" L" + segments[0].x + " " + segments[0].y).repeat(segments.length);
 var mpath = svg.append ('path').attr ('d',new_line )
             .attr ('fill', 'none')
             .attr ('stroke', 'blue')

for (i=0; i<segments.length; i++)
    {
    new_segment = " " + "L"+segments[i].x + " " + segments[i].y
    new_line = line + new_segment.repeat(segments.length-i)
    mpath.transition().attr('d',new_line).duration(1000).delay(i*1000);
    line = line + new_segment

    }

It is a bit ugly, but works. You can see it on jsFiddle