重构用这样的d3.js编写的代码的推荐方法是什么?

时间:2013-12-24 07:46:47

标签: javascript svg d3.js

这是代码。

var mapXOffset = 20;
var mapYOffset = 20;
var personSize = 4;
var redCount = 200;
var blueCount = 200;

function redraw() {
    var svg = d3.select("svg");
    var tempArray = makeRandomArray(0,10000, redCount+blueCount);
    var redArray = tempArray.slice(0,redCount);
    var blueArray = tempArray.slice(redCount,redCount+blueCount);

    var redData = svg.selectAll("rect.red")
        .data(redArray);
    var blueData = svg.selectAll("rect.blue")
        .data(blueArray);


    redData.enter().append("rect")
    .attr("x", function(d, i)
          {
          return mapXOffset + personSize * (d%100);
          })
    .attr("y", function(d)
          { 
          return mapYOffset + personSize * (d/100);
          })
    .attr("width", personSize)
    .attr("height", personSize)
    .attr("class", "red");

    blueData.enter().append("rect")
    .attr("x", function(d, i)
          {
          return mapXOffset + personSize * (d%100);
          })
    .attr("y", function(d)
          { 
          return mapYOffset + personSize * (d/100);
          })
    .attr("width", personSize)
    .attr("height", personSize)
    .attr("class", "blue");

    redData.transition()
    .duration(1000)
    .attr("x", function(d, i)
          {
          return mapXOffset + personSize * (d%100);
          })
    .attr("y", function(d)
          { 
          return mapYOffset + personSize * (d/100);
          });    

    blueData.transition()
    .duration(1000)
    .attr("x", function(d, i)
          {
          return mapXOffset + personSize * (d%100);
          })
    .attr("y", function(d)
          { 
          return mapYOffset + personSize * (d/100);
          });    

};

如图所示,除redData部分外,blueData.attr("class", "");的工作几乎相同。重构代码的方法更好:(1)将redundent代码包装到函数中(2)创建数组[redArray, BlueArray]并迭代它。或者其他方式更喜欢?

2 个答案:

答案 0 :(得分:1)

迈克博斯托克写了great article on reusable charts. 还有一本非常好的书叫Developing a D3.js Edge,我强烈推荐它(它很简短,易读)。

本质上,您希望使用getter-setter方法将图表实现为闭包,这使您可以根据需要重复使用图表代码,同时还可以为您提供修改现有图表对象属性的可配置性。

答案 1 :(得分:1)

看起来你可以简单地通过创建updateData()方法并从重绘中调用它来重构。

function updateData(selector, class, array) {

  var svg = d3.select("svg");
  var data = svg.selectAll(selector).data(array);

  data.enter().append('rect')
    .attr('x', function(d, i) {
      return mapXOffset + personSize * (d%100); })
    .attr('y', function(d) {
      return mapYOffset + personSize * (d/100); })
    .attr("width", personSize)
    .attr("height", personSize)
    .attr("class", class);

  data.transition()
    .duration(1000)
    .attr("x", function(d, i) {
      return mapXOffset + personSize * (d%100); })
    .attr("y", function(d) { 
      return mapYOffset + personSize * (d/100); });    
}


function redraw() {
    var tempArray = makeRandomArray(0,10000, redCount+blueCount);
    var redArray = tempArray.slice(0,redCount);
    var blueArray = tempArray.slice(redCount,redCount+blueCount);

    updataData('rect.red', 'red', redArray);
    updataData('rect.blue', 'blue', blueArray);
};

或者,您可以在svg中定义refresh并将其作为另一个参数传递。如果添加的参数太多,请考虑将它们作为对象文字传递。

您也可以迭代一个数组,但每个数组元素需要的参数多于redArrayblueArray。最终,它是基于哪种方法产生最简洁和/或可读代码的判断调用。

这就是我想象阵列版本的样子。它具有一定的吸引力,因为它不需要单独的函数定义。

function redraw() {
  var tempArray = makeRandomArray(0,10000, redCount+blueCount);
  var redArray = tempArray.slice(0,redCount);
  var blueArray = tempArray.slice(redCount,redCount+blueCount);

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

  var red = {selector: 'rect.red', class: 'red', array: redArray};
  var blue = {selector: 'rect.blue', class: 'blue', array: blueArray};

  [red, blue].forEach(function(item) {
    var data = svg.selectAll(item.selector).data(item.array);
    data.enter().append('rect')
      .attr('x', function(d, i) {
        return mapXOffset + personSize * (d%100); })
      .attr('y', function(d) {
        return mapYOffset + personSize * (d/100); })
      .attr("width", personSize)
      .attr("height", personSize)
      .attr("class", item.class);

    data.transition()
      .duration(1000)
      .attr("x", function(d, i) {
        return mapXOffset + personSize * (d%100); })
      .attr("y", function(d) { 
        return mapYOffset + personSize * (d/100); });    
  });
}