|
<!DOCTYPE html> |
|
<svg width="960" height="500"> |
|
<path transform="translate(180,150)scale(2,2)" fill="none" stroke="black" stroke-width="1.5"></path> |
|
</svg> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
|
|
var d0 = "M0,0c100,0 0,100 100,100c100,0 0,-100 100,-100", |
|
d1 = "M0,0c100,0 0,100 100,100c100,0 0,-100 100,-100c100,0 0,100 100,100"; |
|
|
|
d3.select("path") |
|
.attr("d", d0) |
|
.transition() |
|
.duration(2000) |
|
.on("start", function repeat() { |
|
d3.active(this) |
|
.attrTween("d", pathTween(d1, 4)) |
|
.transition() |
|
.attrTween("d", pathTween(d0, 4)) |
|
.transition() |
|
.on("start", repeat); |
|
}); |
|
|
|
function pathTween(d1, precision) { |
|
return function() { |
|
var path0 = this, |
|
path1 = path0.cloneNode(), |
|
n0 = path0.getTotalLength(), |
|
n1 = (path1.setAttribute("d", d1), path1).getTotalLength(); |
|
|
|
// Uniform sampling of distance based on specified precision. |
|
var distances = [0], i = 0, dt = precision / Math.max(n0, n1); |
|
while ((i += dt) < 1) distances.push(i); |
|
distances.push(1); |
|
|
|
// Compute point-interpolators at each distance. |
|
var points = distances.map(function(t) { |
|
var p0 = path0.getPointAtLength(t * n0), |
|
p1 = path1.getPointAtLength(t * n1); |
|
return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]); |
|
}); |
|
|
|
return function(t) { |
|
return t < 1 ? "M" + points.map(function(p) { return p(t); }).join("L") : d1; |
|
}; |
|
}; |
|
} |
|
|
|
</script> |