如何更新和删除条形图中的条形组?

时间:2017-12-13 16:48:10

标签: javascript d3.js graph

我已经和d3 v4合作了几个月并做了很多研究。我终于抓住了数据连接概念,但试图弄清楚如何更新分组条形图。“

我认为我的问题是我在执行连接时没有选择正确的SVG元素,因此update()exit()方法总是返回空集。以下是基于d3广告代码的代码:Codepen请参阅#135 行,其中所有魔法都假设正在发生。

/* Based on https://bl.ocks.org/mbostock/3887051 */

var svg = d3.select("svg"),
  margin = { top: 20, right: 20, bottom: 100, left: 40 },
  width = +svg.attr("width") - margin.left - margin.right,
  height = +svg.attr("height") - margin.top - margin.bottom,
  g = svg
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var formatTime = d3.timeFormat("%X");
var legendKeys = new Map();
var x0 = d3
  .scaleBand()
  .rangeRound([0, width])
  .paddingInner(0.1);

var x1 = d3.scaleBand().padding(0.05);

var y = d3.scaleLinear().rangeRound([height, 0]);

var z = d3
  .scaleOrdinal()
  .range([
    "#98abc5",
    "#8a89a6",
    "#7b6888",
    "#6b486b",
    "#a05d56",
    "#d0743c",
    "#ff8c00"
  ]);

var data = [];
for (var i = 0; i < 5; i++) {
  generateData();
}

var keys = Object.keys(data[0].y);
keys.forEach(k => legendKeys.set(".data-" + k.replace(/ /g, "")));
g.append("g").attr("class", "bars");

var xAxis = g
  .append("g")
  .attr("class", "x--axis")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x0).tickFormat(formatTime));

var yAxis = g
  .append("g")
  .attr("class", "y--axis")
  .call(d3.axisLeft(y).ticks(null, "s"));

var legend = g
  .append("g")
  .attr("font-family", "sans-serif")
  .attr("font-size", 8)
  .attr("text-anchor", "end")
  .attr("class", "legend")
  .selectAll("g")
  .data(keys.slice())
  .enter()
  .append("g")
  .attr("transform", function(d, i) {
    return "translate(0," + i * 20 + ")";
  });

legend
  .append("rect")
  .attr("x", width - 19)
  .attr("width", 19)
  .attr("height", 19)
  .attr("style", "cursor:pointer")
  .attr("fill", z)
  .on("click", function(d) {
    var key = ".data-" + d.replace(/ /g, "");
    // determine if current line is visible
    var active = legendKeys.get(key) ? false : true,
      newOpacity = active ? 0 : 1;
    // hide or show the elements
    d3.selectAll(key).style("opacity", newOpacity);
    // update whether or not the elements are active
    legendKeys.set(key, active);
    this.style.opacity = active ? 0.5 : 1;
  });

legend
  .append("text")
  .attr("x", width - 24)
  .attr("y", 9.5)
  .attr("dy", "0.32em")
  .text(function(d) {
    return d;
  });

window.setInterval(() => {
  generateData();
  if (data.length > 14) {
    data.shift();
  }
  updateChart();
}, 1000);

function updateChart() {
  x0.domain(
    data.map(function(d) {
      return d.x;
    })
  );
  x1.domain(keys).rangeRound([0, x0.bandwidth()]);
  y
    .domain([
      0,
      d3.max(data, function(d) {
        return d3.max(keys, function(key) {
          return d.y[key];
        });
      })
    ])
    .nice();

  d3.select(".x--axis")
    .transition()
    .call(d3.axisBottom(x0).tickFormat(formatTime));

  d3.select(".y--axis")
    .transition()
    .call(d3.axisLeft(y));

  var barGroups = g
    .select(".bars")
    .selectAll(".barGroup")
    .data(data);

  barGroups
    .enter()
    .append("g")
    .attr("transform", function(d) {
      return "translate(" + x0(d.x) + ",0)";
    })
    .attr("class", "barGroup")
    .selectAll("rect")
    .data(function(d) {
      return keys.map(function(key) {
        return { key: key, value: d.y[key] };
      });
    })
    .enter()
    .append("rect")
    .attr("x", function(d) {
      return x1(d.key);
    })
    .attr("y", function(d) {
      return y(d.value);
    })
    .attr("width", x1.bandwidth())
    .attr("height", function(d) {
      return height - y(d.value);
    })
    .attr("class", function(d) {
      return "data-" + d.key.replace(/ /g, "");
    })
    .attr("fill", function(d) {
      return z(d.key);
    })
    .merge(barGroups)
    .attr("width", x1.bandwidth())
    .attr("height", function(d) {
      return height - y(d.value);
    })
    .attr("x", function(d) {
      return x1(d.key);
    })
    .attr("y", function(d) {
      return y(d.value);
    });

  // Exit
  var old = barGroups
    .exit()
    .remove();

  // Release memory for elements that were removed
  old = null;
}

function generateData() {
  var lastDate = new Date();
  if (data.length > 0) {
    lastDate = new Date(data[data.length - 1].x);
  }
  lastDate.setHours(lastDate.getHours() + 1);
  
  data.push({
    x: lastDate,
    y: {
      A: d3.randomUniform(0, 100)(),
      B: d3.randomUniform(0, 100)(),
      C: d3.randomUniform(0, 100)()
    }
  });
}
/* Based on https://bl.ocks.org/mbostock/3887051 */
rect.bar {
  fill: steelblue;
}

text {
  fill: #000;
  font: 11px sans-serif;
  font-weight:500;
}

/* body {
  background-color: #f00;
} */

.legend rect:hover {
  stroke-width:0.5;
  stroke: black;
}

svg {
  background-color:#eee;
  shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<!-- Based on https://bl.ocks.org/mbostock/3887051 -->
<svg width="900" height="400"></svg>

1 个答案:

答案 0 :(得分:0)

我提供的这种模式用于更新分组条形图。如果不深入研究代码,可能会对其进行调整。

    //update pattern for bar chart
    var groups = this.plot.selectAll('.bar-group').data(data, function(d) { return d; });

    //EXIT bars no longer in the data   
    groups.exit()
     .transition()
      .duration(300)
     .remove();

    //ENTER bars with new data
    var bars = groups.enter().append("g")
            .attr('class', 'bar-group')
            .attr("transform", function(d) { return "translate(" + that.x0Scale(d[that.x_var]) + ",0)"; })
          .merge(groups)
            .selectAll('rect')
            .data(function(d) { return that.grouping.map(function(key) { return {key: key, value: +d[key]}; }); });

    //EXIT bars in group just in case that changed
    bars.exit().transition().duration(300).attr('height', 0).attr("y", that.yScale(0)).remove()

    bars.enter().append("rect")
            .attr('class', 'bar')
            .attr("x", function(d) { return that.x1Scale(d.key); })
            .attr("y", that.yScale(0));
        .merge(bars)
        .transition().duration(500)
            .attr("x", function(d) { return that.x1Scale(d.key); })
            .attr("y", function(d) { return that.yScale(Math.max(0, d.value)); })
            .attr("width", d3.min([that.x1Scale.bandwidth(), 200]))
            .attr("height", function(d) { return Math.abs(that.yScale(0)- that.yScale(d.value)); })
            .attr("fill", function(d) { return that.zScale(d.key); })
            .attr("opacity", 0.85 );