Repeat transition of dashed line with d3.js

I’ve added code for two potential solutions below. One gives a more moving motion, the other looks more like the line is growing in place.

const width = 300,
  height = 50;
const dashArray = [8, 5];
const dashArrayLength = d3.sum(dashArray);
let pathLength;
const svg = d3.select("svg")
  .attr("viewBox", [0, 0, width, height]);

svg.append('path')
  .attr('d', d3.line()([
    [100, height / 4],
    [200, height / 4]
  ]))
  .attr('stroke', 'black')
  .attr('fill', 'none')
  .attr("stroke-dasharray", function() {
    pathLength = this.getTotalLength();

    // Find how often `dashArrayLength` fits within `pathLength`
    const repeat = Math.ceil(pathLength / dashArrayLength);
    const array = (dashArray.join(" ") + " ").repeat(repeat);

    // Prepend pathLength to give it a complete empty space beforehand
    return "0 " + pathLength + " " + array;
  })
  .attr("stroke-dashoffset", "0")
  .transition()
  .on("start", function repeat() {
    d3.active(this)
      .transition()
      .duration(4000)
      .attr("stroke-dashoffset", -pathLength)
      .transition()
      .duration(1000)
      .attr("stroke-dashoffset", "0")
      .on("start", repeat);
  });

svg.append('path')
  .attr('d', d3.line()([
    [100, height / 2],
    [200, height / 2]
  ]))
  .attr('stroke', 'black')
  .attr('fill', 'none')
  .attr("stroke-dasharray", function() {
    pathLength = this.getTotalLength();

    // Prepend pathLength to give it a complete empty space beforehand
    return "0 " + pathLength;
  })
  .transition()
  .on("start", function repeat() {
    d3.active(this)
      .transition()
      .duration(4000)
      .attrTween("stroke-dasharray", function() {
        return function(t) {
          const tweenPathLength = pathLength * t;

          // Find how often `dashArrayLength` fits within `tweenPathLength`,
          // this time rounding down
          const repeat = Math.floor(tweenPathLength / dashArrayLength);
          const array = (dashArray.join(" ") + " ").repeat(repeat);

          // See how much space there is still left over to draw a final,
          // maybe partial, stripe
          const finalStripe = Math.min(
            tweenPathLength - repeat * dashArrayLength,
            dashArray[0]
          );

          // Finally, append a large empty space at the end
          return array + [finalStripe, pathLength];
        }
      })
      .transition()
      .duration(1000)
      .attr("stroke-dasharray", "0 " + pathLength)
      .on("start", repeat);
  });

svg.append("text")
  .attr("x", 30)
  .attr("y", height / 4)
  .attr("font-size", "0.4em")
  .text("Moving effect")

svg.append("text")
  .attr("x", 30)
  .attr("y", height / 2)
  .attr("font-size", "0.4em")
  .text("Growing effect")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top