I’m building a hierarchical data visualization using D3’s circle packing. The library arranges the nodes well, but I’m having trouble with zooming. Specifically, the leaf nodes (circles without children) appear too small when zoomed out and too large when zoomed in. I want these leaf nodes to remain a consistent size on the screen, regardless of the zoom level.
import { Component, OnInit, ElementRef, Input } from '@angular/core';
import * as d3 from 'd3';
@Component({
selector: 'app-circle-view',
templateUrl: './circle-view.component.html',
styleUrls: ['./circle-view.component.css']
})
export class CircleViewComponent implements OnInit {
@Input() canvas: number = 0;
constructor(private elRef: ElementRef) {}
ngOnInit(): void {
this.createVisualization();
}
private createVisualization(): void {
const width = this.canvas;
const height = this.canvas;
const canvas = d3.select(this.elRef.nativeElement.querySelector('svg'))
.attr("viewBox", `-${width / 2} -${height / 2} ${width} ${height}`)
.attr("width", width)
.attr("height", height);
const pack = d3.pack().size([width, height]).padding(3);
const root = pack(d3.hierarchy(this.buildHierarchy()).sum(() => 1));
const node = canvas.append("g")
.selectAll("circle")
.data(root.descendants())
.join("circle")
.attr("fill", d => d.children ? 'rgba(0, 174, 207, 0.4)' : 'rgb(107, 209, 112)')
.on('click', (event, d) => {
zoom(event, d);
event.stopPropagation();
});
canvas.on("click", (event) => zoom(event, root));
let view: [number, number, number];
zoomTo([root.x, root.y, root.r * 2]);
function zoomTo(v: [number, number, number]) {
const k = width / v[2];
view = v;
node.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`)
.attr("r", d => d.r * k);
}
function zoom(event: any, d: d3.HierarchyCircularNode<any>) {
const transition = canvas.transition()
.duration(750)
.tween("zoom", () => {
const i = d3.interpolateZoom(view, [d.x, d.y, d.r * 2]);
return t => zoomTo(i(t));
});
}
}
private buildHierarchy() {
// Sample hierarchical data structure
return {
id: 1,
name: 'Root Circle',
children: [
{ id: 2, name: 'Circle 1', children: [{ id: 3, name: 'Circle 1.1' }] },
{ id: 4, name: 'Circle 2' }
]
};
}
}
I’ve tried assigning size values directly to the data in an attempt to control the node sizes, but this didn’t give me the desired result because the size values aren’t directly linked to the radius of the circles.
const root = d3.hierarchy(data)
.sum(function(d) { return d.value || 0; });
Please help me I’m only a student. Nobody taught me D3 ):