Getting a better performance on repeatedly method on d3

后端 未结 2 432
孤街浪徒
孤街浪徒 2021-01-15 03:44

For example, I need to calculate a Math.sqrt of my data for each attr, how can I calculate only one time the Math.sqrt(d)?

var circle = svgContainer.data(dat         


        
2条回答
  •  鱼传尺愫
    2021-01-15 04:33

    Probably, the most idiomatic way for doing this in D3 is using selection.each, which:

    Invokes the specified function for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]).

    So, in your case:

    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...
    
        });
    

    Here is a demo:

    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
          });
      })
    
    

    Another common approach in D3 codes is setting a new data property in the first attr method, and retrieving it latter:

    .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...
    

    That way you also perform the calculation only once per element.

    Here is the demo:

    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
      });
    
    

    PS: by the way, your solution with var aux will not work. Try it and you'll see.

提交回复
热议问题