我刚刚开始涉足d3,发现学习曲线非常陡峭。这个过程与我习惯的过程完全不同,数学大多数都超出了我的想象。
无论如何,我的项目包含一个力布局,代表系统之间的集成地图。这部分工作得非常好,但我确实有一个主要问题,这也在Michael Bostocks网站上的强制定向布局演示中有所体现: 启动节点时,它们似乎在画布上呈现。在此之后,一些严肃的物理数学正在接管,模拟一个引力,它在一个相当混乱的路径上来回发送节点,直到它们冷静下来并在一些随机坐标上稳定下来。虽然这些动作在第一次运行演示时会逐渐消失,但是当您尝试从管理员的角度查看公司的网络接口状态时,服务器不会保持静止,一段时间后会变得很烦人。
我确信我为这个项目设置了正确的布局,因为我确实希望服务器自动布局,我确实希望可视化它们之间的链接。然而,我对万有引力效应感到矛盾。
我想知道;是否可以手动设置每个节点的初始位置,以便我可以将它们放在更接近重力中心并稍微缩短“反弹时间”?
答案 0 :(得分:34)
上述所有答案都误解了ØysteinAmundsen的问题。
在它启动时稳定力的唯一方法是将node.x和node.y设置为适当的值。 请注意,节点是d3.js的数据,而不是表示的DOM类型。
例如,如果你加载
nodes = [{"id": a}, {"id": b}, {"id": c}]
进入
d3.layout.force().nodes(nodes)
您必须设置节点数组中所有元素的所有.x和.y 它会是这样的(在coffeescript中)
nodes = [{"id": a}, {"id": b}, {"id": c}]
for i in [0..2]
nodes[i].x = 500 #the initial x position of node
nodes[i].y = 300 #the initial y position of node
d3.layout.force().nodes(nodes).links(links)
然后节点将从force.start()开始。 这样可以避免混乱。
答案 1 :(得分:30)
在内部,在“正常”使用情况下,强制布局会反复调用自己的tick()
方法(通过setInterval
或requestAnimationFrame
),直到布局解决方案为止。此时,其alpha()
值等于或接近0.
因此,如果您希望通过此解决方案流程“快进”,则可以反复同步调用tick()
方法,直到布局的alpha达到一个值,根据您的特定要求,该值构成“关闭”足够的解决方案。像这样:
var force = d3.layout.force(),
safety = 0;
while(force.alpha() > 0.05) { // You'll want to try out different, "small" values for this
force.tick();
if(safety++ > 500) {
break;// Avoids infinite looping in case this solution was a bad idea
}
}
if(safety < 500) {
console.log('success??');
}
运行此代码后,您可以根据节点的状态绘制布局。或者,如果您通过绑定到tick事件(即force.on('tick', drawMyLayout)
)来绘制布局,那么您需要在此代码运行之后进行绑定,否则您将不必要地在while
循环期间同步渲染布局数百次。
JohnS将这种方法归结为一个简洁的功能。在本页的某处查看他的答案。
答案 2 :(得分:11)
我刚才处理过类似的事情。有几点需要考虑。
1)迭代刻度模拟了一个达到平衡的系统。因此,在系统稳定并且您进行自动布局之前,无法避免在需要时多次调用tick。也就是说,您无需为每个刻度更新可视化模拟工作!事实上,如果你不这样做,迭代会更快。我的代码的相关部分是:
var iters = 600; // You can get decent results from 300 if you are pressed for time
var thresh = 0.001;
if(!hasCachedLayout || optionsChanged || minorOptionsChanged) {
force.start(); // Defaults to alpha = 0.1
if(hasCachedLayout) {
force.alpha(optionsChanged ? 0.1 : 0.01);
}
for (var i = iters; i > 0; --i) {
force.tick();
if(force.alpha() < thresh) {
//console.log("Reached " + force.alpha() + " for " + data.nodes.length + " node chart after " + (iters - i) + " ticks.");
break;
}
}
force.stop();
}
它同步运行,运行后我为所有节点和链接创建dom元素。对于小图表,这种运行速度非常快,但您会发现较大的图形(100多个节点)存在延迟 - 它们的计算成本更高。
2)您可以缓存/种子布局。如果没有设置位置,则力布局将在初始化时均匀分布节点!因此,如果确保节点已设置x和y属性,则将使用这些属性。特别是当我更新现有图形时,我将重新使用先前布局中的x和y位置。
您会发现初始布局良好,您需要批次更少的迭代才能达到稳定的配置。 (这是上面代码中hasCachedLayout跟踪的内容)。注意:如果你从相同的布局重新使用相同的节点,那么你还必须确保将px和py设置为NaN,否则你会得到奇怪的结果。
答案 3 :(得分:8)
基于其他答案我已经采用了这种方法:
function forwardAlpha(layout, alpha, max) {
alpha = alpha || 0;
max = max || 1000;
var i = 0;
while(layout.alpha() > alpha && i++ < max) layout.tick();
}
答案 4 :(得分:4)
也许force.friction(0.5)
或其他低于默认值0.9的数字会有帮助吗?至少它会在页面加载时给人一种不那么混乱的印象。
答案 5 :(得分:0)
var width = 960,
height = 500;
var fill = d3.scale.category20();
var force = d3.layout.force()
.size([width, height])
.nodes([{}]) // initialize with a single node
.linkDistance(30)
.charge(-60)
.on("tick", tick);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.on("mousedown", mousedown);
svg.append("rect")
.attr("width", width)
.attr("height", height);
var nodes = force.nodes(),
links = force.links(),
node = svg.selectAll(".node"),
link = svg.selectAll(".link");
// var cursor = svg.append("circle")
// .attr("r", 30)
// .attr("transform", "translate(-100,-100)")
// .attr("class", "cursor");
restart();
function mousedown() {
var point = d3.mouse(this),
node = {
x: width / 2,
y: height / 2,
"number": Math.floor(Math.random() * 100)
},
n = nodes.push(node);
// add links to any nearby nodes
/* nodes.forEach(function(target) {
var x = target.x - node.x,
y = target.y - node.y;
if (Math.sqrt(x * x + y * y) < 30) {
links.push({source: node, target: target});
}
});
*/
restart();
}
function tick() {
link.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;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
function restart() {
link = link.data(links);
link.enter().insert("line", ".node")
.attr("class", "link");
node = node.data(nodes);
// node.enter().insert("circle", ".cursor")
// .attr("class", "node")
// .attr("r", 5)
// .call(force.drag);
var nodeEnter = node.enter().insert("svg:g", ".cursor")
.attr("class", "node")
.call(force.drag);
nodeEnter.append("svg:circle")
.attr("r", 5)
nodeEnter.append("svg:text")
.attr("class", "textClass")
.attr("x", 14)
.attr("y", ".31em")
.text(function(d) {
return d.number;
});
force.start();
}
rect {
fill: none;
pointer-events: all;
}
.node {
fill: #000;
}
.cursor {
fill: none;
stroke: brown;
pointer-events: none;
}
.link {
stroke: #999;
}
.textClass {
stroke: #323232;
font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
font-weight: normal;
stroke-width: .5;
font-size: 14px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
您可能正在寻找的一个例子。它设定了x&amp; y在将新节点插入布局之前的新属性的属性。所需的位置是svg元素的中心。