如何在力导向图中禁用动画?
How to disable animation in a force-directed graph?
有什么方法可以禁用 D3 力导向图中的动画吗?
我正在处理这个例子:https://bl.ocks.org/mbostock/4062045
我想在没有初始动画的情况下渲染图表,也就是说,显示所有节点和链接的最终位置。
编辑
此方法只是简单地隐藏了模拟的动画部分。请参阅 ,它在不绘制中间结果的情况下执行模拟,这意味着用户不必在解决方案缓慢演变时等待。
========
'animation'实际上是模拟运行。可以玩模拟运行的时间,但这可能意味着节点卡在局部最小值 - see the documentation here for more details.
您可以选择向 end
事件添加侦听器,该事件在模拟完成时触发。我创建了一个片段,其中图表最初是隐藏的,然后在完成模拟后出现。
另一种方法是在服务器端渲染图表(如果这是一个选项),然后提供一个现成的 SVG,可以用 d3 进一步操作。
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
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))
.on('end', function() {
svg.classed('hidden', false)
d3.select('#loading').remove()
});
// I wasn't able to get the snippet to load the original data from https://bl.ocks.org/mbostock/raw/4062045/miserables.json so this is a copy hosted on glitch
d3.json("https://cdn.glitch.com/8e57a936-9a34-4e95-a03d-598e5738f44d%2Fmiserables.json", function(error, graph) {
if (error) {
console.log(error)
};
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) {
return color(d.group);
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) {
return d.id;
});
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.hidden {
visibility: hidden
}
img {
display: block;
margin-left: auto;
margin-right: auto;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<img id ="loading" src="http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif" />
<svg width="960" height="600" class="hidden"></svg>
尽管这个问题已经有了 ,但建议的解决方案并不是在 D3 力图中禁用动画的正确方法。浏览器仍在移动节点和链接!您只是 看不到 它们在移动,但浏览器正在移动它们,进行大量计算并浪费大量 time/resources。此外,您不需要服务器端。
我的回答提出了一个不同的解决方案,实际上不绘制动画。例如,您可以在 Mike Bostock(D3 创作者)的 this code 中看到它。
当您了解什么是 tick
函数时,这个解决方案很容易遵循:它只是一个计算模拟中所有位置并前进一步的函数。尽管绝大多数 D3 力导向图在每个刻度处绘制节点和链接,但您不需要这样做。
您可以执行以下操作:
使用stop()
停止模拟,定义后立即停止:
var simulation = d3.forceSimulation(graph.nodes)
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.stop();//stop the simulation here
模拟运行不画任何东西。这是最重要的一步:您不需要在每个刻度时移动元素。在这里,我 运行 宁 300 个刻度,这大约是默认数字:
for (var i = 0; i < 300; ++i) simulation.tick();
然后,只需使用模拟创建的属性(x
、y
、source
、target
)绘制圆圈和行,一次:
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
以下是仅包含这些更改的链接块:http://bl.ocks.org/anonymous/8a4e4e2fed281ea5e2a5c804a9a03783/85ced3ea82a4bed20a2010530562b655d8f6e464
将此解决方案的时间与 "hiding-the-nodes" 解决方案(已接受的答案)的时间进行比较。这里的速度更快。在我的测试中,我得到了这个结果:
- "hiding-the-nodes"解法:5000ms左右
- 此解决方案:大约 200 毫秒
也就是快了 25 倍。
PS:为简单起见,我删除了分叉块中的 ticked
函数。如果要拖节点,直接加回去即可。
编辑 D3 v5.8
现在 D3 v5.8 允许将交互次数传递给 simulation.tick()
,您甚至不再需要 for
循环。所以,而不是:
for (var i = 0; i < 300; ++i) simulation.tick();
你可以这样做:
simulation.tick(300);
有什么方法可以禁用 D3 力导向图中的动画吗?
我正在处理这个例子:https://bl.ocks.org/mbostock/4062045
我想在没有初始动画的情况下渲染图表,也就是说,显示所有节点和链接的最终位置。
编辑
此方法只是简单地隐藏了模拟的动画部分。请参阅
========
'animation'实际上是模拟运行。可以玩模拟运行的时间,但这可能意味着节点卡在局部最小值 - see the documentation here for more details.
您可以选择向 end
事件添加侦听器,该事件在模拟完成时触发。我创建了一个片段,其中图表最初是隐藏的,然后在完成模拟后出现。
另一种方法是在服务器端渲染图表(如果这是一个选项),然后提供一个现成的 SVG,可以用 d3 进一步操作。
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
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))
.on('end', function() {
svg.classed('hidden', false)
d3.select('#loading').remove()
});
// I wasn't able to get the snippet to load the original data from https://bl.ocks.org/mbostock/raw/4062045/miserables.json so this is a copy hosted on glitch
d3.json("https://cdn.glitch.com/8e57a936-9a34-4e95-a03d-598e5738f44d%2Fmiserables.json", function(error, graph) {
if (error) {
console.log(error)
};
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) {
return color(d.group);
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) {
return d.id;
});
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.hidden {
visibility: hidden
}
img {
display: block;
margin-left: auto;
margin-right: auto;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<img id ="loading" src="http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif" />
<svg width="960" height="600" class="hidden"></svg>
尽管这个问题已经有了
我的回答提出了一个不同的解决方案,实际上不绘制动画。例如,您可以在 Mike Bostock(D3 创作者)的 this code 中看到它。
当您了解什么是 tick
函数时,这个解决方案很容易遵循:它只是一个计算模拟中所有位置并前进一步的函数。尽管绝大多数 D3 力导向图在每个刻度处绘制节点和链接,但您不需要这样做。
您可以执行以下操作:
使用
stop()
停止模拟,定义后立即停止:var simulation = d3.forceSimulation(graph.nodes) .force("link", d3.forceLink().id(function(d) { return d.id; })) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(width / 2, height / 2)) .stop();//stop the simulation here
模拟运行不画任何东西。这是最重要的一步:您不需要在每个刻度时移动元素。在这里,我 运行 宁 300 个刻度,这大约是默认数字:
for (var i = 0; i < 300; ++i) simulation.tick();
然后,只需使用模拟创建的属性(
x
、y
、source
、target
)绘制圆圈和行,一次:node.attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; })
以下是仅包含这些更改的链接块:http://bl.ocks.org/anonymous/8a4e4e2fed281ea5e2a5c804a9a03783/85ced3ea82a4bed20a2010530562b655d8f6e464
将此解决方案的时间与 "hiding-the-nodes" 解决方案(已接受的答案)的时间进行比较。这里的速度更快。在我的测试中,我得到了这个结果:
- "hiding-the-nodes"解法:5000ms左右
- 此解决方案:大约 200 毫秒
也就是快了 25 倍。
PS:为简单起见,我删除了分叉块中的 ticked
函数。如果要拖节点,直接加回去即可。
编辑 D3 v5.8
现在 D3 v5.8 允许将交互次数传递给 simulation.tick()
,您甚至不再需要 for
循环。所以,而不是:
for (var i = 0; i < 300; ++i) simulation.tick();
你可以这样做:
simulation.tick(300);