【问题标题】:d3: rotations and force directed layoutsd3:旋转和力导向布局
【发布时间】:2014-05-02 14:06:53
【问题描述】:

当我在我的 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; });
}

注意:我修改了找到here的原始jsfiddle

【问题讨论】:

  • 这里(Chrome)并没有那么糟糕,但是有一个明显的区别。不过,您实际上无能为力——文本的旋转非常昂贵,因为它不是一个简单的形状。
  • 但这似乎与单个文本旋转的处理更相关,因为如果您将角度设置为非零,它仍然非常快。

标签: javascript d3.js rotation force-layout


【解决方案1】:

为了追查问题的根源,我尝试使文本的各个方面相同/不同。见this version of your fiddle。请注意,文本不同,每个文本元素的角度也不同(因此无法优化)但是每个元素的角度是恒定的——它不会在每个刻度上改变。

结果?一开始有点迟钝(当图表中有很多重叠时),但它很快就会进展到 30fps 以下的平滑动画。

同样如此(最终帧速率略高于 30fps),即使文本内容每刻都在变化,如 this version。

这与 the usual rule of optimizing animation 相矛盾,即改变转换应该比改变内容更有效。

根据 Chrome 帧率检查器,your original fiddle 每次重绘所消耗的大部分时间(在我的计算机上以大约 4fps 的速度运行)都被“绘制设置”步骤占用——即,在计算图像的每个“层”。

This blog has a quick-and-easy recap of the different steps of a repaint。引用:

以下步骤将 DOM 中的元素渲染为屏幕上的图像:

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

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

  3. 布局 - 元素根据它们在屏幕上的位置进行几何布局

  4. Paint Setup - DOM 被分割成渲染层,用于填充每个元素的像素

  5. 绘画 - 每一层都被绘制成位图,并通过软件光栅化器作为纹理上传到 GPU

  6. 合成层 - 这些层由 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;
    }
    
  • 显示文本,但不要旋转它(或者,在强制布局停止时将其旋转到位,如上所述)。

【讨论】:

    【解决方案2】:

    当您执行//var angle = 0; 时,此代码的编译器可能会意识到结果毫无意义并被优化,因为它永远不会改变。这可以解释为什么在注释该代码时它的运行速度要快 20 倍。

    我怀疑即使您保留了平移部分并删除了旋转,它也会更慢(尽管平移的计算成本明显低于旋转,因为与旋转相比,平移只需 3 次加法)。

    虽然我对 d3.js 不太熟悉,但您现在似乎正在执行一个可能在 cpu 上多次调用的函数中的平移和旋转。 (通常你会希望通过着色器在 gpu 上执行此操作 - 尽管我不确定这是否/如何适用于 d3.js)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-26
      • 2015-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-02
      • 1970-01-01
      相关资源
      最近更新 更多