JSFiddle - React, Tailwind, and code Playground

by Ibrahim Tanyalcin

HTML

<script src="//d3js.org/d3.v3.min.js"></script>

CSS

svg  {
	width:400px;
	height:300px;
}
path {
  fill: none;
  stroke: #000;
  stroke-width: 3px;
}

circle {
  fill: steelblue;
  stroke: #fff;
  stroke-width: 3px;
}

JavaScript

var points = [
  [480, 200],
  [580, 400],
  [680, 100],
  [780, 300],
  [180, 300],
  [280, 100],
  [380, 400]
];

var svg = d3.select("body").append("svg").attr("viewBox","0 0 960 500").attr("preserveAspectRatio","none");

var path = svg.append("path")
    .data([points])
    .attr("d", d3.svg.line()
    .tension(0) // Catmull–Rom
    .interpolate("cardinal-closed"));

svg.selectAll(".point")
    .data(points)
  .enter().append("circle")
    .attr("r", 4)
    .attr("transform", function(d) { return "translate(" + d + ")"; });

var circle = svg.append("circle")
    .attr("r", 13)
    .attr("transform", "translate(" + points[0] + ")");

transition();

function transition() {
  circle.transition()
      .duration(10000)
      .tween("transform", translateAlong(path.node()))
      .each("end", transition);
}

// Returns an attrTween for translating along the specified path element.

function translateAlong(path){
	var points  = path.__points || collectPoints(path);
	return function (d,i){
		var transformObj = this.transform.baseVal[0];
		return function(t){
			var index = t * 1000 | 0,
				point = points[index];
			transformObj.setTranslate(point[0],point[1]);
		}
	}
}
function collectPoints(path) {
	var l = path.getTotalLength(),
		step = l*1e-3,
		points = [];
	for(var i = 0,p;i<=1000;++i){
		p = path.getPointAtLength(i*step);
		points.push([p.x,p.y]);
	}
	return path.__points = points;
}