forked from mbostock's block: Point-Along-Path Interpolation
xxxxxxxxxx
<html>
<meta charset="utf-8">
<head>
<style>
path {
fill: lightblue;
stroke: steelblue;
stroke-width: 3px;
stroke-linecap: round;
}
circle {
stroke: darkgoldenrod;
stroke-dasharray: 1 1;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v5.js"></script>
<script>
// DATA + VARS
var points = [
[900, 440],
[480, 220],
[ 60, 440],
[120, 40],
[180, 420],
[240, 80],
[300, 360],
[360, 120],
[420, 300],
[480, 160],
[540, 300],
[600, 120],
[660, 360],
[720, 80],
[780, 420],
[840, 40]
];
var line = d3.line().curve(d3.curveCardinalClosed.tension(0))
var origDash = "4 8 16 8";
// duration time
var t = 12000;
// SVG
var svg = d3.select("body").append("svg")
.datum(points)
.attr("width", 960)
.attr("height", 500);
// append radial color gradient to new svg <defs> element
var radialGradient = svg.append("defs").append("radialGradient")
.attr("id", "radial-gradient")
.attr("cx", "50%")
.attr("cy", "50%")
.attr("r", "75%")
// define color scales
var numColors = 9,
gradientScale = d3.scaleLinear()
.domain([0,(numColors-1)/2,numColors-1])
.range(["lightyellow","goldenrod","darkgoldenrod"])
// bind specific color stops to radialGradient
radialGradient.selectAll("stop")
.data(d3.range(numColors))
.enter().append("stop")
.attr("offset", function(d,i) { return i/(numColors-1)*50 + 40 + "%"; })
.attr("stop-color", d => { return gradientScale(d) });
// APPEND LINE/PATH
// faint underlying
svg.append("path")
.style("stroke", "#ddd")
.style("stroke-dasharray", origDash)
.attr("d", line);
var path = svg.append("path")
.style("stroke-dasharray", origDash)
.attr("d", line)
// APPEND CIRCLES/POINTS
// little guys
svg.selectAll(".point")
.data(points)
.enter().append("circle")
.attr("r", 4)
.style("fill","url(#radial-gradient)")
.attr("transform", function(d) { return "translate(" + d + ")"; });
// big guy (in motion)
var point = svg.append("circle")
.attr("r", 14)
.style("fill","url(#radial-gradient)")
.style("stroke-width", "4")
.attr("transform", "translate(" + points[0] + ")")
var l = path.node().getTotalLength();
// FUNCTIONS
function goPoint() {
point.transition()
.duration(t)
.attrTween("transform", translateAlong(path.node()))
.on("start", goPath) // point transition triggers path
}
function goPath() {
path.transition()
.duration(t)
.styleTween("stroke-dasharray", tweenDash)
.transition()
.duration(t/4)
.styleTween("stroke-dasharray", dashBack)
.on("end", goPoint)
}
// functions returning interpolated stroke-dasharray value
function tweenDash() {
var i = d3.interpolateString("0," + l, l + "," + l);
return function(t) { return i(t); };
}
function dashBack() {
var i = d3.interpolateString(l + ",0", "0," + l);
return function(t) { return i(t); };
}
// function returns value for translating point along path
function translateAlong(path) {
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
}
}
}
// INITIATE ANIMATION
goPoint()
</script>
</body>
</html>
https://d3js.org/d3.v5.js