George Mauer
George Mauer

Reputation: 122052

Draw a path out gradually from the middle

Suppose I have an arbitrary path drawn with d3

var points = [0, 4, 4, 4, 8, 8, 4, 4, 4, 8, 4, 4, 4, 4, 0];
var svg = d3.select('svg');

var line = d3.svg.line()
            .y(function(d) { return 10*d})
            .x(function(d, t) { return t*20 })
            .interpolate('cubic');

svg.append('path')
  .attr('d', line(points))
  .attr('stroke', 'black')
  .attr('stroke-width', 2)
  .attr('fill', 'none')
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"></script>
<svg style="width: 100%; height: 100%; outline: 1px solid green;"></svg>

I want to animate the path drawing out slowly. I understand how to do it from beginning to end but in my case I want to start at an arbitrary point on the line and draw outward at the same rate in both direction. I can't quite figure out how to do this.

I can utilize attrTween and take slices of the array varying by time, but if I do that, the line shifts since I don't know how to set the horizontal offset correctly at each step.

Upvotes: 3

Views: 936

Answers (1)

meetamit
meetamit

Reputation: 25157

Hopefully you're working with the second answer on the page you linked to (the one by @duopixel), because that's a much better way. If so, then you can make the line start drawing from the middle if you slightly modify the attributes:

var points = [0, 4, 4, 4, 8, 8, 4, 4, 4, 8, 4, 4, 4, 4, 0];
var svg = d3.select('svg');

var line = d3.svg.line()
            .y(function(d) { return 10*d})
            .x(function(d, t) { return t*20 })
            .interpolate('cubic');

var path = svg.append('path')
  .attr('d', line(points))
  .attr('stroke', 'black')
  .attr('stroke-width', 2)
  .attr('fill', 'none');

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

path
 .attr("stroke-dasharray", '0 ' + totalLength)
 .attr("stroke-dashoffset", totalLength/2)
 .transition()
   .duration(2000)
   .attr("stroke-dasharray", totalLength + ' 0')
   .attr("stroke-dashoffset", totalLength);
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"></script>
<svg style="width: 100%; height: 100%; outline: 1px solid green;"></svg>

How it works:

First, consider the original, common scenario of animating the drawing of a path from its start to its end. If l is the length of the path (obtained by path.node().getTotalLength()) then setting the path's stroke-dasharray to l + ' ' + l makes the dash and the gap between it and the next dash both equal to l. Under these conditions, the path would appear solid, because the dash length being l "pushes" the gap past the end of the path. By setting stroke-dashoffset to l, the above scenario is reversed — the path is entirely a gap (hence invisible) and the dash falls off the path. By transitioning between these 2 states (from one with offset to one without offset), we get a gradual drawing of a line.

Now, to animate from the middle, the dash is defined with length 0 and the gap has a length l. So now the line is also completely invisible, due to the 0-length dash and the l-length gap . Setting stroke-dashoffset to l/2 moves this 0-length dash halfway down the path. That's the animation starting state. The end state is the reverse dasharray where gap is 0 and dash-length is l, which renders the line completely solid. To properly transition between these states, we also need to set the end state's stroke-dashoffset to l. That was something I figured out experimentally, and I'm not sure exactly how to explain it in words. But it works....

Upvotes: 7

Related Questions