问题
I'm using https://observablehq.com/@d3/zoomable-circle-packing as an example to try d3 and circle packing in angular. I have the data which seems to be hierarchical and I'm following along the code that's presented. However, my d3.hierarchy() is giving me strange results. For some reason, all d.x, d.y and r are all negative. I couldn't figure out why. I'm still reading some guides and books trying to understand if I can figure out how d3.hierarchy works to understand why the values are negative. But I'm bit struggling with that. What's happening in the code and how can I show the circle packing with 3 circles for each object in the array?
I have the codes in stackblitz below.
StackBlitz
The code is here in stackblitz
Code
import { Component, OnInit, ElementRef, ViewChild, VERSION, AfterViewInit } from '@angular/core';
import * as d3 from 'd3';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit, AfterViewInit {
private diameter: number;
private margin = { top: 20, right: 20, bottom: 20, left: 20 };
private width: number;
private height: number;
private svg; any;
private g: any;
private svgContainer: ElementRef;
private color: any;
private dac: any;
private pack: any;
@ViewChild('circleContainer', { static: false }) set content(content: ElementRef) {
if (content) {
this.svgContainer = content;
}
}
ngOnInit() {
this.dac = {
"name": "DAC",
"children":
[
{
"name":
[
"Direction"
],
"children":
{
"children":
[
{"name":"leader","score":3.33,"_row":"leader"},
{"name":"same_sector","score":3.64,"_row":"same_sector"},
{"name":"senior_teams","score":3.81,"_row":"senior_teams"},
{"name":"team","score":3.81,"_row":"team"}
]
}
},
{
"title":
[
"Alignment"
],
"children":
{
"children":
[
{"name":"leader","score":3,"_row":"leader"},
{"name":"same_sector","score":3.51,"_row":"same_sector"},
{"name":"senior_teams","score":3.48,"_row":"senior_teams"},
{"name":"team","score":3.48,"_row":"team"}
]
}
},
{
"title":
[
"Commitment"
],
"children":
{
"children":
[
{"name":"leader","score":3.67,"_row":"leader"},
{"name":"same_sector","score":4.05,"_row":"same_sector"},
{"name":"senior_teams","score":3.57,"_row":"senior_teams"},
{"name":"team","score":3.57,"_row":"team"}
]
}
}
]
}
this.createChart();
}
ngAfterViewInit() {
this.width = 500 - this.margin.left - this.margin.right;
this.height = 400 - this.margin.top - this.margin.bottom;
this.color = d3.scaleLinear<string>()
.domain([1, 5])
.range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
.interpolate(d3.interpolateHcl);
if (this.svgContainer && this.dac) {
this.createChart()
}
}
private createSVG(rect) {
this.svg = d3.select(rect)
.append('svg')
.attr('viewBox', `0 0 ${this.width / 2} ${this.height / 2}`)
.attr('preserveAspectRatio', 'xMinYMin meet')
.style('display', 'block')
.style('margin', "0 auto")
.style('background', this.color(1))
.style('cursor', 'pointer')
.attr('class', 'bubble-chart')
this.diameter = +this.svg.attr("width")
console.log("diameter: ", this.diameter);
this.pack = data => d3.pack()
.size([this.diameter - this.margin.left, this.diameter - this.margin.right])
.padding(2)
(d3.hierarchy(data)
.sum(d => d.score)
.sort((a, b) => b.value - a.value))
}
private createChart() {
let that = this;
const rect = this.svgContainer.nativeElement;
this.createSVG(rect);
const root = this.pack(this.dac);
let focus = root;
let view;
console.log(root);
this.svg = this.svg
.on('click', (event) => zoom(event, root));
const node = this.svg.append('g')
.selectAll('circle')
.data(root.descendants().slice(1))
.join('circle')
.attr('fill', d => d.children ? this.color(d.depth) : "white")
.attr('pointer-events', d => !d.children ? "none" : null)
.on('mouseover', function () { d3.select(this).attr('stroke', '#999'); })
.on('mouseout', function () { d3.select(this).attr('stroke', 'null'); })
.on('click', (event, d) => focus !== d && (zoom(event, d), event.stopPropagation()));
const label = this.svg.append('g')
.style('font', '10px Roboto')
.attr('pointer-events', 'none')
.attr('text-anchor', 'middle')
.selectAll('text')
.data(root.descendants())
.join('text')
.style('fill-opacity', d => d.parent === root ? 1 : 0)
.style('display', d => d.parent === root ? 'inline' : 'none')
.text(d => d.data.name);
zoomTo([root.x, root.y, root.r * 2]);
function zoomTo(v) {
const k = that.width / v[2];
view = v;
console.log("k: ", k);
console.log("v:",v);
label.attr('transform', d => {
console.log("dx: ", d.x)
console.log("dy: ", d.y);
return `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(event, d) {
const focus0 = focus;
focus = d;
const transition = that.svg.transition()
.duration(event.altKey ? 7500 : 750)
.tween('zoom', d => {
const i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2]);
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' });
}
}
}
回答1:
You have 2 problems:
Your data structure is incorrect, you have objects as the value of your
childrenproperties. They should be arrays.You are setting the SVG
viewBox, not its width. Therefore, this...this.diameter = +this.svg.attr("width")...is just
+null, which is0. Because of that, the array in thesize()method will have negative values, which explains your main issue. Use yourwidthandheightinstead.
Here is the forked code: https://stackblitz.com/edit/angular-circle-packing-uyigxs?file=src/app/app.component.ts
来源:https://stackoverflow.com/questions/63947769/why-the-value-for-r-is-negative-from-d3-hierarchy-in-circle-packing-in-d3-js