How to add a separator in D3 bar chart

后端 未结 1 1607
小蘑菇
小蘑菇 2021-01-28 09:22

I\'m having a bar chart in my application which was drawn using D3. The sample code has been added below. In my chart, it shows months in a 12 months time period. So depending o

1条回答
  •  被撕碎了的回忆
    2021-01-28 10:01

    Here's a simple way to add a line and a date using your existing scales.

    // create a group for your line and date.
    const dateLine = chart.append('g')
    // position the group: xScale('Jan') is the left edge of the Jan rect, so it needs
    // to be in the space between the Dec and Jen rect elements. That space is
    // given by xScale.step(), the step size for each month, * the padding / 2
      .attr('transform', 'translate(' + (xScale('Jan') - xScale.step() * xScale.padding() * 0.5) + ', 0)')
    
    // append a line, same height as the chart itself
    dateLine.append('line')
      .attr('y2', height)
    
    // append a text element with the year in it. You can change the x and y attributes
    // to position it to your liking
    dateLine.append('text')
      .text('2018')
      .attr('x', 20)
      .attr('y', 20)
    

    const sample = [{
        month: 'Sep',
        value: 78.9,
        color: '#000000',
        date: '30/09/17'
      },
      {
        month: 'Oct',
        value: 75.1,
        color: '#00a2ee',
        date: '31/10/17'
      },
      {
        month: 'Nov',
        value: 68.0,
        color: '#fbcb39',
        date: '30/11/17'
      },
      {
        month: 'Dec',
        value: 67.0,
        color: '#007bc8',
        date: '31/12/17'
      },
      {
        month: 'Jan',
        value: 65.6,
        color: '#65cedb',
        date: '31/01/18'
      },
      {
        month: 'Feb',
        value: 65.1,
        color: '#ff6e52',
        date: '28/02/18'
      },
      {
        month: 'Mar',
        value: 61.9,
        color: '#f9de3f',
        date: '31/03/18'
      },
      {
        month: 'Apr',
        value: 60.4,
        color: '#5d2f8e',
        date: '30/04/18'
      },
      {
        month: 'May',
        value: 59.6,
        color: '#008fc9',
        date: '31/05/18'
      },
      {
        month: 'Jun',
        value: 59.6,
        color: '#507dca',
        date: '30/06/18'
      },
      {
        month: 'Jul',
        value: 80.6,
        color: '#507dca',
        date: '31/07/18'
      },
      {
        month: 'Aug',
        value: 45.6,
        color: '#507dca',
        date: '31/08/18'
      },
      {
        month: 'Sep ',
        value: 78.6,
        color: '#507dca',
        date: '30/09/18'
      }
    ];
    
    const svg = d3.select('svg');
    const svgContainer = d3.select('#container');
    
    const margin = 80;
    const width = 1000 - 2 * margin;
    const height = 600 - 2 * margin;
    
    const chart = svg.append('g')
      .attr('transform', `translate(${margin}, ${margin})`);
    
    const xScale = d3.scaleBand()
      .range([0, width])
      .domain(sample.map((s) => s.month))
      .padding(0.4)
    
    const yScale = d3.scaleLinear()
      .range([height, 0])
      .domain([0, 100]);
    
    // vertical grid lines
    // const makeXLines = () => d3.axisBottom()
    //   .scale(xScale)
    
    const makeYLines = () => d3.axisLeft()
      .scale(yScale)
    
    chart.append('g')
      .attr('transform', `translate(0, ${height})`)
      .call(d3.axisBottom(xScale));
    
    chart.append('g')
      .call(d3.axisLeft(yScale));
    
    const dateLine = chart.append('g')
      .attr('transform', 'translate(' +
        (xScale('Jan') - xScale.step() * xScale.padding() * 0.5) + ', 0)')
    dateLine.append('line')
      .attr('y2', height)
    
    dateLine.append('text')
      .text('2018')
      .attr('x', 20)
      .attr('y', 20)
    
    // vertical grid lines
    // chart.append('g')
    //   .attr('class', 'grid')
    //   .attr('transform', `translate(0, ${height})`)
    //   .call(makeXLines()
    //     .tickSize(-height, 0, 0)
    //     .tickFormat('')
    //   )
    
    chart.append('g')
      .attr('class', 'grid')
      .call(makeYLines()
        .tickSize(-width, 0, 0)
        .tickFormat('')
      )
    
    const barGroups = chart.selectAll()
      .data(sample)
      .enter()
      .append('g')
    
    barGroups
      .append('rect')
      .attr('class', 'bar')
      .attr('x', (g) => xScale(g.month))
      .attr('y', (g) => yScale(g.value))
      .attr('height', (g) => height - yScale(g.value))
      .attr('width', xScale.bandwidth())
      .on('mouseenter', function(actual, i) {
        d3.selectAll('.value')
          .attr('opacity', 0)
    
        d3.select(this)
          .transition()
          .duration(300)
          .attr('opacity', 0.6)
          .attr('x', (a) => xScale(a.month) - 5)
          .attr('width', xScale.bandwidth() + 10)
    
        const y = yScale(actual.value)
    
        line = chart.append('line')
          .attr('id', 'limit')
          .attr('x1', 0)
          .attr('y1', y)
          .attr('x2', width)
          .attr('y2', y)
    
        barGroups.append('text')
          .attr('class', 'divergence')
          .attr('x', (a) => xScale(a.month) + xScale.bandwidth() / 2)
          .attr('y', (a) => yScale(a.value) + 30)
          .attr('fill', 'white')
          .attr('text-anchor', 'middle')
          .text((a, idx) => {
            const divergence = (a.value - actual.value).toFixed(1)
    
            let text = ''
            if (divergence > 0) text += '+'
            text += `${divergence}%`
    
            return idx !== i ? text : '';
          })
    
      })
      .on('mouseleave', function() {
        d3.selectAll('.value')
          .attr('opacity', 1)
    
        d3.select(this)
          .transition()
          .duration(300)
          .attr('opacity', 1)
          .attr('x', (a) => xScale(a.month))
          .attr('width', xScale.bandwidth())
    
        chart.selectAll('#limit').remove()
        chart.selectAll('.divergence').remove()
      })
    
    barGroups
      .append('text')
      .attr('class', 'value')
      .attr('x', (a) => xScale(a.month) + xScale.bandwidth() / 2)
      .attr('y', (a) => yScale(a.value) + 30)
      .attr('text-anchor', 'middle')
      .text((a) => `${a.value}%`)
    
    svg
      .append('text')
      .attr('class', 'label')
      .attr('x', -(height / 2) - margin)
      .attr('y', margin / 2.4)
      .attr('transform', 'rotate(-90)')
      .attr('text-anchor', 'middle')
      .text('Love meter (%)')
    
    svg.append('text')
      .attr('class', 'label')
      .attr('x', width / 2 + margin)
      .attr('y', height + margin * 1.7)
      .attr('text-anchor', 'middle')
      .text('Months')
    
    svg.append('text')
      .attr('class', 'title')
      .attr('x', width / 2 + margin)
      .attr('y', 40)
      .attr('text-anchor', 'middle')
      .text('Most loved programming languages in 2018')
    
    svg.append('text')
      .attr('class', 'source')
      .attr('x', width - margin / 2)
      .attr('y', height + margin * 1.7)
      .attr('text-anchor', 'start')
      .text('Source: Stack Overflow, 2018')
    body {
      font-family: 'Open Sans', sans-serif;
    }
    
    div#layout {
      text-align: center;
    }
    
    div#container {
      width: 1000px;
      height: 600px;
      margin: auto;
      background-color: #2F4A6D;
    }
    
    svg {
      width: 100%;
      height: 100%;
    }
    
    .bar {
      fill: #80cbc4;
    }
    
    text {
      font-size: 12px;
      fill: #fff;
    }
    
    path {
      stroke: gray;
    }
    
    line {
      stroke: gray;
    }
    
    line#limit {
      stroke: #FED966;
      stroke-width: 3;
      stroke-dasharray: 3 6;
    }
    
    .grid path {
      stroke-width: 0;
    }
    
    .grid .tick line {
      stroke: #9FAAAE;
      stroke-opacity: 0.3;
    }
    
    text.divergence {
      font-size: 14px;
      fill: #2F4A6D;
    }
    
    text.value {
      font-size: 14px;
    }
    
    text.title {
      font-size: 22px;
      font-weight: 600;
    }
    
    text.label {
      font-size: 14px;
      font-weight: 400;
    }
    
    text.source {
      font-size: 10px;
    }
    
    
    
    
    

    0 讨论(0)
提交回复
热议问题