当我在d3应用程序中使用旋转功能时,整个应用程序会慢慢爬行。
作为示例:如果取消注释// var angle = 0;在以下jsfiddle中,它的运行速度提高了20倍。
这是为什么?轮换是非常昂贵还是我做错了什么?
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; });
linktext.attr("transform", function(d) {
var xDiff = d.source.x - d.target.x;
var yDiff = d.source.y - d.target.y;
var angle = Math.atan2(yDiff, xDiff) * (180.0 / Math.PI);
//var angle = 0;
return "translate(" + (d.source.x + d.target.x) / 2 + ","
+ (d.source.y + d.target.y) / 2 + ")rotate(" + angle + ")";
});
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
注意:我修改了原始的jsfiddle here
答案 0 :(得分:2)
为了追踪问题的根源,我开始尝试使文本的各个方面相同/不同。见this version of your fiddle。请注意,文本是不同的,并且每个文本元素的角度都不同(因此不可能进行优化)但是每个元素的角度是恒定的 - 它不会在每个滴答上发生变化。
结果呢?起初稍微迟缓(当图中有很多重叠时),但它很快就会以30fps的速度平滑动画。
同样如此(最终帧速率略高于30fps)即使文本内容每次更改都会改变,例如this version。
这与the usual rule of optimizing animation相矛盾,改变转换应该比改变内容更有效。
根据Chrome的帧速率检查器,your original fiddle每次重新绘制时消耗的大部分时间(我的计算机上的时钟频率约为4fps)正被" Paint设置&#占用34;步骤 - 即,计算每个"层"图像。
This blog has a quick-and-easy recap of the different steps of a repaint。引用:
以下步骤将DOM中的元素渲染为屏幕上的图像:
触发 - 元素已加载到DOM中,或以某种方式进行修改
重新计算样式 - 样式应用于元素(或重新计算)
布局 - 元素根据其在屏幕上的位置以几何方式布局
绘制设置 - DOM被分割为渲染层,用于填充每个元素的像素
绘画 - 每个图层都绘制成位图,并通过软件光栅化工具上传到GPU作为纹理
- 醇>
复合图层 - 图层由GPU合成并绘制到最终屏幕图像
通常情况下,GPU可以在最终的"组合中有效地完成转换。步骤(现代操作系统上的现代浏览器将自动将工作转移到GPU)。
有两个原因可能导致这种情况发生。首先,这种优化可能甚至不适用于SVG(尽管我非常确定最新Chrome的默认设置是优化SVG转换)。但是,即使浏览器对SVG转换使用了一些GPU优化,您的GPU也只能在内存不足之前处理有限数量的层。有近200个单独转换的文本元素(以及上下分层的未转换内容),这可能是一个瓶颈。请参阅this HTML5Rocks post或this MSDN article,其中提供了一些取消独立图层构成的性能限制示例。
无论发生什么事情,最终的结果是你的CPU,而不是你的GPU,每次都在计算旋转并将文本分层,这样做效率不高。
那么,你能做些什么呢?
我尝试使用矩阵变换优化代码,而不是先计算角度,然后让浏览器计算旋转度(see live version)......但这并没有产生明显的差异。更改为简单的倾斜变换而不是旋转有一点帮助(帧速率高达11fps),但这只是在滞后动画之上添加了丑陋的文本。
不幸的是,看起来你真的不得不以某种方式妥协。一些选择:
隐藏文本,直到强制布局停止,然后才计算旋转。 Working example
密钥代码(Javascript):
var vis = d3.select(".intgraph").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.on("click", function(){
if ( force.alpha() )
force.stop();
else
force.resume();
});
force.on("start", function(){
vis.classed("running", true);
})
.on("end", function () {
linktext.attr("transform", function (d) {
var xDiff = d.source.x - d.target.x,
xMid = d.source.x - xDiff / 2;
var yDiff = d.source.y - d.target.y,
yMid = d.source.y - yDiff / 2;
var hyp = Math.sqrt(xDiff * xDiff + yDiff * yDiff),
cos = xDiff / hyp,
sin = yDiff / hyp;
return "matrix(" +
[cos, sin, -sin, cos, xMid, yMid] + ")";
});
vis.classed("running", false);
});
CSS:
.running text {
display:none;
}
显示文字,但不要旋转它(可选择在强制布局停止时将其旋转放置,如上所述)。
答案 1 :(得分:0)
执行//var angle = 0;
时,此代码的编译器可能会意识到结果毫无意义并且已经过优化,因为它永远不会更改。这可以解释为什么在评论该代码时运行速度快20倍。
我怀疑即使你保留了翻译部分并删除了旋转它也会慢一点(尽管翻译的计算成本远低于旋转,因为与旋转相比,翻译只有3次加法)。
虽然我对d3.js不是很熟悉,但你现在似乎正在执行一个函数中的转换和旋转,这个函数可能会在cpu上被多次调用。 (通常你会想通过着色器在gpu上做这个 - 虽然我不确定是否/如何适用于d3.js)。