我正在尝试在D3 v4中重现this example ("Static Force Layout" by Mike Bostock)。
我将迈克的代码与我在创建动态力图(我可以找到here)时学到的内容结合起来。
我(可能是错误地)认为制作静态力图会更容易,但我并不完全理解Mike教程的逻辑,我不能“将其翻译成v4语言”。
在Mike的示例中,每次刷新页面时,节点都会采用不同的位置。我的理解是每个tick操作随机移动每个节点。因此,这部分代码(随机)确定了位置:
// Use a timeout to allow the rest of the page to load first.
setTimeout(function() {
// Run the layout a fixed number of times.
// The ideal number of times scales with graph complexity.
// Of course, don't run too long—you'll hang the page!
force.start();
for (var i = n * n; i > 0; --i) force.tick();
force.stop();
svg.selectAll("line")
.data(links)
.enter().append("line")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 4.5);
loading.remove();
}, 10);
其中n是任意设定的。
这就是我翻译那部分的方式:
setTimeout(function() {
var simulation = d3.forceSimulation()
.force('link', d3.forceLink().id(function (d) { return d.ID; }))
.force('charge', d3.forceManyBody())
.force('center', d3.forceCenter(width / 2, height / 2));
var n = 1000;
//I thought that since I have many nodes and edges in my graph I should use a highgher n. However, wheter n=10 or 1000 nothing changes in the final result
for (var i = n * n; i > 0; --i) simulation.tick();
simulation
.nodes(data.nodes)
.on('tick', ticked);
simulation.force('link')
.links(data.edges)
.distance(distance);
function ticked () {
d3.selectAll('circle')
.attr('cx', function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
d3.selectAll('line')
.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y; })
.attr('x2', function(d) { return d.target.x; })
.attr('y2', function(d) { return d.target.y; });
}
//Strange fact: I need to have both this tick function and the position attributes for each node and edge for the visualization to show something.
simulation.stop();
var link = graph.append("g")
.attr('class', 'links')
.selectAll("line")
.data(data.edges)
.enter()
.append("line")
.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y; })
.attr('x2', function(d) { return d.target.x; })
.attr('y2', function(d) { return d.target.y; });
var node = graph.append("g")
.attr('class', 'nodes')
.selectAll("circle")
.data(data.nodes)
.enter()
.append("circle")
.attr("r", radius)
.attr('fill', 'red')
.attr('cx', function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
loading.remove();
}, 10);
知道我做错了吗?
答案 0 :(得分:0)
更新2016-10-18 下面关于实现一个不需要的滴答功能的东西,看起来完全不正确,这是我在摆弄一天左右之后所能说的。我想的不仅仅是删除帖子,我还会以后来的新理解更新它。为了清楚起见,从我所知道的情况来看,确实需要让模拟对你的对象做任何事情。
我现在正在通过类似的问题工作并且没有解决方案,但确实注意到一些可能值得重新考虑的代码:
请注意,手动调用simulation.tick时不会调度tick事件;事件仅由内部计时器调度,用于模拟的交互式渲染。要影响模拟,请注册力而不是修改节点事件侦听器内的节点位置或速度。
这一切都让我相信以下代码的主体是不必要的,甚至可能是错误的:
function ticked () {
d3.selectAll('circle')
.attr('cx', function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
d3.selectAll('line')
.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y; })
.attr('x2', function(d) { return d.target.x; })
.attr('y2', function(d) { return d.target.y; });
}
希望有所帮助(我知道现在已经老了,但谁知道......)
答案 1 :(得分:-1)
您需要在计算结束位置之前停止并重新启动模拟:
let _newNodes = null
const force = d3.forceSimulation(nodes);
// bind listeners first
force.on('tick', () => {
const {nodes} = this.state;
nodes.forEach(d => { // keep within bounds
d.x = Math.max(d.z, Math.min(this.props.width - d.z, d.x)); // incrementally derive bound x
d.y = Math.max(d.z, Math.min(this.props.height - d.z, d.y)); // incrementally derive bound y
});
_newNodes = nodes;
});
// register force events
force
.force('y', d3.forceY(200).strength(0.01))
// all of your configs here ...
.stop();
// calculate end state
for (let i = 0, n = Math.ceil(Math.log(force.alphaMin()) / Math.log(1 - this.force.alphaDecay())); i < n; ++i) {
force.tick();
}
// resume rendering
force.restart();