Reputation: 1656
I am trying the implementation of the collapsible tree in d3 v4. I was plying with this example and realized, it is using a custom function to create the link shape
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
Since d3 v 4.9 there is a built-in link generator and I wonder how can it be used in this example.
I have troubles understanding following calls
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
....
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function(d) {
var o = {x: source.x, y: source.y}
return diagonal(o, o)
})
.remove();
I understand, that this is creating a curved line from point(x,y) to point (x,y) - so basically from and to the same point ?
Furthermore, I tried to update
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function(d){ return diagonal(d, d.parent) });
with following code
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', d3.linkHorizontal()
.source(function (d) {return d.parent})
.target(function (d) {return d})
);
but I got lot of errors in the console
d3.v4.min.js:2 Error: <path> attribute d: Expected number, "MNaN,NaNCNaN,NaN,…".
Could someone explain my mistake or point me to some working code ? Many thanks!
Upvotes: 3
Views: 1512
Reputation: 3971
I know it is too late but here is a great example of how to animate the path https://observablehq.com/@onoratod/animate-a-path-in-d3. Just build a path then animate it.
const linkGen = d3.linkHorizontal();
const paths = linksData.map(d => {
return { path: linkGen({target:...,source:...}), color: d.color }
})
paths.map(path => {
var path = svg.append("path")
.attr("d", path.path)
.attr("fill", "none")
.attr("stroke-width", 2)
.attr("stroke", path.color);
const length = path.node().getTotalLength();
// This function will animate the path over and over again
// Animate the path by setting the initial offset and dasharray and then transition the offset to 0
path.attr("stroke-dasharray", length + " " + length)
.attr("stroke-dashoffset", length)
.transition()
.ease(d3.easeLinear)
.attr("stroke-dashoffset", 0)
.duration(3000)
})
Upvotes: 0
Reputation: 1656
I think I figured out most of the confusion from my original answer.
Creating the "zero length path" on link creation
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
and on link removal
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function(d) {
var o = {x: source.x, y: source.y}
return diagonal(o, o)
})
.remove();
is used to create the animation, when the nodes, together with the related links slides-out/retrieves-back-in their parent nodes. The animation animates transformation of the path to/from their final shape from/to the "null" shape - thus the link starting and finishing at the same coordinates.
The link generator can be then used like this
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', d3.linkHorizontal()
.source(function(){ return [sourceNode.y0, sourceNode.x0]})
.target(function(){ return [sourceNode.y0, sourceNode.x0]}));
...
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', d3.linkHorizontal()
.source(function (d) {return [d.parent.y, d.parent.x]})
.target(function (d) {return [d.y, d.x]})
);
...
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', d3.linkHorizontal()
.source(function(){ return [sourceNode.y, sourceNode.x]})
.target(function(){ return [sourceNode.y, sourceNode.x]}))
.remove();
Upvotes: 3