D3 - Add bars into group

2020-04-21 00:33发布

I have a stackblitz here - https://stackblitz.com/edit/chart-update-1-34uvdo?file=src/app/bar-chart.ts

It's a d3 chart in an angular app.

The bars have arrows to show the start and finish position of the bar.

I'd like to have each bar arrow compo in one g element - at the moment they are all in the same g element.

How can I craete a g element for each bar and arrow.

bar.enter()
    .append("line")
    .attr("x1", d => this.x(d.phase) + this.x.bandwidth() / 2)
    .attr("y1", d => this.y(d.start) + ((d.start < d.finish) ? -5 : 5))
    .attr("x2", d => this.x(d.phase) + this.x.bandwidth() / 2)
    .attr("y2", d => this.y(d.finish) + ((d.start < d.finish) ? 5 : -5))
    .attr("stroke", "black")
    .attr("stroke-width", 3)
    .attr("marker-end", "url(#arrow)")
    .style('pointer-events', 'none'); 

标签: d3.js
1条回答
家丑人穷心不美
2楼-- · 2020-04-21 00:45

Have a look again at the d3 general update pattern. It is one of those d3 concepts that will always be a little confusing, especially when applying it to anything except the simplest examples.

So by looking at the above pattern we can break it down as:

  • Data Join

    Join new data with old elements, if any.

  • Update

    Update old elements as needed.

  • Enter

    Create new elements as needed.

  • Enter + Update

    After merging the entered elements with the update selection, apply operations to both.

  • Exit

    Remove old elements as needed.


private drawUpdate(data) {
  this.x.domain(data.map((d: any) => {
    return d.phase
  }));

  this.getExtent(data);

  this.y.domain(d3.extent(this.values));

  this.createAxis();

  // Data Join
  const bar = this.chart.selectAll(".bargroup")
    .data(data);

  // Updates
  // Update rect dimensions
  bar.select('rect').attr("x", (d, i) => {
    return this.x(d.phase)
  })
    .attr("width", (d, i) => {
      return this.x.bandwidth()
    })
    .attr("y", (d, i) => {
      if (d.start < d.finish) {
        return this.y(d.finish);
      } else {
        return this.y(d.start);
      }
    })
    .attr("height", (d, i) => {
      if (d.start < d.finish) {
        return this.y(d.start) - this.y(d.finish);
      } else {
        return this.y(d.finish) - this.y(d.start);
      }
    })

  // Update line dimensions
  bar.select('line')
    .attr("x1", d => this.x(d.phase) + this.x.bandwidth() / 2)
    .attr("y1", d => this.y(d.start) + ((d.start < d.finish) ? -5 : 5))
    .attr("x2", d => this.x(d.phase) + this.x.bandwidth() / 2)
    .attr("y2", d => this.y(d.finish) + ((d.start < d.finish) ? 5 : -5))

  // Enter
  const barEnter = bar.enter()
    .append("g")
    .classed('bargroup', true)

  // Enter & update - append new rects, update dimensions
  barEnter
    .append('rect')
    .attr("x", (d, i) => {
      return this.x(d.phase)
    })
    .attr("width", (d, i) => {
      return this.x.bandwidth()
    })
    .attr("y", (d, i) => {
      if (d.start < d.finish) {
        return this.y(d.finish);
      } else {
        return this.y(d.start);
      }
    })
    .attr("height", (d, i) => {
      if (d.start < d.finish) {
        return this.y(d.start) - this.y(d.finish);
      } else {
        return this.y(d.finish) - this.y(d.start);
      }
    })

  // Enter & update - append new lines, update dimensions
  barEnter
    .append("line")
    .attr("stroke", "white")
    .attr("stroke-width", 3)
    .attr("marker-end", "url(#arrow)")
    .style('pointer-events', 'none')
    .attr("x1", d => this.x(d.phase) + this.x.bandwidth() / 2)
    .attr("y1", d => this.y(d.start) + ((d.start < d.finish) ? -5 : 5))
    .attr("x2", d => this.x(d.phase) + this.x.bandwidth() / 2)
    .attr("y2", d => this.y(d.finish) + ((d.start < d.finish) ? 5 : -5))

  //Exit
  bar.exit()
    .remove();

  d3.select('.y-axis').call(this.y_axis)

  d3.select('.x-axis').call(this.x_axis)
    .attr("transform", "translate(0," + this.height + ")");

}

Updated stackblitz

查看更多
登录 后发表回答