[英]How to add a separator in D3 bar chart
我的应用程序中有一个使用D3绘制的条形图。 示例代码已添加到下面。 在我的图表中,它显示了12个月内的几个月。 因此,根据情况,十二月不一定总是到达x轴的拐角处。 因此,为了显示年份的分隔,我想在图表中显示一个分隔两年的分隔符。 有没有办法可以做到。 以下是我的要求的图像。 有谁知道如何做到这一点?
https://jsfiddle.net/yasirunilan/w7h81xz2/748/
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));
// 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')
这是使用现有比例尺添加线和日期的简单方法。
// 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; }
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet"> <script src="https://d3js.org/d3.v4.min.js"></script> <div id='layout'> <!-- <h2>Bar chart example</h2> --> <div id='container'> <svg /> </div> </div>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.