为什么 d3.js 中的圆包装中 d3.hierarchy 的 r 值是负值?
Why the value for r is negative from d3.hierarchy in circle packing in d3.js?
我以 https://observablehq.com/@d3/zoomable-circle-packing 为例,在 angular 中尝试 d3
和 circle packing
。我有似乎是分层的数据,我正在遵循所提供的代码。但是,我的 d3.hierarchy()
给出了奇怪的结果。由于某种原因,所有 d.x
、d.y
和 r
都是负数。我不知道为什么。我仍在阅读一些指南和书籍,试图了解我是否可以弄清楚 d3.hierarchy
是如何工作来理解为什么这些值是负数的。但我有点挣扎。代码中发生了什么,我如何为数组中的每个对象显示包含 3 个圆圈的圆圈包装?
我在下面的 stackblitz 中有代码。
StackBlitz
stackblitz
中的代码是here
代码
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' });
}
}
}
您有 2 个问题:
您的数据结构不正确,您将对象作为 children
属性的值。它们应该是数组。
您正在设置 SVG viewBox
,而不是它的宽度。因此,这...
this.diameter = +this.svg.attr("width")
...就是 +null
,也就是 0
。因此, size()
方法中的数组将具有负值,这解释了您的主要问题。请改用 width
和 height
。
这是分叉代码:https://stackblitz.com/edit/angular-circle-packing-uyigxs?file=src/app/app.component.ts
我以 https://observablehq.com/@d3/zoomable-circle-packing 为例,在 angular 中尝试 d3
和 circle packing
。我有似乎是分层的数据,我正在遵循所提供的代码。但是,我的 d3.hierarchy()
给出了奇怪的结果。由于某种原因,所有 d.x
、d.y
和 r
都是负数。我不知道为什么。我仍在阅读一些指南和书籍,试图了解我是否可以弄清楚 d3.hierarchy
是如何工作来理解为什么这些值是负数的。但我有点挣扎。代码中发生了什么,我如何为数组中的每个对象显示包含 3 个圆圈的圆圈包装?
我在下面的 stackblitz 中有代码。
StackBlitz
stackblitz
中的代码是here代码
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' });
}
}
}
您有 2 个问题:
您的数据结构不正确,您将对象作为
children
属性的值。它们应该是数组。您正在设置 SVG
viewBox
,而不是它的宽度。因此,这...this.diameter = +this.svg.attr("width")
...就是
+null
,也就是0
。因此,size()
方法中的数组将具有负值,这解释了您的主要问题。请改用width
和height
。
这是分叉代码:https://stackblitz.com/edit/angular-circle-packing-uyigxs?file=src/app/app.component.ts