Can't make paths draw growing slowly with D3

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);

Answers:

Answer

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.

Answer

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

Answer

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

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.