数据结构:
{
"name": "flare",
"children": [
{
"name": "",
"children": [
{
"name": "",
"children": [
{
"name": "",
"value": 3938
},
]
},
]
}
]
}
大概的思路简单来说就是 —创建pack布局—转换数据—画圆—添加文字—添加mouse事件
/* eslint-disable no-param-reassign */
import * as d3 from 'd3';
export default function zoomPacking(id, data) {
(() => {
d3.select(id)
.selectAll('svg')
.remove();
})();
/** 宽 */
const width = 500;
/** 高 */
const height = 500;
const color = d3
.scaleLinear()
.domain([0, 5])
.range(['hsl(152,80%,80%)', 'hsl(228,30%,40%)'])
.interpolate(d3.interpolateHcl);
const pack = dataset =>
d3
.pack()
.size([width, height])
.padding(3)(
d3
.hierarchy(dataset)
.sum(d => d.value)
.sort((a, b) => b.value - a.value)
);
const root = pack(data);
let focus = root;
let view;
const svg = d3
.select(id)
.append('svg')
.attr('viewBox', `-${width / 2} -${height / 2} ${width} ${height}`)
.attr('width', width)
.attr('height', height)
.style('display', 'block')
.style('background', color(0))
.style('cursor', 'pointer')
.style('margin', '100 100px')
.on('click', () => zoom(root));
const node = svg
.append('g')
.selectAll('circle')
.data(root.descendants().slice(1))
.join('circle')
.attr('fill', d => (d.children ? color(d.depth) : 'white'))
.attr('pointer-events', d => (!d.children ? 'none' : null))
.on('mouseover', function() {
d3.select(this).attr('stroke', '#000');
})
.on('mouseout', function() {
d3.select(this).attr('stroke', null);
})
.on('click', d => focus !== d && (zoom(d), d3.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-opacity', d => (d.parent === root ? 1 : 0))
.text(d => d.data.name);
zoomTo([root.x, root.y, root.r * 2]);
function zoomTo(v) {
const k = width / v[2];
view = v;
label.attr('transform', d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`);
node.attr('transform', d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`);
node.attr('r', d => d.r * k);
}
function zoom(d) {
focus = d;
const transition = svg
.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween('zoom', () => {
const i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2]);
return t => zoomTo(i(t));
});
label
.filter(() => {
return d.parent !== focus;
})
.transition(transition)
.style('fill-opacity', a => (a.parent === focus ? 1 : 0));
}
}