d3使用selection.join更新堆叠的条形图 [英] d3 Update stacked bar graph using selection.join

查看:42
本文介绍了d3使用selection.join更新堆叠的条形图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个堆叠的条形图,该条形图应在更改数据时进行更新,并且我想使用d3v5和selection.join,如此处所述

I am creating a stacked bar graph which should update on changing data and I want to use d3v5 and selection.join as explained here https://observablehq.com/@d3/learn-d3-joins?collection=@d3/learn-d3.
When entering the data everything works as expected, however the update function is never called (that's the console.log() for debugging.). So it looks like it is just entering new data all the time.
How can I get this to work as expected?

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
    body {
      margin: 0;
    }
    .y.axis .domain {
      display: none;
    }
    </style>
  </head>
  <body>
    <div id="chart"></div>

    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script>

    let xVar = "year";

    let alphabet = "abcdef".split("");
    let years = [1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003];

    let margin = { left:80, right:20, top:50, bottom:100 };

    let width = 600 - margin.left - margin.right,
        height = 600 - margin.top - margin.bottom;

    let g = d3.select("#chart")
      .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
      .append("g")
        .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");

    let color = d3.scaleOrdinal(["#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f"])
    let x = d3.scaleBand()
        .rangeRound([0, width])
        .domain(years)
        .padding(.25);

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

    let xAxis = d3.axisBottom(x);

    let yAxis = d3.axisRight(y)
      .tickSize(width)

    g.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    g.append("g")
        .attr("class", "y axis")
        .call(yAxis);

    let stack = d3.stack()
        .keys(alphabet)
        .order(d3.stackOrderNone)
        .offset(d3.stackOffsetNone);

    redraw(randomData());

    d3.interval(function(){
      redraw(randomData());  
    }, 1000);

    function redraw(data){

      // update the y scale
      y.domain([0, d3.max(data.map(d => d.sum ))])
      g.select(".y")
        .transition().duration(1000)
          .call(yAxis);

      groups = g.append('g')
        .selectAll('g')
        .data(stack(data))
        .join('g')
        .style('fill', (d,i) => color(d.key));
      groups.selectAll('.stack')
        .data(d => d)
        .attr('class', 'stack')
        .join(
          enter => enter.append('rect')
            .data(d => d)
              .attr('x', d => x(d.data.year))
              .attr('y', y(0))
              .attr('width', x.bandwidth())
            .call(enter => enter.transition().duration(1000)
              .attr('y', d => y(d[1]))
              .attr('height', d => y(d[0]) - y(d[1]))
            ),
          update => update
          .attr('x', d => x(d.data.year))
          .attr('y', y(0))
          .attr('width', x.bandwidth())
            .call(update => update.transition().duration(1000)
            .attr('y', d => y(d[1]))
              .attr('height', d => y(d[0]) - y(d[1]))
            .attr(d => console.log('update stack'))
          )
      )

    }

    function randomData(data){
      return years.map(function(d){
        let obj = {};
        obj.year = d;
        let nums = [];
        alphabet.forEach(function(e){
          let num = Math.round(Math.random()*2);
          obj[e] = num;
          nums.push(num);
        });
        obj.sum = nums.reduce((a, b) => a + b, 0);
        return obj;
      });
    }

    </script>
  </body>
</html>

这是在工作的jsfiddle中: https://jsfiddle.net/blabbath/yeq5d1tp/

Here is it in a working jsfiddle: https://jsfiddle.net/blabbath/yeq5d1tp/

我首先提供了一个错误的链接,这是正确的链接.我的示例主要基于以下内容: https://bl.ocks.org/HarryStevens/7e3ec1a6722a153a5d102b6c42f4501d>

I provided a wrong link first, here is the right one. My example is heavily based on this: https://bl.ocks.org/HarryStevens/7e3ec1a6722a153a5d102b6c42f4501d

推荐答案

几天前我遇到了同样的问题.我的操作方式如下:

I had the same issue a few days ago. The way I did it is as follows:

我们有两个 .join ,父级一个用于堆栈,子级用于矩形.

We have two .join, the parent one is for the stack and the child is for the rectangles.

在父联接的 enter 中,我们调用 updateRects 以便第一次绘制矩形,此 updateRects 函数将执行子 .join ,第二个join函数将绘制矩形.

In the enter of the parent join, we call the updateRects in order to draw the rectangles for the first time, this updateRects function will do the child .join, this second join function will draw the rectangles.

对于更新,我们执行相同的操作,但不是在联接的 enter 函数中执行此操作,而是在 update 中进行此操作.

For the update we do the same, but instead of doing it in the enter function of the join, we do it in the update.

此外,我的SVG的结构不同,我有一个堆栈组,然后有堆栈组和一个钢筋组,在这个钢筋组中,我添加了矩形.在下面的小提琴中,您可以看到我为父组添加了 stack 类.

Also, my SVG is structured in a different way, I have a stacks groups, then I have the stack group, and a bars group, in this bars group I add the rectangles. In the fiddle below, you can see that I added the parent group with the class stack.

这两个功能如下:

updateStack:

updateStack:

  function updateStack(data) {
    // update the y scale
    y.domain([0, d3.max(data.map((d) => d.sum))]);
    g.select(".y").transition().duration(1000).call(yAxis);

    const stackData = stack(data);

    stackData.forEach((stackedBar) => {
      stackedBar.forEach((stack) => {
        stack.id = `${stackedBar.key}-${stack.data.year}`;
      });
    });

    let bars = g
      .selectAll("g.stacks")
      .selectAll(".stack")
      .data(stackData, (d) => {
        return d.key;
      });

    bars.join(
      (enter) => {
        const barsEnter = enter.append("g").attr("class", "stack");

        barsEnter
          .append("g")
          .attr("class", "bars")
          .attr("fill", (d) => {
            return color(d.key);
          });

        updateRects(barsEnter.select(".bars"));

        return enter;
      },
      (update) => {
        const barsUpdate = update.select(".bars");
        updateRects(barsUpdate);
      },
      (exit) => {
        return exit.remove();
      }
    );
  }

updateRects:

updateRects:

  function updateRects(childRects) {
    childRects
      .selectAll("rect")
      .data(
        (d) => d,
        (d) => d.id
      )
      .join(
        (enter) =>
          enter
            .append("rect")
            .attr("id", (d) => d.id)
            .attr("class", "bar")
            .attr("x", (d) => x(d.data.year))
            .attr("y", y(0))
            .attr("width", x.bandwidth())
            .call((enter) =>
              enter
                .transition()
                .duration(1000)
                .attr("y", (d) => y(d[1]))
                .attr("height", (d) => y(d[0]) - y(d[1]))
            ),
        (update) =>
          update.call((update) =>
            update
              .transition()
              .duration(1000)
              .attr("y", (d) => y(d[1]))
              .attr("height", (d) => y(d[0]) - y(d[1]))
          ),
        (exit) =>
          exit.call((exit) =>
            exit
              .transition()
              .duration(1000)
              .attr("y", height)
              .attr("height", height)
          )
      );
  }

这是jsfiddle的更新 https://jsfiddle.net/5oqwLxdj/1/

Here is an update jsfiddle https://jsfiddle.net/5oqwLxdj/1/

希望对您有帮助.

这篇关于d3使用selection.join更新堆叠的条形图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆