【问题标题】:D3 transition from stacked bar to bar chart only works the first timeD3 从堆积条形图到条形图的过渡仅在第一次有效
【发布时间】:2017-12-17 05:27:48
【问题描述】:

我正在尝试创建两个图表,一个堆叠条形图和一个条形图,每个图表都显示不同的数据集。但是当我点击一个按钮时,我想从堆积条形图转换到条形图,反之亦然。我放在一起的代码只在第一次工作,然后当我想从条形图转换回堆叠条形图时,所有条形以单个条形的形式堆叠在一起。有人可以指出我如何从条形图转换回堆叠条形图的正确方向吗?任何帮助,将不胜感激。 (我还没有真正弄乱轴,所以它们没有改变是正常的)。

这里是它当前外观的链接:https://jhjanicki.github.io/stackbartobar/

下面是我的代码:

var value = 'stack';

var data = [{
  name: "Shihuahuaco",
  value: 1067,
  china: 772
}, {
  name: "Cachimbo",
  value: 283,
  china: 1
}, {
  name: "Estoraque",
  value: 204,
  china: 150
}, {
  name: "Cumala",
  value: 154,
  china: 0
}, {
  name: "Ishpingo",
  value: 108,
  china: 3
}, {
  name: "Huayruro",
  value: 108,
  china: 1
}, {
  name: "Tornillo",
  value: 61,
  china: 4
}, {
  name: "Congona",
  value: 54,
  china: 0
}, {
  name: "Capirona",
  value: 37,
  china: 5
}, {
  name: "Tahuari",
  value: 33,
  china: 14
}, {
  name: "Marupa",
  value: 33,
  china: 1
}, {
  name: "Quinilla",
  value: 28,
  china: 4
}, {
  name: "Azucar huayo",
  value: 22,
  china: 15
}, {
  name: "Protium sp.",
  value: 19,
  china: 0
}, {
  name: "Nogal",
  value: 15,
  china: 6
}, {
  name: "Ana Caspi",
  value: 14,
  china: 2
}, {
  name: "Cedro",
  value: 14,
  china: 0
}, {
  name: "Carapa guianensis",
  value: 12,
  china: 0
}, {
  name: "Leche caspi",
  value: 12,
  china: 0
}, {
  name: "Andiroba",
  value: 11,
  china: 0
}, {
  name: "Copaiba",
  value: 7,
  china: 4
}, {
  name: "Palo baston",
  value: 6,
  china: 0
}, {
  name: "Moena",
  value: 5,
  china: 0
}, {
  name: "Almendro",
  value: 5,
  china: 0
}, {
  name: "Chancaquero",
  value: 4,
  china: 0
}, {
  name: "Caimitillo",
  value: 3,
  china: 1
}, {
  name: "Nogal amarillo",
  value: 3,
  china: 0
}, {
  name: "Couma macrocarpa",
  value: 3,
  china: 0
}, {
  name: "Tulpay",
  value: 3,
  china: 0
}, {
  name: "Carapa",
  value: 3,
  china: 0
}, {
  name: "Dacryodes olivifera",
  value: 2,
  china: 0
}, {
  name: "Capinuri",
  value: 2,
  china: 2
}, {
  name: "Brosimum alicastrum",
  value: 2,
  china: 0
}, {
  name: "Paramachaerium ormosioide",
  value: 2,
  china: 0
}, {
  name: "Brosimum sp.",
  value: 2,
  china: 0
}, {
  name: "Manchinga",
  value: 2,
  china: 0
}];
// data for stacked bar

var points = [{
    'lon': 105.3,
    'lat': 33.5,
    'name': 'China',
    'GTF': 1024,
    "ID": "CHN"
  },
  {
    'lon': -70.9,
    'lat': 18.8,
    'name': 'Dominican Republic',
    'GTF': 470,
    "ID": "DOM"
  },
  {
    'lon': -101,
    'lat': 38,
    'name': 'USA',
    'GTF': 248,
    "ID": "USA"
  },
  {
    'lon': -102.5,
    'lat': 22.7,
    'name': 'Mexico',
    'GTF': 220,
    "ID": "MEX"
  },
  {
    'lon': 2.98,
    'lat': 46,
    'name': 'France',
    'GTF': 85,
    "ID": "FRA"
  }
];
//data for bar



var margin = {
    top: 20,
    right: 30,
    bottom: 150,
    left: 60
  },
  widthB = 700 - margin.left - margin.right,
  heightB = 500 - margin.top - margin.bottom;


var dataIntermediate = ['value', 'china'].map(function(key, i) {
  return data.map(function(d, j) {
    return {
      x: d['name'],
      y: d[key]
    };
  })
})

var dataStackLayout = d3.layout.stack()(dataIntermediate);


var svg = d3.select("#chart").append("svg")
  .attr("width", widthB + margin.left + margin.right)
  .attr("height", heightB + margin.top + margin.bottom)

var gBar = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
  .attr('class', 'gBar');



var x = d3.scale.ordinal()
  .rangeRoundBands([0, widthB], .2);

var y = d3.scale.linear()
  .range([heightB, 0]);

var xAxis = d3.svg.axis()
  .scale(x)
  .orient("bottom");

var yAxis = d3.svg.axis()
  .scale(y)
  .orient("left")
  .ticks(8)
  .tickFormat(function(d) {
    return y.tickFormat(4, d3.format(",d"))(d)
  });


data.forEach(function(d) {
  d.value = +d.value; // coerce to number
  d.china = +d.china;
});

x.domain(dataStackLayout[0].map(function(d) {
  return d.x;
}));


y.domain([0, d3.max(dataStackLayout[dataStackLayout.length - 1],
  function(d) {
    return d.y0 + d.y;
  })]).nice();

var layer;
var bars;

//axes
gBar.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + (heightB + 10) + ")")
  .call(xAxis)
  .selectAll("text")
  .style('font-size', '14px')
  .style('font-family', 'Alegreya')
  .style("text-anchor", "end")
  .attr("dx", "-0.40em")
  .attr("dy", ".10em")
  .attr("transform", function(d) {
    return "rotate(-65)"
  });

gBar.append("g")
  .attr("class", "y axis")
  .call(yAxis)
  .selectAll("text")
  .style('font-size', '16px')
  .style('font-family', 'Alegreya');



function draw() {
  if (value == 'stack') {

    layer = gBar.selectAll(".stack")
      .data(dataStackLayout);

    layer.exit()
      .transition()
      .delay(function(d, i) {
        return 30 * i;
      })
      .duration(1500)
      .style("fill", "none")
      .remove();

    layer.enter().append("g")
      .attr("class", "stack")
      .style("fill", function(d, i) {
        return i == 0 ? '#b4d5c3' : '#ecaeb3';
      });



    bars = layer.selectAll("rect")
      .data(function(d) {
        return d;
      });

    // the "EXIT" set:
    bars.exit()
      .transition()
      .delay(function(d, i) {
        return 30 * i;
      })
      .duration(1500)
      .attr("y", y(0))
      .attr("height", heightB - y(0))
      .style('fill-opacity', 1e-6)
      .remove();

    // the "ENTER" set:
    bars.enter().append("rect")
      .transition()
      .delay(function(d, i) {
        return 30 * i;
      })
      .duration(3000)
      .attr("x", function(d) {
        return x(d.x);
      })
      .attr("y", function(d) {
        return y(d.y + d.y0);

      })
      .attr("height", function(d) {
        return y(d.y0) - y(d.y + d.y0);

      })
      .attr("width", x.rangeBand());


    // the "UPDATE" set:
    bars.transition().delay(function(d, i) {
        return 30 * i;
      }).duration(1500).attr("x", function(d) {
        return x(d.x);
      })
      .attr("width", x.rangeBand()) // constant, so no callback function(d) here
      .attr("y", function(d) {
        return y(d.y + d.y0);
      })
      .attr("height", function(d) {
        return y(d.y0) - y(d.y + d.y0);
      });

  } else { // draw bar

    x.domain(points.map(function(d) {
      return d.name;
    }));

    y.domain([0, 1024]).nice();

    bars = layer.selectAll("rect")
      .data(points);

    // the "EXIT" set:		
    bars.exit()
      .transition()
      .delay(function(d, i) {
        return 30 * i;
      })
      .duration(1500)
      .attr("y", y(0))
      .attr("height", heightB - y(0))
      .style('fill-opacity', 1e-6)
      .remove();

    // the "ENTER" set:
    bars.enter().append("rect")
      .transition()
      .delay(function(d, i) {
        return 30 * i;
      })
      .duration(3000)
      .attr("x", function(d) {
        return x(d.name);
      })
      .attr("y", function(d) {
        return y(d.GTF);
      })
      .attr("height", function(d) {
        return heightB - y(d.GTF);;
      })
      .attr("width", x.rangeBand());


    // the "UPDATE" set:
    bars.transition().delay(function(d, i) {
        return 30 * i;
      }).duration(1500).attr("x", function(d) {
        return x(d.name);
      })
      .attr("width", x.rangeBand()) // constant, so no callback function(d) here
      .attr("y", function(d) {
        return y(d.GTF);
      })
      .attr("height", function(d) {
        return heightB - y(d.GTF);
      });
  }

}

window.onload = draw();

$("#click").on('click', function() {

  if (value == 'stack') {
    value = 'bar';
  } else {
    value = 'stack';
  }
  draw();
});
body {
  font-family: 'Alegreya', serif;
}

.axis text {
  font: 10px sans-serif;
}

.axis path {
  fill: none;
  stroke: #000;
  stroke-width: 0px;
  shape-rendering: crispEdges;
}

.axis line {
  fill: none;
  stroke: #000;
  stroke-width: 0.5px;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.js"></script>
<div id="chart"></div>
<p id="click"> click here to change </p>

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您的代码中的问题是您正在更改条形图的比例域,但您没有将它们更改回堆叠条形图。

    因此,您应该将其放在堆叠条的draw() 部分(条件语句)中:

    x.domain(dataStackLayout[0].map(function(d) {
      return d.x;
    }));
    
    y.domain([0, d3.max(dataStackLayout[dataStackLayout.length - 1],
      function(d) {
        return d.y0 + d.y;
      })]).nice();
    

    这是您的更改代码(我还调用了 x 轴):

    var value = 'stack';
    
    var data = [{
      name: "Shihuahuaco",
      value: 1067,
      china: 772
    }, {
      name: "Cachimbo",
      value: 283,
      china: 1
    }, {
      name: "Estoraque",
      value: 204,
      china: 150
    }, {
      name: "Cumala",
      value: 154,
      china: 0
    }, {
      name: "Ishpingo",
      value: 108,
      china: 3
    }, {
      name: "Huayruro",
      value: 108,
      china: 1
    }, {
      name: "Tornillo",
      value: 61,
      china: 4
    }, {
      name: "Congona",
      value: 54,
      china: 0
    }, {
      name: "Capirona",
      value: 37,
      china: 5
    }, {
      name: "Tahuari",
      value: 33,
      china: 14
    }, {
      name: "Marupa",
      value: 33,
      china: 1
    }, {
      name: "Quinilla",
      value: 28,
      china: 4
    }, {
      name: "Azucar huayo",
      value: 22,
      china: 15
    }, {
      name: "Protium sp.",
      value: 19,
      china: 0
    }, {
      name: "Nogal",
      value: 15,
      china: 6
    }, {
      name: "Ana Caspi",
      value: 14,
      china: 2
    }, {
      name: "Cedro",
      value: 14,
      china: 0
    }, {
      name: "Carapa guianensis",
      value: 12,
      china: 0
    }, {
      name: "Leche caspi",
      value: 12,
      china: 0
    }, {
      name: "Andiroba",
      value: 11,
      china: 0
    }, {
      name: "Copaiba",
      value: 7,
      china: 4
    }, {
      name: "Palo baston",
      value: 6,
      china: 0
    }, {
      name: "Moena",
      value: 5,
      china: 0
    }, {
      name: "Almendro",
      value: 5,
      china: 0
    }, {
      name: "Chancaquero",
      value: 4,
      china: 0
    }, {
      name: "Caimitillo",
      value: 3,
      china: 1
    }, {
      name: "Nogal amarillo",
      value: 3,
      china: 0
    }, {
      name: "Couma macrocarpa",
      value: 3,
      china: 0
    }, {
      name: "Tulpay",
      value: 3,
      china: 0
    }, {
      name: "Carapa",
      value: 3,
      china: 0
    }, {
      name: "Dacryodes olivifera",
      value: 2,
      china: 0
    }, {
      name: "Capinuri",
      value: 2,
      china: 2
    }, {
      name: "Brosimum alicastrum",
      value: 2,
      china: 0
    }, {
      name: "Paramachaerium ormosioide",
      value: 2,
      china: 0
    }, {
      name: "Brosimum sp.",
      value: 2,
      china: 0
    }, {
      name: "Manchinga",
      value: 2,
      china: 0
    }];
    
    var points = [{
      'lon': 105.3,
      'lat': 33.5,
      'name': 'China',
      'GTF': 1024,
      "ID": "CHN"
    }, {
      'lon': -70.9,
      'lat': 18.8,
      'name': 'Dominican Republic',
      'GTF': 470,
      "ID": "DOM"
    }, {
      'lon': -101,
      'lat': 38,
      'name': 'USA',
      'GTF': 248,
      "ID": "USA"
    }, {
      'lon': -102.5,
      'lat': 22.7,
      'name': 'Mexico',
      'GTF': 220,
      "ID": "MEX"
    }, {
      'lon': 2.98,
      'lat': 46,
      'name': 'France',
      'GTF': 85,
      "ID": "FRA"
    }];
    
    var margin = {
        top: 20,
        right: 30,
        bottom: 150,
        left: 60
      },
      widthB = 700 - margin.left - margin.right,
      heightB = 500 - margin.top - margin.bottom;
    
    var dataIntermediate = ['value', 'china'].map(function(key, i) {
      return data.map(function(d, j) {
        return {
          x: d['name'],
          y: d[key]
        };
      })
    })
    
    var dataStackLayout = d3.layout.stack()(dataIntermediate);
    
    var svgBar = d3.select("#chart").append("svg")
      .attr("width", widthB + margin.left + margin.right)
      .attr("height", heightB + margin.top + margin.bottom)
    
    var gBar = svgBar.append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
      .attr('class', 'gBar');
    
    var x = d3.scale.ordinal()
      .rangeRoundBands([0, widthB], .2);
    
    var y = d3.scale.linear()
      .range([heightB, 0]);
    
    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");
    
    var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left")
      .ticks(8)
      .tickFormat(function(d) {
        return y.tickFormat(4, d3.format(",d"))(d)
      });
    
    data.forEach(function(d) {
      d.value = +d.value; // coerce to number
      d.china = +d.china;
    });
    
    x.domain(dataStackLayout[0].map(function(d) {
      return d.x;
    }));
    
    y.domain([0, d3.max(dataStackLayout[dataStackLayout.length - 1],
      function(d) {
        return d.y0 + d.y;
      })]).nice();
    
    var layer;
    
    // this part 
    var bars;
    
    var gX = gBar.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + (heightB + 10) + ")");
    
    
    gBar.append("g")
      .attr("class", "y axis")
      .call(yAxis)
      .selectAll("text")
      .style('font-size', '16px')
      .style('font-family', 'Alegreya');
    
    function draw() {
      if (value == 'stack') {
    
        x.domain(dataStackLayout[0].map(function(d) {
          return d.x;
        }));
    
        y.domain([0, d3.max(dataStackLayout[dataStackLayout.length - 1],
          function(d) {
            return d.y0 + d.y;
          })]).nice();
    
        layer = gBar.selectAll(".stack")
          .data(dataStackLayout);
    
        layer.exit()
          .transition()
          .delay(function(d, i) {
            return 30 * i;
          })
          .duration(1500)
          .style("fill", "none")
          .remove();
    
        layer.enter().append("g")
          .attr("class", "stack")
          .style("fill", function(d, i) {
            return i == 0 ? '#b4d5c3' : '#ecaeb3';
          });
    
        bars = layer.selectAll("rect")
          .data(function(d) {
            return d;
          });
    
        bars.exit()
          .transition()
          .delay(function(d, i) {
            return 30 * i;
          })
          .duration(1500)
          .attr("y", y(0))
          .attr("height", heightB - y(0))
          .style('fill-opacity', 1e-6)
          .remove();
    
        bars.enter().append("rect")
          .transition()
          .delay(function(d, i) {
            return 30 * i;
          })
          .duration(3000)
          .attr("x", function(d) {
            return x(d.x);
          })
          .attr("y", function(d) {
            return y(d.y + d.y0);
          })
          .attr("height", function(d) {
            return y(d.y0) - y(d.y + d.y0);
          })
          .attr("width", x.rangeBand());
    
    
        // the "UPDATE" set:
        bars.transition().delay(function(d, i) {
            return 30 * i;
          }).duration(1500).attr("x", function(d) {
            return x(d.x);
          }) // (d) is one item from the data array, x is the scale object from above
          .attr("width", x.rangeBand()) // constant, so no callback function(d) here
          .attr("y", function(d) {
            return y(d.y + d.y0);
          })
          .attr("height", function(d) {
            return y(d.y0) - y(d.y + d.y0);
          })
          .style("fill-opacity", 1);
    
        gX.call(xAxis)
          .selectAll("text")
          .style('font-size', '14px')
          .style('font-family', 'Alegreya')
          .style("text-anchor", "end")
          .attr("dx", "-0.40em")
          .attr("dy", ".10em")
          .attr("transform", function(d) {
            return "rotate(-65)"
          });
    
      } else {
    
        x.domain(points.map(function(d) {
          return d.name;
        }));
    
        y.domain([0, 1024]).nice();
    
        // this part 
        bars = layer.selectAll("rect")
          .data(points);
    
        bars.exit()
          .transition()
          .delay(function(d, i) {
            return 30 * i;
          })
          .duration(1500)
          .attr("y", y(0))
          .attr("height", heightB - y(0))
          .style('fill-opacity', 1e-6)
          .remove();
    
        bars.enter().append("rect")
          .transition()
          .delay(function(d, i) {
            return 30 * i;
          })
          .duration(3000)
          .attr("x", function(d) {
            return x(d.name);
          })
          .attr("y", function(d) {
            return y(d.GTF);
    
          })
          .attr("height", function(d) {
            return heightB - y(d.GTF);;
    
          })
          .attr("width", x.rangeBand());
    
        // the "UPDATE" set:
        bars.transition().delay(function(d, i) {
            return 30 * i;
          }).duration(1500).attr("x", function(d) {
            return x(d.name);
          }) // (d) is one item from the data array, x is the scale object from above
          .attr("width", x.rangeBand()) // constant, so no callback function(d) here
          .attr("y", function(d) {
            return y(d.GTF);
          })
          .attr("height", function(d) {
            return heightB - y(d.GTF);
          });
    
        gX.call(xAxis);
    
      }
    
    }
    
    window.onload = draw();
    
    
    $("#click").on('click', function() {
    
      if (value == 'stack') {
        value = 'bar';
      } else {
        value = 'stack';
      }
    
      draw();
    
    });
    body {
      font-family: 'Alegreya', serif;
    }
    
    .axis text {
      font: 10px sans-serif;
    }
    
    .axis path {
      fill: none;
      stroke: #000;
      stroke-width: 0px;
      shape-rendering: crispEdges;
    }
    
    .axis line {
      fill: none;
      stroke: #000;
      stroke-width: 0.5px;
      shape-rendering: crispEdges;
    }
    
    .x.axis path {
      display: none;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="https://d3js.org/d3.v3.js"></script>
    <div id="chart"></div>
    <button id="click"> click here to change </button>

    PS:除此之外,您还应该对代码进行许多其他小的更改,包括性能和设计。由于这是(现在)一个正在运行的代码,我建议您在Code Review 上使用 标签发布更多关于如何改进它的问题。

    【讨论】:

    • 谢谢,感谢您对代码设计的建议
    • @jhjanicki 不用担心。 Code Review 比 S.O. 小得多,而且我几乎是唯一一个在那里回答 D3 问题的活跃用户......只需将其标记为 d3.js,我会提出一些更改建议。
    猜你喜欢
    • 2016-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 1970-01-01
    • 2014-07-14
    相关资源
    最近更新 更多