chart = {
const root = pack(data);
let focus = root;
let view;
const svg = d3.create("svg")
.attr("viewBox", `-${width / 2} -${height / 2} ${width} ${height}`)
.style("display", "block")
.style("margin", "0 -14px")
.style("background", 'white')
.style("cursor", "pointer")
.on("click", (event) => zoom(event, root));
const node = svg.append("g")
.selectAll("circle")
.data(root.descendants().slice(1))
.join("circle")
.attr("fill", d => d.children ? "#FFFCFF" : color(2))
.attr("stroke", d => d.children ? "lightgrey" : 'none')
.attr("stroke-width", function(d){
return !d.children? 0:(0.3)})
.attr("pointer-events", d => !d.children ? "none" : null)
.on("mouseover", function() { d3.select(this).attr("stroke", "purple"); })
.on("mouseout", function(event, d) { d3.select(this).attr("stroke", color(d.depth)); })
.on("click", (event, d) => focus !== d && (zoom(event, d), event.stopPropagation()))
;
const label = svg
.append("g")
.style("font", "10px sans-serif")
.attr("pointer-events", "none")
.attr("text-anchor", "middle")
.selectAll("text")
.data(root.descendants())
.join("text")
.style("fill", "darkblue")
// .attr("stroke", 'white')
// .attr("stroke-width", 1)
.attr("stroke-align", "middle")
.append("textPath")
.attr("alignment-baseline", "middle")
.attr("font-size", "6px")
.attr("startOffset", "50%")
.attr("xlink:href", (d) => "#" + d.data.name)
.style("fill-opacity", (d) => (d.parent === root ? 1 : 0))
.style("display", (d) => (d.parent === root ? "inline" : "none"))
.text((d) => d.data.name);
const pathes = svg
.append("g")
.selectAll("path")
.data(root.descendants().slice(1))
.join("path")
.attr("id", (d) => d.data.name)
.attr("fill", "none")
zoomTo([root.x, root.y, root.r * 2]);
function zoomTo(v) {
const k = width / v[2];
view = v;
console.log('k',k, v);
const x = (d) => (d.x - v[0]) * k
const y = (d) => (d.y - v[1]) * k
label.attr(
"transform",
(d) => `translate(${x(d)},${y(d)})`
);
node.attr(
"transform",
function(d) {console.log('node.attr', d);return `translate(${x(d)},${y(d)})`}
);
node.attr("r", (d) => d.r * k);
pathes.attr("d", (d) => {
const dx = x(d)
const dy = y(d)
const dr = d.r * k
return `M ${dx - dr} ${dy} a ${dr} ${dr} 0 0 1 ${dr * 2} 0`});
}
function zoom(event, d) {
const focus0 = focus;
focus = d;
const transition = svg
.transition()
.duration(event.altKey ? 7500 : 750)
.tween("zoom", (d) => {
const i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2]);
console.log('i',view);
return (t) => zoomTo(i(t));
});
label
.filter(function (d) {
return d.parent === focus || this.style.display === "inline";
})
.transition(transition)
.style("fill-opacity", (d) => (d.parent === focus ? 1 : 0))
.on("start", function (d) {
if (d.parent === focus) this.style.display = "inline";
})
.on("end", function (d) {
if (d.parent !== focus) this.style.display = "none";
});
}
return svg.node();
}