'点对点路径'd3可视化性能问题

时间:2018-09-12 05:52:39

标签: javascript performance d3.js transition

我已经通过代码https://bl.ocks.org/mbostock/1705868进行了'point-along-path'd3可视化。我注意到,当该点沿其路径移动时,它会消耗7-11%的CPU使用率。

在当前情况下,我大约有100条路径,并且在每条路径上,我都必须将点(圆)从源移动到目标。因此,随着更多点同时移动,它消耗了90%以上的CPU内存。

我尝试过:

                   function translateAlong(path) {
                      var l = path.getTotalLength();
                      return function(d, i, a) {
                          return function(t) {
                               var p = path.getPointAtLength(t * l);
                               return "translate(" + p.x + "," + p.y + ")";
                          };
                       };
                    }

                    // On each path(100 paths), we are moving circles from source to destination.
                    var movingCircle = mapNode.append('circle')
                        .attr('r', 2)
                        .attr('fill', 'white')

                    movingCircle.transition()
                        .duration(10000)
                        .ease("linear")
                        .attrTween("transform", translateAlong(path.node()))
                        .each("end", function() {
                            this.remove();
                        });

那么减少CPU使用率的更好方法应该是什么? 谢谢。

3 个答案:

答案 0 :(得分:3)

  

发布修改:

     
      
  • Here是默认设置。 (在2.7Ghz i7上100%的峰值CPU约为%99)
  •   
  • Here是我的版本。 (在2.7Ghz i7上,CPU峰值约20%可获得100点)
  •   
     

我平均快5倍。

我认为这里的瓶颈是每17毫秒对getPointAtLength方法的调用。如果需要的话,我也将避免冗长的字符串连接,但是在您的情况下,它的使用时间不长,所以我认为是最好的方法:

  • 预先缓存点,并且仅以给定的分辨率计算一次(在这里我分成了1000个部分)
  • 减少对requestAnimationFrame中的DOM方法的调用(您会看到该函数接收归一化的t参数)

在默认情况下,有2个呼叫,在呼叫getPointAtLength时有1个,而在设置translate(在后台)时有1个。

您可以将translateAlong替换为以下内容:

 function translateAlong(path){
    var points  = path.__points || collectPoints(path);
    return function (d,i){
        var transformObj = this.transform.baseVal[0];
        return function(t){
            var index = t * 1000 | 0,
                point = points[index];
            transformObj.setTranslate(point[0],point[1]);
        }
    }
}
function collectPoints(path) {
    var l = path.getTotalLength(),
        step = l*1e-3,
        points = [];
    for(var i = 0,p;i<=1000;++i){
        p = path.getPointAtLength(i*step);
        points.push([p.x,p.y]);
    }
    return path.__points = points;
}

对补间线进行了小的修改:

.tween("transform", translateAlong(path.node()))

不需要设置attr,调用它就足够了。结果如下:

http://jsfiddle.net/ibowankenobi/8kx04y29/

告诉我是否确实有所改善,因为我不确定100%。

答案 1 :(得分:1)

实现此目标的另一种方法可能是使用svg:animateMotion,您可以使用该docs沿给定路径移动元素。这是来自{{3}}的示例。本质上您想要:

<svg>
  <path id="path1" d="...">
    <circle cx="" cy="" r="10" fill="red">
      <animateMotion dur="10s" repeatCount="0">
        <mpath xlink:href="#path1" />
      </animateMotion>
    </circle>
  </path>
</svg>

我没有介绍它,但是我认为您要比使用SVG本身内置的东西要难得多。

浏览器支持

请注意,在@ibrahimtanyalcin发表评论后,我开始检查浏览器的兼容性。事实证明,IE或Microsoft Edge不支持此功能。

答案 2 :(得分:-1)

在我的计算机上:

  • @mbostock使用8%的CPU。
  • @ibrahimtanyalcin使用11%的CPU。
  • @Ian使用10%的CPU。

对于@mbostock和@ibrahimtanyalcin,这意味着transitiontransform更新使用CPU。

如果我将100个这些动画放入1个SVG中,我会得到

  • @mbostock使用50%的CPU。 (1个核心已满)
  • @Ian使用40%的CPU。

所有动画看起来都很流畅。

一种可能性是使用https://stackoverflow.com/a/39914235/9938317transform更新功能中添加睡眠

修改

在不错的Andrew Reid answer中,我发现了一些优化方法。

我编写了一个Canvas + JS 100 000测试版本,该测试仅负责计算部分并计算在4000毫秒内可以执行的迭代次数。 order=falset的一半时间。

我已经编写了自己的随机数生成器,以确保每次运行修改后的代码时都得到相同的随机数。

代码的块版本: 200次迭代

根据parseInt的文档

  

parseInt不应替代Math.floor()

将数字转换为字符串,然后将字符串解析到.听起来不是很有效。

parseInt()替换Math.floor():218次迭代

我还发现一行没有功能并且看起来并不重要

let p = new Int16Array(2);

它在内部while循环中。

用这行代替

let p;

给出 300次迭代

使用这些修改,代码可以以60 Hz的帧频处理更多点。

我尝试了其他一些方法,但是结果却变慢了。

令我惊讶的是,如果我预先计算段的长度并将segment的计算简化为单纯的数组查找,它比进行4个数组查找和几个Math.pow的速度要慢以及添加项和Math.sqrt