Как добавить разделитель в гистограмму D3 D3V3 - PullRequest
0 голосов
/ 15 ноября 2018

В моем приложении есть гистограмма, нарисованная с помощью D3V3.Пример кода был добавлен ниже.На моем графике показаны месяцы за 12 месяцев.Поэтому в зависимости от ситуации, декабрь не всегда подходит к углу оси X.Итак, чтобы показать разделение года, я хочу показать разделитель на диаграмме, чтобы разделить два года.Есть ли способ, который можно сделать.Ниже приведено изображение моего требования.Кто-нибудь знает как это сделать?Я нашел решение для этого для D3V4, используя отдельный вопрос , но это не работает для D3v3.

Пример кода приведен ниже.https://jsfiddle.net/yasirunilan/921fsugh/4/

sample

    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.scale.ordinal()
  .rangeRoundBands([0, width], 0.4)
  .domain(sample.map((s) => s.month))

const yScale = d3.scale.linear()
  .range([height, 0])
  .domain([0, 100]);

// vertical grid lines
// const makeXLines = () => d3.axisBottom()
//   .scale(xScale)

const makeYLines = () => d3.svg.axis().scale(yScale).orient("left")


chart.append('g')
  .attr('transform', `translate(0, ${height})`)
  .call(d3.svg.axis().scale(xScale).orient("bottom").tickSize(1));

chart.append('g')
  .call(d3.svg.axis().scale(yScale).orient("left").tickFormat(d3.format("d")).tickSize(1));

// 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.rangeBand())
  .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.rangeBand() + 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.rangeBand())

    chart.selectAll('#limit').remove()
    chart.selectAll('.divergence').remove()
  })

barGroups
  .append('text')
  .attr('class', 'value')
  .attr('x', (a) => xScale(a.month) + xScale.rangeBand() / 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')

1 Ответ

0 голосов
/ 16 ноября 2018

Если D3v3 не имеет функции scale.step(), создайте нужное место.

const dateLine = chart.append('g')
  .attr('transform', 'translate(' + (xScale('Dec') + xScale('Jan') + xScale.rangeBand()) * 0.5 + ', 0)');
dateLine.append('line')
  .attr('y2', height);

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.scale.ordinal()
      .rangeRoundBands([0, width], 0.4)
      .domain(sample.map((s) => s.month))

    const yScale = d3.scale.linear()
      .range([height, 0])
      .domain([0, 100]);

const dateLine = chart.append('g')
  .attr('transform', 'translate(' + (xScale('Dec') + xScale('Jan') + xScale.rangeBand()) * 0.5 + ', 0)');
dateLine.append('line')
  .attr('y2', height);

dateLine.append('text')
  .text('2018')
  .attr('x', 20)
  .attr('y', 20);

    // vertical grid lines
    // const makeXLines = () => d3.axisBottom()
    //   .scale(xScale)

    const makeYLines = () => d3.svg.axis().scale(yScale).orient("left")
      

    chart.append('g')
      .attr('transform', `translate(0, ${height})`)
      .call(d3.svg.axis().scale(xScale).orient("bottom").tickSize(1));

    chart.append('g')
      .call(d3.svg.axis().scale(yScale).orient("left").tickFormat(d3.format("d")).tickSize(1));

    // 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.rangeBand())
      .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.rangeBand() + 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.rangeBand())

        chart.selectAll('#limit').remove()
        chart.selectAll('.divergence').remove()
      })

    barGroups
      .append('text')
      .attr('class', 'value')
      .attr('x', (a) => xScale(a.month) + xScale.rangeBand() / 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;
}
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<script src="https://d3js.org/d3.v3.js"></script>
<div id='layout'>
  <div id='container'>
    <svg />
  </div>
</div>
...