javascript - D3 从堆叠条形图到条形图的转换仅在第一次有效

标签 javascript d3.js

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

以下是当前外观的链接: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>

最佳答案

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

因此,您应该将其放在堆叠条的 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 上发布有关如何改进它的更多问题,使用标签。

关于javascript - D3 从堆叠条形图到条形图的转换仅在第一次有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47852471/

相关文章:

javascript - 在Javascript中将变量插入另一个字符串变量中

javascript - 在 Canvas 的某个点放大

javascript - 关于 DOM 的 d3 错误

javascript - 我想知道为什么2个变量不能进入js

javascript - 带有缩放元素的响应式网格

d3.js - d3 水平条形图,背景为 100%

javascript - 如何获取 d3 中包含填充的条形宽度?

javascript - d3 v4 圆环图具有与小值重叠的文本和折线

javascript - 意外的数据绑定(bind)行为

javascript - 基金会 5 : call js function from topbar dropdown anchor