我尝试了一个简单的气泡图,该气泡图具有排斥功能,但整个力模拟功能不起作用,我是D3的新手,请帮忙
圈子仍然重叠,请提供帮助。
export class AppComponent {
title = 'd3Project';
dataset = [
[34, 78, 14],
[109, 280, 20],
[310, 120, 22],
[79, 411, 32],
[420, 220, 23],
[233, 145, 26],
[333, 96, 27],
[222, 333, 24],
[78, 320, 35],
[21, 123, 12],
[22, 126, 40]
];
dataSet: any = [
[14, 78],
[20, 280],
[22, 120],
[32, 411],
[23, 220],
[26, 145],
[27, 96],
[24, 333],
[35, 320],
[12, 123],
[40, 126]
];
w = 1000;
h = 500;
padding = 30;
xScale = d3.scaleLinear()
.domain([0, d3.max(this.dataset, (d) => d[0])])
.range([this.padding, this.w - this.padding]);
yScale = d3.scaleLinear()
.domain([0, d3.max(this.dataset, (d) => d[1])])
.range([this.h - this.padding, this.padding]);
data = [14, 20, 22, 32, 23, 26, 27, 24, 35, 43, 40];
ngOnInit() {
const svg = d3.select("body")
.append("svg")
.attr("width", this.w)
.attr("height", this.h)
// .attr('x',this.w)
// .attr('y',this.h)
.attr('viewBox', '-700 -300 ' + (this.w + 700) + ' ' + (this.h + 200))
// viewBox="0 0 100 100"
var simulation = d3.forceSimulation().nodes(this.dataset)
//add forces
//we're going to add a charge to each node
//also going to add a centering force
simulation
.force("charge_force", d3.forceManyBody().strength(-50))
.force("center_force", d3.forceCenter(this.w / 2, this.h / 2))
.on('tick', ticked);
function ticked() {
node.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
});
}
let node = svg.selectAll("circle")
.data(this.dataset)
.enter()
.append("circle")
// .attr('cx',(d) => Math.round( this.xScale(d[0]) ) )
// .attr('cy',(d) => Math.round( this.yScale(d[1]) ) )
.attr('r', (d) => d[2])
.attr('position', 'relative')
.attr('transform', 'translate(' + [this.w / 2, this.h / 2] + ')')
.style('fill', (d) => {
if (d[2] % 2) return 'purple';
else if (d[2] < 200) return 'red';
else
return 'orange';
})
.append('title')
.text((d) => 'radius: ' + d[2]);
}
答案 0 :(得分:1)
您的数据结构(数组数组)是错误的。 API清楚要传递给模拟的数据结构:
每个节点必须是一个对象。 (强调我的)
例如,我们可以将您的数据转换为对象数组:
dataset = dataset.map(function(d) {
return {
data: d
}
});
在这里,我将内部数组作为值放在data
属性中。然后,模拟将填充所有其他属性(x
,y
,index
,vx
和vy
)。
此外,您还有一个更微妙的错误:您选择的node
是<title>
元素的选择,而不是<circle>
元素。因此,ticked
函数将不起作用。因此,请中断node
选择,如下所示:
let node = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
//etc...
node.append('title')
//etc...
这是您所做的更改的代码:
dataset = [
[34, 78, 14],
[109, 280, 20],
[310, 120, 22],
[79, 411, 32],
[420, 220, 23],
[233, 145, 26],
[333, 96, 27],
[222, 333, 24],
[78, 320, 35],
[21, 123, 12],
[22, 126, 40]
];
dataset = dataset.map(function(d) {
return {
data: d
}
});
w = 600;
h = 400;
padding = 30;
const svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var simulation = d3.forceSimulation(dataset)
.force("charge_force", d3.forceManyBody().strength(-50))
.force("center_force", d3.forceCenter(w / 2, h / 2))
.on('tick', ticked);
function ticked() {
node.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
let node = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
// .attr('cx',(d) => Math.round( this.xScale(d[0]) ) )
// .attr('cy',(d) => Math.round( this.yScale(d[1]) ) )
.attr('r', (d) => d.data[2])
.style('fill', (d) => {
if (d.data[2] % 2) return 'purple';
else if (d.data[2] < 200) return 'red';
else
return 'orange';
});
node.append('title')
.text((d) => 'radius: ' + d.data[2]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>