在d3上重复使用方法获得更好的性能

时间:2017-09-01 04:33:45

标签: javascript performance d3.js

例如,我需要为每个attr计算我的数据的Math.sqrt,如何只计算一次Math.sqrt(d)?

var circle = svgContainer.data(dataJson).append("ellipse")
    .attr("cx", function(d) {
        return Math.sqrt(d) + 1
    })
    .attr("cy", function(d) {
        return Math.sqrt(d) + 2
    })
    .attr("rx", function(d) {
        return Math.sqrt(d) + 3
    })
    .attr("ry", function(d) {
        return Math.sqrt(d) + 4
    });

有任何优雅/表演模式吗?我这样想:

var aux;
var circle = svgContainer.data(dataJson).append("ellipse")
    .attr("cx", function(d) {
        aux = Math.sqrt(d);
        return aux + 1
    })
    .attr("cy", function(d) {
        return aux + 2
    })
    .attr("rx", function(d) {
        return aux + 3
    })
    .attr("ry", function(d) {
        return aux + 4
    });

2 个答案:

答案 0 :(得分:2)

D3的低估特性是版本4引入的local variables概念。这些变量允许您在节点上存储信息(这就是它被称为 local )独立于可能已绑定到该节点的数据。您不必膨胀数据来存储其他信息。

  

D3 locals允许您定义独立于数据的本地状态。

使用局部变量优于其他方法的主要优点可能是它可以顺利地融入经典的D3方法;没有必要引入另一个循环来保持代码清洁。

使用局部变量来存储预先计算的值可能是人们可以想象的最简单的用例。另一方面,它完美地说明了D3的局部变量是什么:Store一些复杂的信息,可能需要繁重的工作才能在节点上本地创建,retrieve稍后在您的代码中进一步使用。

无耻地复制并调整Gerardo的代码答案,解决方案可以像这样实现:



var svg = d3.select("svg");

var data = d3.range(100, 1000, 100);

var roots = d3.local();   // This is the instance where our square roots will be stored

var ellipses = svg.selectAll(null)
  .data(data)
  .enter()
  .append("ellipse")
  .attr("fill", "gainsboro")
  .attr("stroke", "darkslateblue")
  .attr("cx", function(d) {
    return roots.set(this, Math.sqrt(d)) * 3;  // Calculate and store the square root
  })
  .attr("cy", function(d) {
    return roots.get(this) * 3;                // Retrieve the previously stored root
  })
  .attr("rx", function(d) {
    return roots.get(this) + 3;                // Retrieve the previously stored root
  })
  .attr("ry", function(d) {
    return roots.get(this) + 4;                // Retrieve the previously stored root
  });

<script src="//d3js.org/d3.v4.min.js"></script>
<svg></svg>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

在D3中执行此操作的最常用方法可能是使用selection.each,其中:

  

按顺序为每个选定的元素调用指定的函数,传递当前数据(d),当前索引(i)和当前组(节点),并将其作为当前DOM元素(节点[i] ])。

所以,在你的情况下:

circle.each(function(d){

    //calculates the value just once for each datum:
    var squareRoot = Math.sqrt(d)

    //now use that value in the DOM element, which is 'this':
    d3.select(this).attr("cx", squareRoot)
        .attr("cy", squareRoot)
        //etc...

    });

这是一个演示:

&#13;
&#13;
var svg = d3.select("svg");

var data = d3.range(100, 1000, 100);

var ellipses = svg.selectAll(null)
  .data(data)
  .enter()
  .append("ellipse")
  .attr("fill", "gainsboro")
  .attr("stroke", "darkslateblue")
  .each(function(d) {
    var squareRoot = Math.sqrt(d);
    d3.select(this)
      .attr("cx", function(d) {
        return squareRoot * 3
      })
      .attr("cy", function(d) {
        return squareRoot * 3
      })
      .attr("rx", function(d) {
        return squareRoot + 3
      })
      .attr("ry", function(d) {
        return squareRoot + 4
      });
  })
&#13;
<script src="//d3js.org/d3.v4.min.js"></script>
<svg></svg>
&#13;
&#13;
&#13;

D3代码中的另一种常见方法是在第一个attr方法中设置一个新的数据属性,并在后面检索它:

.attr("cx", function(d) {
        //set a new property here
        d.squareRoot = Math.sqrt(d.value);
        return d.squareRoot * 3
    })
    .attr("cy", function(d) {
        //retrieve it here
        return d.squareRoot * 3
    })
    //etc...

这样你每个元素只执行一次计算。

以下是演示:

&#13;
&#13;
var svg = d3.select("svg");

var data = d3.range(100, 1000, 100).map(function(d) {
  return {
    value: d
  }
});

var ellipses = svg.selectAll(null)
  .data(data)
  .enter()
  .append("ellipse")
  .attr("fill", "gainsboro")
  .attr("stroke", "darkslateblue")
  .attr("cx", function(d) {
    d.squareRoot = Math.sqrt(d.value);
    return d.squareRoot * 3
  })
  .attr("cy", function(d) {
    return d.squareRoot * 3
  })
  .attr("rx", function(d) {
    return d.squareRoot + 3
  })
  .attr("ry", function(d) {
    return d.squareRoot + 4
  });
&#13;
<script src="//d3js.org/d3.v4.min.js"></script>
<svg></svg>
&#13;
&#13;
&#13;

PS :顺便说一句,使用var aux的解决方案无效。试试吧,你会看到。