Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3: Smoothly animate a hand drawn line?

Please see this pen for a working but janky version of what I want: a d3 animation that simulates, in real time, the path of a line being draw by a person.

I have two issues with my current implementation:

  1. It's choppy. You can see each piece of the path appearing as a chunk, rather than looking as if it was the path of a pen being moved across the screen. One solution is to make each chunk smaller -- is there any other solution?

  2. It's recursive. Recursively calling draw with setTimeout() feels antithetical to the d3 spirit. Is there a more declarative solution? Perhaps one using transition()?

Javascript

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


var lineData = d3.range(5,800,15).map(function(x) {
  return {x: x, y: 10 + Math.floor(Math.random()*6)-3}
});

var lineFunction = d3.svg.line()
                         .x(function(d) { return d.x; })
                         .y(function(d) { return d.y; })
                         .interpolate("basis");

function draw(points) {

  var lineGraph = svg.append("path")                            
                     .attr("stroke", "blue")
                     .attr("stroke-width", 1)
                     .attr("fill", "none")
                     .attr("d", lineFunction(points));
  
  if (points.length < lineData.length)
    setTimeout(function(){
      draw(lineData.slice(0,points.length+1));
    }, 50);
}

draw([]);
                        
like image 220
Jonah Avatar asked Mar 21 '23 06:03

Jonah


1 Answers

See this simple (and neat) trick for gradually animating the drawing of a path, using stroke-dasharray. Here's your code, modified.

var lineGraph = svg
  .append("path")
  .attr("stroke", "blue")
  .attr("stroke-width", 1)
  .attr("fill", "none")
  .attr("d", lineFunction(lineData))
  .transition()
  .duration(5000)
  .attrTween("stroke-dasharray", tweenDash)

function tweenDash() {
  var l = this.getTotalLength(),
      i = d3.interpolateString("0," + l, l + "," + l);
  return function(t) { return i(t); };
}
like image 98
meetamit Avatar answered Mar 29 '23 05:03

meetamit