d3:旋转和力导向布局

时间:2014-05-02 14:06:54

标签: javascript d3.js rotation force-layout

当我在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

2 个答案:

答案 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中的元素渲染为屏幕上的图像:

     
      
  1. 触发 - 元素已加载到DOM中,或以某种方式进行修改

  2.   
  3. 重新计算样式 - 样式应用于元素(或重新计算)

  4.   
  5. 布局 - 元素根据其在屏幕上的位置以几何方式布局

  6.   
  7. 绘制设置 - DOM被分割为渲染层,用于填充每个元素的像素

  8.   
  9. 绘画 - 每个图层都绘制成位图,并通过软件光栅化工具上传到GPU作为纹理

  10.   
  11. 复合图层 - 图层由GPU合成并绘制到最终屏幕图像

  12.   

通常情况下,GPU可以在最终的"组合中有效地完成转换。步骤(现代操作系统上的现代浏览器将自动将工作转移到GPU)。

有两个原因可能导致这种情况发生。首先,这种优化可能甚至不适用于SVG(尽管我非常确定最新Chrome的默认设置是优化SVG转换)。但是,即使浏览器对SVG转换使用了一些GPU优化,您的GPU也只能在内存不足之前处理有限数量的层。有近200个单独转换的文本元素(以及上下分层的未转换内容),这可能是一个瓶颈。请参阅this HTML5Rocks postthis 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)。