使用 springy.js 加载多个图表时闪烁 canvas
Flickering canvas when loading multiple graphs with springy.js
使用 springy.js 将新图形渲染到 canvas 时,在鼠标悬停时,新图形和之前加载的旧图形之间出现闪烁。
要重现 运行 下面的代码片段并单击更改图表按钮,然后将鼠标悬停在 canvas。
function drawGraph(data) {
var graph = new Springy.Graph();
graph.loadJSON(data);
$('#graph-canvas').springy({
graph: graph
});
}
var graphs = [{
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#0000ff'}],
['b', 'd', {color: '#0000ff'}],
['c', 'b', {color: '#0000ff'}],
['d', 'a', {color: '#0000ff'}],
['c', 'a', {color: '#0000ff'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#00ff00'}],
['b', 'a', {color: '#00ff00'}],
['c', 'e', {color: '#00ff00'}],
['d', 'a', {color: '#00ff00'}],
['e', 'b', {color: '#00ff00'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e', 'f'],
edges: [
['a', 'd', {color: '#ff0000'}],
['b', 'e', {color: '#ff0000'}],
['c', 'f', {color: '#ff0000'}],
['d', 'f', {color: '#ff0000'}],
['e', 'd', {color: '#ff0000'}]
]
}]
drawGraph(graphs[2]);
$('#draw-graph').click(function() {
drawGraph(graphs[Math.floor(Math.random() * graphs.length)])
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springyui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springy.js"></script>
<canvas id="graph-canvas" width=600 height=600>
</canvas>
<button id="draw-graph">
change graph
</button>
两种解决方案
正确的方法
我写了一个答案,但现在我正要提交它,我意识到这是一个糟糕的解决方案。
答案应该是..如果你想改变你需要重新加载页面的图表,因为这似乎是 Springy.js 作者的设计意图。
或者Jquery可能有办法?
如果重新加载页面不切实际,以下是原始答案
丑陋的方式
快速查看代码并假设您提到的闪烁是在不同图形之间切换。
对于那些稍有 canvas 经验的人来说,其原因是显而易见的。您有两个或更多渲染 运行,每个渲染都在请求一个渲染帧,浏览器将呈现它认为正确的帧。结果是在两个渲染之间闪烁。
我不使用 JQuery 所以解决方案很可能位于 JQuery 如果它有办法取消引用插件和所有关联。
然后我想解决方案就是停止渲染器。因此,我快速查看了 Springy
的原型(使用 console.log(graph)
将其放入控制台),但发现没有任何东西可以通过简单的调用来完成。
您不能仅通过覆盖或删除来取消引用图形,因为它会保持其引用,因为它会不断调用 requestAnimationFrame()
所以通过查看源代码,很明显 springy.js 没有提供通用的重置或停止,以便可以取消引用并重新启动。 (作者应该考虑提供的东西)
所以我想停止它的方法是访问 Springy.eventListeners
数组。迭代该数组和任何具有方法 stop
的东西都可以调用它。完成后,您可以安全地取消引用 Springy 并创建一个新的。运气不好:(
强制错误。
关于下面示例代码的注释
注 1
我写了上面的代码然后尝试了代码并且调用 stop 没有效果所以需要更激烈的操作。下面的代码仍然包括不错的尝试(加上调试的东西)但是在停止调用之后我不得不通过用牛替换所需的函数来强制它停止,对不起我的意思是字符串。这让它停止了
注 2
我已经使用超时让 Springy 有时间崩溃,但渲染器可能没有使用方法 drawNode
因此不会崩溃。
解决闪烁的Demo
var graph; // needs to be global or in scope
function drawGraph(data) {
if(graph !== undefined){ // it already exists so must be stopped and dereferenced.
// safely find eventListeners and iterate
if(graph.eventListeners !== undefined && typeof graph.eventListeners.forEach === "function"){
graph.eventListeners.forEach(function(listener){
console.log(listener); // debug info
if(typeof listener.stop === "function"){
listener.stop();
// See above SO answer NOTE 1
// OK this did not stop it. So time to poke it in the eye
// forcing it to error by replacing a function
if(typeof listener.drawNode === "function"){
listener.drawNode = "have a cow";
}
}
})
// I am not sure if this needs the next frame or not so am playing it safe
// by giving it a little time 1/4 second to settle
setTimeout(function(){
// I am not a JQuery user so not sure if this will upset it
// remove it from the DOM
var canvas = document.getElementById("graph-canvas");
if(canvas !== null){
canvas.springy = undefined;
}
graph = new Springy.Graph();
graph.loadJSON(data);
$('#graph-canvas').springy({
graph: graph
});
}, 250); // do this in 250 ms may require more time see SO NOTE 2
}else{
// throw if nothing can be done.
throw new Error("Unable to stop springy.");
}
}else{ // first time so nothing to do but standard start
graph = new Springy.Graph();
graph.loadJSON(data);
$('#graph-canvas').springy({
graph: graph
});
}
}
var graphs = [{
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#0000ff'}],
['b', 'd', {color: '#0000ff'}],
['c', 'b', {color: '#0000ff'}],
['d', 'a', {color: '#0000ff'}],
['c', 'a', {color: '#0000ff'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#00ff00'}],
['b', 'a', {color: '#00ff00'}],
['c', 'e', {color: '#00ff00'}],
['d', 'a', {color: '#00ff00'}],
['e', 'b', {color: '#00ff00'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e', 'f'],
edges: [
['a', 'd', {color: '#ff0000'}],
['b', 'e', {color: '#ff0000'}],
['c', 'f', {color: '#ff0000'}],
['d', 'f', {color: '#ff0000'}],
['e', 'd', {color: '#ff0000'}]
]
}]
drawGraph(graphs[2]);
$('#draw-graph').click(function() {
drawGraph(graphs[Math.floor(Math.random() * graphs.length)])
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springyui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springy.js"></script>
<canvas id="graph-canvas" width=600 height=600>
</canvas>
<button id="draw-graph">
change graph
</button>
好的,在我的机器和浏览器上停止了它 (chrome),但为了安全起见,联系 Springy 的作者(如果这是 public)以了解如何停止是值得的它作为文档和源代码根本没有说明。
要解决您看到的问题,请不要为每个图表创建新的 graph
、layout
和 renderer
,只需清除图表然后加载新数据即可。
var renderer;
function drawGraph(data) {
if (renderer) {
renderer.graph.edges.slice().map(function(edge) {
renderer.graph.removeEdge(edge);
});
renderer.graph.nodes.slice().map(function(node) {
renderer.graph.removeNode(node);
});
renderer.graph.loadJSON(data);
} else {
var graph = new Springy.Graph();
graph.loadJSON(data);
renderer = $('#graph-canvas').springy({
graph: graph
});
}
}
var graphs = [{
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#0000ff'}],
['b', 'd', {color: '#0000ff'}],
['c', 'b', {color: '#0000ff'}],
['d', 'a', {color: '#0000ff'}],
['c', 'a', {color: '#0000ff'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#00ff00'}],
['b', 'a', {color: '#00ff00'}],
['c', 'e', {color: '#00ff00'}],
['d', 'a', {color: '#00ff00'}],
['e', 'b', {color: '#00ff00'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e', 'f'],
edges: [
['a', 'd', {color: '#ff0000'}],
['b', 'e', {color: '#ff0000'}],
['c', 'f', {color: '#ff0000'}],
['d', 'f', {color: '#ff0000'}],
['e', 'd', {color: '#ff0000'}]
]
}]
drawGraph(graphs[2]);
$('#draw-graph').click(function() {
drawGraph(graphs[Math.floor(Math.random() * graphs.length)])
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springyui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springy.js"></script>
<canvas id="graph-canvas" width=600 height=600>
</canvas>
<button id="draw-graph">
change graph
</button>
使用 springy.js 将新图形渲染到 canvas 时,在鼠标悬停时,新图形和之前加载的旧图形之间出现闪烁。
要重现 运行 下面的代码片段并单击更改图表按钮,然后将鼠标悬停在 canvas。
function drawGraph(data) {
var graph = new Springy.Graph();
graph.loadJSON(data);
$('#graph-canvas').springy({
graph: graph
});
}
var graphs = [{
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#0000ff'}],
['b', 'd', {color: '#0000ff'}],
['c', 'b', {color: '#0000ff'}],
['d', 'a', {color: '#0000ff'}],
['c', 'a', {color: '#0000ff'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#00ff00'}],
['b', 'a', {color: '#00ff00'}],
['c', 'e', {color: '#00ff00'}],
['d', 'a', {color: '#00ff00'}],
['e', 'b', {color: '#00ff00'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e', 'f'],
edges: [
['a', 'd', {color: '#ff0000'}],
['b', 'e', {color: '#ff0000'}],
['c', 'f', {color: '#ff0000'}],
['d', 'f', {color: '#ff0000'}],
['e', 'd', {color: '#ff0000'}]
]
}]
drawGraph(graphs[2]);
$('#draw-graph').click(function() {
drawGraph(graphs[Math.floor(Math.random() * graphs.length)])
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springyui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springy.js"></script>
<canvas id="graph-canvas" width=600 height=600>
</canvas>
<button id="draw-graph">
change graph
</button>
两种解决方案
正确的方法
我写了一个答案,但现在我正要提交它,我意识到这是一个糟糕的解决方案。
答案应该是..如果你想改变你需要重新加载页面的图表,因为这似乎是 Springy.js 作者的设计意图。
或者Jquery可能有办法?
如果重新加载页面不切实际,以下是原始答案
丑陋的方式
快速查看代码并假设您提到的闪烁是在不同图形之间切换。
对于那些稍有 canvas 经验的人来说,其原因是显而易见的。您有两个或更多渲染 运行,每个渲染都在请求一个渲染帧,浏览器将呈现它认为正确的帧。结果是在两个渲染之间闪烁。
我不使用 JQuery 所以解决方案很可能位于 JQuery 如果它有办法取消引用插件和所有关联。
然后我想解决方案就是停止渲染器。因此,我快速查看了 Springy
的原型(使用 console.log(graph)
将其放入控制台),但发现没有任何东西可以通过简单的调用来完成。
您不能仅通过覆盖或删除来取消引用图形,因为它会保持其引用,因为它会不断调用 requestAnimationFrame()
所以通过查看源代码,很明显 springy.js 没有提供通用的重置或停止,以便可以取消引用并重新启动。 (作者应该考虑提供的东西)
所以我想停止它的方法是访问 Springy.eventListeners
数组。迭代该数组和任何具有方法 stop
的东西都可以调用它。完成后,您可以安全地取消引用 Springy 并创建一个新的。运气不好:(
强制错误。
关于下面示例代码的注释 注 1
我写了上面的代码然后尝试了代码并且调用 stop 没有效果所以需要更激烈的操作。下面的代码仍然包括不错的尝试(加上调试的东西)但是在停止调用之后我不得不通过用牛替换所需的函数来强制它停止,对不起我的意思是字符串。这让它停止了
注 2
我已经使用超时让 Springy 有时间崩溃,但渲染器可能没有使用方法 drawNode
因此不会崩溃。
解决闪烁的Demo
var graph; // needs to be global or in scope
function drawGraph(data) {
if(graph !== undefined){ // it already exists so must be stopped and dereferenced.
// safely find eventListeners and iterate
if(graph.eventListeners !== undefined && typeof graph.eventListeners.forEach === "function"){
graph.eventListeners.forEach(function(listener){
console.log(listener); // debug info
if(typeof listener.stop === "function"){
listener.stop();
// See above SO answer NOTE 1
// OK this did not stop it. So time to poke it in the eye
// forcing it to error by replacing a function
if(typeof listener.drawNode === "function"){
listener.drawNode = "have a cow";
}
}
})
// I am not sure if this needs the next frame or not so am playing it safe
// by giving it a little time 1/4 second to settle
setTimeout(function(){
// I am not a JQuery user so not sure if this will upset it
// remove it from the DOM
var canvas = document.getElementById("graph-canvas");
if(canvas !== null){
canvas.springy = undefined;
}
graph = new Springy.Graph();
graph.loadJSON(data);
$('#graph-canvas').springy({
graph: graph
});
}, 250); // do this in 250 ms may require more time see SO NOTE 2
}else{
// throw if nothing can be done.
throw new Error("Unable to stop springy.");
}
}else{ // first time so nothing to do but standard start
graph = new Springy.Graph();
graph.loadJSON(data);
$('#graph-canvas').springy({
graph: graph
});
}
}
var graphs = [{
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#0000ff'}],
['b', 'd', {color: '#0000ff'}],
['c', 'b', {color: '#0000ff'}],
['d', 'a', {color: '#0000ff'}],
['c', 'a', {color: '#0000ff'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#00ff00'}],
['b', 'a', {color: '#00ff00'}],
['c', 'e', {color: '#00ff00'}],
['d', 'a', {color: '#00ff00'}],
['e', 'b', {color: '#00ff00'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e', 'f'],
edges: [
['a', 'd', {color: '#ff0000'}],
['b', 'e', {color: '#ff0000'}],
['c', 'f', {color: '#ff0000'}],
['d', 'f', {color: '#ff0000'}],
['e', 'd', {color: '#ff0000'}]
]
}]
drawGraph(graphs[2]);
$('#draw-graph').click(function() {
drawGraph(graphs[Math.floor(Math.random() * graphs.length)])
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springyui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springy.js"></script>
<canvas id="graph-canvas" width=600 height=600>
</canvas>
<button id="draw-graph">
change graph
</button>
好的,在我的机器和浏览器上停止了它 (chrome),但为了安全起见,联系 Springy 的作者(如果这是 public)以了解如何停止是值得的它作为文档和源代码根本没有说明。
要解决您看到的问题,请不要为每个图表创建新的 graph
、layout
和 renderer
,只需清除图表然后加载新数据即可。
var renderer;
function drawGraph(data) {
if (renderer) {
renderer.graph.edges.slice().map(function(edge) {
renderer.graph.removeEdge(edge);
});
renderer.graph.nodes.slice().map(function(node) {
renderer.graph.removeNode(node);
});
renderer.graph.loadJSON(data);
} else {
var graph = new Springy.Graph();
graph.loadJSON(data);
renderer = $('#graph-canvas').springy({
graph: graph
});
}
}
var graphs = [{
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#0000ff'}],
['b', 'd', {color: '#0000ff'}],
['c', 'b', {color: '#0000ff'}],
['d', 'a', {color: '#0000ff'}],
['c', 'a', {color: '#0000ff'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e'],
edges: [
['a', 'e', {color: '#00ff00'}],
['b', 'a', {color: '#00ff00'}],
['c', 'e', {color: '#00ff00'}],
['d', 'a', {color: '#00ff00'}],
['e', 'b', {color: '#00ff00'}]
]
}, {
nodes: ['a', 'b', 'c', 'd', 'e', 'f'],
edges: [
['a', 'd', {color: '#ff0000'}],
['b', 'e', {color: '#ff0000'}],
['c', 'f', {color: '#ff0000'}],
['d', 'f', {color: '#ff0000'}],
['e', 'd', {color: '#ff0000'}]
]
}]
drawGraph(graphs[2]);
$('#draw-graph').click(function() {
drawGraph(graphs[Math.floor(Math.random() * graphs.length)])
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springyui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/springy/2.7.1/springy.js"></script>
<canvas id="graph-canvas" width=600 height=600>
</canvas>
<button id="draw-graph">
change graph
</button>