Skip to content

Instantly share code, notes, and snippets.

@magjac
Last active August 3, 2017 22:55
Show Gist options
  • Save magjac/79e67c1d2f13b830594f5aef46e04a34 to your computer and use it in GitHub Desktop.
Save magjac/79e67c1d2f13b830594f5aef46e04a34 to your computer and use it in GitHub Desktop.
Path Tween D3 v4 High Precision Precalculate

This is a port of https://bl.ocks.org/mbostock/3916621 to D3 v4 and an attempt to be able to use much higher precision without affecting smoothness. The idea is to precalculate the resampled path before the transision starts, during its delay. The result is a big jump in all but the first transition because of problems (?) with D3 timer:

d3/d3-timer#26

d3/d3-timer#27

Actually, what I really want to do is not to use much higher precision, but to do this type of path tweening on many paths simultaneously. This was just an easier way to illustrate the idea and the problem.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
</style>
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
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";
svg.append("path")
.attr("transform", "translate(180,150)scale(2,2)")
.attr("d", d0)
.call(transition, d0, d1);
function transition(path, d0, d1) {
console.time('Init -> start delay');
path.transition()
.delay(2000)
.duration(2000)
.attrTween("d", pathTween(path.node(), d1, 0.5))
.on("start", function() {
console.timeEnd('Init -> start delay');
console.time('Start -> end delay');
})
.on("end", function() {
console.timeEnd('Start -> end delay');
path.call(transition, d1, d0);
});
}
function pathTween(node, d1, precision) {
console.time('Tween init');
var path0 = node,
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]);
});
console.timeEnd('Tween init');
return function() {
return function(t) {
return t < 1 ? "M" + points.map(function(p) { return p(t); }).join("L") : d1;
};
};
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment