如何在条形图后面移动网格线(D3)

时间:2018-04-25 18:10:06

标签: javascript angular d3.js

我想在条形图后面移动网格线,实际上我在upgradeChart()函数的条形图之前打印网格线,有什么建议吗? 我需要网格线:)我不能删除它们。 这是我的代码:

    import { Component, OnInit, OnChanges, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation, Input } from '@angular/core';
import * as d3 from 'd3';
import * as _d3Tip from 'd3-tip';

@Component({
  selector: 'app-histogram',
  templateUrl: './histogram.component.html',
  styleUrls: ['./histogram.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class HistogramComponent implements OnInit, OnChanges {
   @ViewChild('chart') private chartContainer: ElementRef;
   @Input() public config;
    private chart;
    private margin;
    private width;
    private height;
    private xScale;
    private yScale;
    private xAxis;
    private yAxis;
    private element;
    private svg;

  constructor() {
  }

  ngOnInit() {
    console.log(this.config);
    this.createChart( );
    if (this.config.data) {
      this.updateChart();
    }
  }

  ngOnChanges() {
    if (this.chart) {
      this.updateChart();
    }
  }

  public createChart() {

// Traemos el elemento donde estará el gráfico en este caso #chart
this.element = this.chartContainer.nativeElement;

// Guardamos los datos en una constante
const data = this.config.data;

// Declaramos el margen, el ancho y la altura del gráfico según los estilos del div #chart (d3-chart)
this.margin = { top: 40, right: 20, bottom: 30, left: 40 };
this.width = this.element.offsetWidth - this.margin.left - this.margin.right;
this.height = this.element.offsetHeight - this.margin.top - this.margin.bottom;

this.svg = d3.select(this.element).append('svg')
  .attr('width', this.element.offsetWidth)
  .attr('height', this.element.offsetHeight);

// chart plot area
this.chart = this.svg.append('g')
  .attr('class', 'bars')
  .attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);

// Creamos las escalas
this.xScale = d3.scaleBand().range([0, this.width]).padding(0.3);
this.yScale = d3.scaleLinear().range([this.height, 0]);

// Definimos la posicion del eje X en la parte de abajo
this.xAxis = this.svg.append('g')
  .attr('class', 'x axis')
  .attr('transform', `translate(${this.margin.left}, ${this.margin.top + this.height})`)
  .call(d3.axisBottom(this.xScale));

// Definimos la posicion del eje Y en la parte izquierda con su formato..
this.yAxis = this.svg.append('g')
  .attr('class', 'y axis')
  .attr('transform', `translate(${this.margin.left}, ${this.margin.top})`)
  .call(d3.axisLeft(this.yScale));


 }

  public updateChart() {

// Necesario al importar la libreria d3-tip para versiones superiores a 4 en d3.js
const d3Tip = _d3Tip.bind(d3);

// Creamos el tooltip
const tip = d3Tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
  return '<span class="highlight">' + d.valueY + '€' + '</span>';
});

this.svg.call(tip);

// Definimos los datos de las escalas
this.xScale.domain(this.config.data.map( d => d.valueX));
this.yScale.domain([0, d3.max(this.config.data, d => d.valueY)]);
this.xAxis.transition().call(d3.axisBottom(this.xScale));
this.yAxis.transition().call(d3.axisLeft(this.yScale).tickSize(-this.width).ticks(4).tickFormat(d => d + '€'));

const update = this.chart.selectAll('.bar')
  .data(this.config.data);

  // remove exiting bars
  update.exit().remove();

  // update existing bars
  this.chart.selectAll('.bar').transition()
    .attr('x', d => this.xScale(d.valueX))
    .attr('y', d => this.yScale(d.valueY))
    .attr('width', d => this.xScale.bandwidth())
    .attr('height', d => this.height - this.yScale(d.valueY));

    update
    .enter().append('rect')
    .attr('class', 'bar')
    .attr('data-index', function(d, i) { return i; })
    .on('mouseover', tip.show)
    .on('mouseout', tip.hide)
    .attr('x', d => this.xScale(d.valueX))
    .attr('y', d => this.yScale(d.valueY))
    .attr('width', this.xScale.bandwidth())
    .transition()
    .ease(d3.easeLinear)
    .duration(300)
    .delay((d, i) => i * 50)
    .attr('height', d => this.height - this.yScale(d.valueY));


 }

}

问题在于这一行:

this.yAxis.transition().call(d3.axisLeft(this.yScale).tickSize(-this.width).ticks(4).tickFormat(d => d + '€'));

此行执行网格线:

.tickSize(-this.width).ticks(4).tickFormat(d => d + '€')

我从我的barChart中提取了一张照片 Bar Chart d3.js v5

网格解决方案:

解决方案是放置此代码:

    // chart plot area
this.chart = this.svg.append('g')
.attr('class', 'bars')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);

之后:

    // Definimos la posicion del eje Y en la parte izquierda con su formato..
this.yAxis = this.svg.append('g')
  .attr('class', 'y axis')
  .attr('transform', `translate(${this.margin.left}, ${this.margin.top})`)
  .call(d3.axisLeft(this.yScale));

编辑:另一个问题:

  1. 有人知道怎样才能响应我的条形图?
  2. 当值为0€时,我需要设置一个最小的条,我该怎么做?

1 个答案:

答案 0 :(得分:1)

我不确定没有小提琴但是,svg最初没有z-index。元素基于DOM树分层。最后绘制的内容显示在顶部。在你的情况下,我看到:

this.xAxis = this.svg.append('g')...

this.yAxis = this.svg.append('g')..

现在不是直接在svg上将它们作为g元素添加,而是尝试为两个轴创建包装g:

this.wrapper = this.svg.append('g');

然后代替上述2,执行:

this.xAxis = this.wrapper.append('g')...

确保在添加元素之前添加了包装器g元素。缓解问题的那个?

对于您的其他问题,

2 - 有多种方法可以使其响应,最简单的方法之一是在svg上使用preserveAspectRatio =“none”属性,然后使用css媒体查询控制div或包装svg的任何元素。如果你的意思是改变绘制轴等的方式,那么你必须调用一个更新函数来更新窗口"resize"事件中的这些元素。

3 - 在图表上设置x,y或width高度或转换属性时使用Math.max和Math.min

.attr("height",function(d,i){return Math.min(someMin,someScale(d))})