我正在尝试根据点击事件更新节点的位置。
我可以看到控制台中的数据已更新,但是x,y坐标未更新。我期望forceSimulation在数据更改后立即更新x,y坐标。
很明显,我误解了它的工作原理。但是,如果您能说出直觉上我到底错在哪里,那真的可以帮助我。我的想法是,力模拟将基于foci_dict更新forceX和forceY坐标。
在控制台中:{name:“ b”,年龄:27,索引:0,x:45.46420808466252,y:54.94672336907456,…}
对象已更新,但是坐标未更新。
<html>
<body>
<canvas id="bubbles" width="1500" height="500"></canvas>
<button onClick="changeMe()"> clicck me </button>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var height = 500;
var width = 1500;
var canvas = d3.select("#bubbles");
var ctx = canvas.node().getContext("2d");
var r = 5;
var data = [
{ name: "a", age: 27 },
{ name: "a", age: 21 },
{ name: "a", age: 37 },
{ name: "b", age: 23 },
{ name: "b", age: 33 },
{ name: "c", age: 23 },
{ name: "d", age: 33 }
];
var foci_points = {
'a': { x: 50, y: 50 },
'b': { x: 150, y: 150 },
'c': { x: 250, y: 250 },
'd': { x: 350, y: 350 }
};
var simulation = d3.forceSimulation()
.force("x", d3.forceX(function(d){
return foci_points[d.name].x;
}))
.force("y", d3.forceY(function(d){
return foci_points[d.name].y;
})
)
.force("collide", d3.forceCollide(r+1))
.force("charge", d3.forceManyBody().strength(10))
.on("tick", update);
simulation.nodes(data);
function update() {
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
data.forEach(drawNode);
ctx.fill();
}
function changeMe(){
data[0].name="b";
console.log(data);
}
function drawNode(d) {
ctx.moveTo(d.x, d.y);
ctx.arc(d.x, d.y, r, 0, 2 * Math.PI);
}
update();
</script>
</html>
答案 0 :(得分:1)
您必须解决两个问题:
仅由于基础数据已更改,作用力不会自动更新。您必须通过调用force.initialize(nodes)
以编程方式重新初始化作用力。鉴于您的代码,这可能需要遵循以下几行:
simulation.force("x").initialize(data); // Get the x force and re-initialize.
simulation.force("y").initialize(data); // Get the y force and re-initialize.
当您单击按钮时,模拟会冷却甚至停止。要使其重新运行,必须通过将alpha设置为大于alphaMin
的某个值并通过restarting进行模拟来重新加热它。
simulation.alpha(1).restart(); // Reheat and restart the simulation.
最好在您的changeMe()
函数中完成这些操作。
查看以下正在运行的演示代码段:
var height = 500;
var width = 1500;
var canvas = d3.select("#bubbles");
var ctx = canvas.node().getContext("2d");
var r = 5;
var data = [{
name: "a",
age: 27
},
{
name: "a",
age: 21
},
{
name: "a",
age: 37
},
{
name: "b",
age: 23
},
{
name: "b",
age: 33
},
{
name: "c",
age: 23
},
{
name: "d",
age: 33
}
];
var foci_points = {
'a': {
x: 50,
y: 50
},
'b': {
x: 150,
y: 150
},
'c': {
x: 250,
y: 250
},
'd': {
x: 350,
y: 350
}
};
var simulation = d3.forceSimulation()
.force("x", d3.forceX(function(d) {
return foci_points[d.name].x;
}))
.force("y", d3.forceY(function(d) {
return foci_points[d.name].y;
}))
.force("collide", d3.forceCollide(r + 1))
.force("charge", d3.forceManyBody().strength(10))
.on("tick", update);
simulation.nodes(data);
function update() {
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
data.forEach(drawNode);
ctx.fill();
}
function changeMe() {
data[0].name = "b";
simulation.force("x").initialize(data);
simulation.force("y").initialize(data);
simulation.alpha(1).restart();
}
function drawNode(d) {
ctx.moveTo(d.x, d.y);
ctx.arc(d.x, d.y, r, 0, 2 * Math.PI);
}
update();
<script src="https://d3js.org/d3.v4.min.js"></script>
<body>
<button onClick="changeMe()"> clicck me </button>
<canvas id="bubbles" width="1500" height="500"></canvas>
</body>