svg = {
const svg = d3.select(DOM.svg(width, height));
const defs = svg.append("defs");
const gradients = defs
.selectAll("linearGradient")
.data(graph.links)
.join("linearGradient")
.attr("id", d => d.gradient.id);
gradients
.append("stop")
.attr("offset", 0.0)
.attr("stop-color", d => d.source.color);
gradients
.append("stop")
.attr("offset", 1.0)
.attr("stop-color", d => d.target.color);
const view = svg
.append("g")
.classed("view", true)
.attr("transform", `translate(${margin}, ${margin})`);
const nodes = view
.selectAll("rect.node")
.data(graph.nodes)
.join("rect")
.classed("node", true)
.attr("id", d => `node-${d.index}`)
.attr("x", d => d.x0)
.attr("y", d => d.y0)
.attr("width", d => d.x1 - d.x0)
.attr("height", d => Math.max(1, d.y1 - d.y0))
.attr("fill", d => d.color)
.attr("opacity", 0.9);
// Add titles for node hover effects.
nodes.append("title").text(d => `${d.name}\n${format(d.value)}`);
// Add text labels.
view
.selectAll("text.node")
.data(graph.nodes)
.join("text")
.classed("node", true)
.attr("x", d => d.x1)
.attr("dx", 6)
.attr("y", d => (d.y1 + d.y0) / 2)
.attr("dy", "0.35em")
.attr("fill", "black")
.attr("text-anchor", "start")
.attr("font-size", 10)
.attr("font-family", "Arial, sans-serif")
.text(d => d.name)
.filter(d => d.x1 > width / 2)
.attr("x", d => d.x0)
.attr("dx", -6)
.attr("text-anchor", "end");
// Define the gray links.
const links = view
.selectAll("path.link")
.data(graph.links)
.join("path")
.classed("link", true)
.attr("d", sankey.sankeyLinkHorizontal())
.attr("stroke", "black")
.attr("stroke-opacity", 0.1)
.attr("stroke-width", d => Math.max(1, d.width))
.attr("fill", "none");
// Add <title> hover effect on links.
links
.append("title")
.text(
d => `${d.source.name} ${arrow} ${d.target.name}\n${format(d.value)}`
);
// Define the default dash behavior for colored gradients.
function setDash(link) {
let el = view.select(`#${link.path.id}`);
let length = el.node().getTotalLength();
el.attr("stroke-dasharray", `${length} ${length}`).attr(
"stroke-dashoffset",
length
);
}
const gradientLinks = view
.selectAll("path.gradient-link")
.data(graph.links)
.join("path")
.classed("gradient-link", true)
.attr("id", d => d.path.id)
.attr("d", sankey.sankeyLinkHorizontal())
.attr("stroke", d => d.gradient)
.attr("stroke-opacity", 0)
.attr("stroke-width", d => Math.max(1, d.width))
.attr("fill", "none")
.each(setDash);
function branchAnimate(node) {
let links = view.selectAll("path.gradient-link").filter(link => {
return node.sourceLinks.indexOf(link) !== -1;
});
let nextNodes = [];
links.each(link => {
nextNodes.push(link.target);
});
links
.attr("stroke-opacity", 0.5)
.transition()
.duration(duration)
.ease(d3.easeLinear)
.attr("stroke-dashoffset", 0)
.on("end", () => {
nextNodes.forEach(node => {
branchAnimate(node);
});
});
}
function branchClear() {
gradientLinks.transition();
gradientLinks.attr("stroke-opactiy", 0).each(setDash);
}
nodes.on("mouseover", branchAnimate).on("mouseout", branchClear);
return svg.node();
}