我遇到了一些问题,让我的SVG标记能够在我触发'tick'回调时特别适用于不同的力量。我正在尝试在模拟运行时更新元素的cx
和cy
属性,但它们本身并不具有我可以更新的属性。例如:
var shape = d3.selectAll('circle')
.on('mouseenter', handleCircleMouseEnter)
.on('mouseout', handleCircleMouseOut)
.on('mousemove', handleMouseMove)
.attr('cursor', 'pointer')
.attr('class', 'shapes')
.attr('cx', function(d) {return d.x;})
.attr('cy', function(d) {return d.y;})
var shapes = d3.selectAll('.shapes')
var simulation = d3.forceSimulation(shapes)
simulation
.force('charge_force', d3.forceManyBody())
.force('charge', d3.forceCollide().radius(5))
.force('center_force', d3.forceCenter(width / 2, height / 2))
.on('tick', ticked)
function ticked() {
// always returns with 'd' as undefined
shapes.attr('cx', function(d) {return d.x;})
shapes.attr('cy', function(d) {return d.y;})
}
奇怪的是,如果我在'tick'属性中没有返回任何内容,我会用力量移动,但d
永远不会被传入。
我正在做的一件奇怪的事情是我从来没有用D3构建这个SVG,我在标记中有一个SVG元素,我用力修改。不确定这是可能的,但似乎它几乎正常工作。不知所措......
非常感谢任何帮助!
答案 0 :(得分:4)
如果我正确地理解了你的问题(我很想听到你这么做的话),你想对SVG中已存在的元素进行力模拟。如果是这种情况,答案是是。但有一个问题:
力模拟使用对象数组来设置相关属性。根据{{3}}:
simulation.nodes([nodes])<>
如果指定了节点,则将模拟的节点设置为指定的对象数组,必要时初始化它们的位置和速度,然后重新初始化任何绑定力。 (强调我的)
该对象数组只是绑定到它们的数据。但是,当您获取SVG中已存在的元素时,没有数据绑定(除非我们手动绑定它们,继续阅读......),这解释了为什么d
返回undefined
。< / p>
因此,我们必须将数据绑定到元素。让我们看看如何做到这一点。假设这个SVG已经存在3个圆圈:
<svg>
<circle cx="20" cy="75" r="20" fill="green"></circle>
<circle cx="150" cy="75" r="20" fill="red"></circle>
<circle cx="280" cy="75" r="20" fill="blue"></circle>
</svg>
要使用力模拟,我们首先设置每个圆的基准:
var circles = svg.selectAll("circle");
circles.each(function() {
d3.select(this).datum({
x: +d3.select(this).attr("cx"),
y: +d3.select(this).attr("cy")
})
});
它可以是任何对象,即使这样也可以:
var circles = svg.selectAll("circle");
circles.each(function() {
d3.select(this).datum({foo: "bar"})
});
然后,我们将该对象数组传递给模拟:
var data = circles.data()
simulation.nodes(data)
.on("tick", ticked);
这是一个演示:
var svg = d3.select("svg");
var simulation = d3.forceSimulation()
.force("collide", d3.forceCollide(20))
.force("charge", d3.forceManyBody().strength(+100))
var circles = svg.selectAll("circle");
circles.each(function() {
d3.select(this).datum({
x: +d3.select(this).attr("cx"),
y: +d3.select(this).attr("cy")
})
});
var data = circles.data()
simulation.nodes(data)
.on("tick", ticked);
function ticked() {
circles.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg>
<circle cx="20" cy="75" r="20" fill="green"></circle>
<circle cx="150" cy="75" r="20" fill="red"></circle>
<circle cx="280" cy="75" r="20" fill="blue"></circle>
</svg>