我正在尝试进行d3js强制仿真,但我想确保我的节点不会中继错误的信息。
使用以下代码显示节点,但由于力布局的动态特性,它偶尔会将某些节点推出其适当的x坐标位置。
inOrder(){
this.simulation
.force("x", d3.forceX(d => this.xScale(d.value)))
.force("y", d3.forceY(this.height / 2))
.alpha(1).restart();
},
这是发生这种情况的一个令人震惊的例子: 这些数字应按从左到右的顺序排列。
我尝试使用节点上的fx属性将位置锁定在适当的位置:
inOrder(){
this.releases.forEach(x => {
x.fx = this.xScale(x.value)
})
this.simulation
.force("x", d3.forceX(d => this.xScale(d.value)))
.force("y", d3.forceY(this.height / 2))
.alpha(1).restart();
},
这可以保留x位置,但是当调用inOrder
方法时,节点立即跳到其最终x位置。这破坏了力仿真的流动性和动态性。
有没有办法做到两全其美?也许通过使用.on("end", () => {})
或.on("tick", () => {})
?事件处理程序?
Mike Bostock(https://stackoverflow.com/users/365814/mbostock)和Shan Carter创作了一些作品,这些作品启发了我在这里要做的事情:
在“更改”和“部门总计”选项卡之间单击 https://archive.nytimes.com/www.nytimes.com/interactive/2012/02/13/us/politics/2013-budget-proposal-graphic.html?hp
在“总体图片”和“按行业查看”选项卡之间单击 https://archive.nytimes.com/www.nytimes.com/interactive/2013/05/25/sunday-review/corporate-taxes.html
答案 0 :(得分:2)
我在这里可能会丢失一些东西,但是对x定位力(和y)的强度进行修改可以帮助确保正确完成您的订购。默认的forceX或forceY强度为0.1,该强度实现如下:
值0.1表示每个应用程序节点应从其当前x位置向目标x位置移动十分之一的距离。较高的值通常会以其他力或约束为代价,将节点更快地移动到目标位置。不建议在[0,1]范围之外的值。 (docs)
因此,我们可以增加forceX强度,并允许节点在x轴上自由移动,我们可以减小forceY-允许节点更轻松地相互跳动-减小碰撞强度也可能有所帮助。
我没有在下面标记圆圈(而是将它们依次涂上阴影),但是我确实进行了检查以查看它们是否井井有条(在模拟结束时记录到控制台),下面的代码片段仅修改了x和y力(不是碰撞力):
var height = 300;
var width = 500;
var data = d3.range(30).map(function(d,i) {
return { size: Math.random()+1, index: i}
});
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var x = d3.scaleLinear()
.domain([1,30])
.range([50,width-50]);
var color = d3.scaleLinear()
.domain([0,29])
.range(["#ccc","#000"])
var simulation = d3.forceSimulation()
.force("x", d3.forceX(d => x(d.index)).strength(0.20))
.force("y", d3.forceY(height / 2).strength(0.05))
.force("collide", d3.forceCollide().radius(d=> d.size*10))
.alpha(1).restart();
var circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", function(d) { return d.size * 10; })
.attr("fill", function(d,i) { return color(i); })
simulation.nodes(data)
.on("tick",tick)
.on("end",verify);
function tick() {
circles
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
}
function verify() {
var n = 0;
for(var i = 0; i < data.length - 1; i++) {
if(data[i].x > data[i+1].x) n++;
}
console.log(n + " out of place");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
该代码段在500x300的区域中放置了30个圆圈,没有太大的问题:我已经测试了几次,其中0错位。在此处放置100个圆圈将引起问题:圆圈将无法在如此狭窄的区域中更改位置:可能需要对力进行进一步修改,但可能更希望使用较大尺寸的图(而不是小片段视图)
另一种选择是在整个模拟过程中修改力:从强大的x力强度和低的碰撞力强度开始,然后缓慢调高碰撞力,以最大程度地减少后续碰撞。这是example修改tick函数中的力的方法-尽管此示例是针对链接长度而不是x上的位置的-但是适应性不应该太难。
另一种可能性是保持alpha高,直到所有圆在x轴上正确排列,然后开始冷却为止,这又必须在tick函数中发生。