D3 带多个标签的 Y 轴
D3 Y-axis with multiple labels
我想创建一个 d3 图表,其中 y 轴标签将如给定图像所示
它们是几个月 -> 低于相应的季度 -> 低于相应的年份
请帮助我如何创建这个。
这是实现此目的的一种方法。
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
// dimensions
const width = 800;
const height = 100;
const margin = { left: 20, right: 20 };
// add main svg element
const svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height);
// create x scale
const startDate = new Date(2019, 1, 01);
const endDate = new Date(2022, 0, 01);
const months = d3.timeMonth.range(startDate, endDate);
const x = d3.scaleTime()
.domain([startDate, endDate])
.range([margin.left, width - margin.right]);
// Month label
const monthAxis = g => g.append('g')
.call(d3.axisBottom(x)
// every month, show abbreviation
.ticks(d3.timeMonth.every(1), d3.timeFormat('%b')));
// Quarter label
// map from month index (Jan is 0) to quarter label
const monthToQuarter = new Map([
[2, 'Q1'], // March
[5, 'Q2'], // June
[8, 'Q3'], // September
[11, 'Q4'] // December
]);
const quarterAxis = g => g.append('g')
// move these labels 30 pixels down
.attr('transform', `translate(0,30)`)
.call(d3.axisBottom(x)
// don't add room for ticks
.tickSize(0)
// only have labels for Mar, Jun, Sep, and Dec
.tickValues(months.filter(d => monthToQuarter.has(d.getMonth())))
// show the quarter instead of the date
.tickFormat(d => monthToQuarter.get(d.getMonth())))
// remove the baseline
.call(g => g.select('.domain').remove())
// remove tick lines
.call(g => g.selectAll('.tick>line').remove())
// Year label
const yearAxis = g => {
const group = g.append('g')
// move these labels 60 pixels down
.attr('transform', `translate(0,60)`);
// data for line segments for year ranges
// lines go from february to january
const segments = months.filter(d => d.getMonth() === 1)
.map(feb => {
const jan = d3.timeMonth.offset(feb, 11);
return [feb, jan];
});
const y = 6;
// add lines
group.append('g')
.selectAll('line')
.data(segments)
.join('line')
.attr('x1', d => x(d[0]))
.attr('x2', d => x(d[1]))
.attr('y1', y)
.attr('y2', y)
.attr('stroke', 'lightgray');
// add circles
group.append('g')
.selectAll('circle')
.data(segments.flat())
.join('circle')
.attr('cx', d => x(d))
.attr('cy', y)
.attr('r', 3)
.attr('fill', 'lightgray');
// add labels for years
group.append('g')
.call(d3.axisBottom(x)
// don't add room for ticks
.tickSize(0)
// center year labels between July and August
.tickValues(
months
.filter(d => d.getMonth() === 6)
.map(d => d3.timeDay.offset(d, 15))
)
.tickFormat(d => d.getFullYear()))
// remove the baseline
.call(g => g.select('.domain').remove())
// remove tick lines
.call(g => g.selectAll('.tick>line').remove())
/*
Duplicate the label and make it have a thick
white stroke. This provides a white background
to the labels so that the line does not show
behind them.
The same technique is used here:
https://observablehq.com/@d3/collapsible-tree
*/
.call(g => g.selectAll('.tick>text').clone()
.lower()
.attr('stroke', 'white')
.attr('stroke-width', '4')
.attr('fill', null)
.text(d => d.getFullYear()))
}
// Draw axes
svg.append('g')
.call(monthAxis)
.call(quarterAxis)
.call(yearAxis);
</script>
</body>
</html>
这增加了三个轴,一个在另一个下面。多年来的轴需要更多的工作来处理直线和圆圈。
输出如下:
我想创建一个 d3 图表,其中 y 轴标签将如给定图像所示
它们是几个月 -> 低于相应的季度 -> 低于相应的年份
请帮助我如何创建这个。
这是实现此目的的一种方法。
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
// dimensions
const width = 800;
const height = 100;
const margin = { left: 20, right: 20 };
// add main svg element
const svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height);
// create x scale
const startDate = new Date(2019, 1, 01);
const endDate = new Date(2022, 0, 01);
const months = d3.timeMonth.range(startDate, endDate);
const x = d3.scaleTime()
.domain([startDate, endDate])
.range([margin.left, width - margin.right]);
// Month label
const monthAxis = g => g.append('g')
.call(d3.axisBottom(x)
// every month, show abbreviation
.ticks(d3.timeMonth.every(1), d3.timeFormat('%b')));
// Quarter label
// map from month index (Jan is 0) to quarter label
const monthToQuarter = new Map([
[2, 'Q1'], // March
[5, 'Q2'], // June
[8, 'Q3'], // September
[11, 'Q4'] // December
]);
const quarterAxis = g => g.append('g')
// move these labels 30 pixels down
.attr('transform', `translate(0,30)`)
.call(d3.axisBottom(x)
// don't add room for ticks
.tickSize(0)
// only have labels for Mar, Jun, Sep, and Dec
.tickValues(months.filter(d => monthToQuarter.has(d.getMonth())))
// show the quarter instead of the date
.tickFormat(d => monthToQuarter.get(d.getMonth())))
// remove the baseline
.call(g => g.select('.domain').remove())
// remove tick lines
.call(g => g.selectAll('.tick>line').remove())
// Year label
const yearAxis = g => {
const group = g.append('g')
// move these labels 60 pixels down
.attr('transform', `translate(0,60)`);
// data for line segments for year ranges
// lines go from february to january
const segments = months.filter(d => d.getMonth() === 1)
.map(feb => {
const jan = d3.timeMonth.offset(feb, 11);
return [feb, jan];
});
const y = 6;
// add lines
group.append('g')
.selectAll('line')
.data(segments)
.join('line')
.attr('x1', d => x(d[0]))
.attr('x2', d => x(d[1]))
.attr('y1', y)
.attr('y2', y)
.attr('stroke', 'lightgray');
// add circles
group.append('g')
.selectAll('circle')
.data(segments.flat())
.join('circle')
.attr('cx', d => x(d))
.attr('cy', y)
.attr('r', 3)
.attr('fill', 'lightgray');
// add labels for years
group.append('g')
.call(d3.axisBottom(x)
// don't add room for ticks
.tickSize(0)
// center year labels between July and August
.tickValues(
months
.filter(d => d.getMonth() === 6)
.map(d => d3.timeDay.offset(d, 15))
)
.tickFormat(d => d.getFullYear()))
// remove the baseline
.call(g => g.select('.domain').remove())
// remove tick lines
.call(g => g.selectAll('.tick>line').remove())
/*
Duplicate the label and make it have a thick
white stroke. This provides a white background
to the labels so that the line does not show
behind them.
The same technique is used here:
https://observablehq.com/@d3/collapsible-tree
*/
.call(g => g.selectAll('.tick>text').clone()
.lower()
.attr('stroke', 'white')
.attr('stroke-width', '4')
.attr('fill', null)
.text(d => d.getFullYear()))
}
// Draw axes
svg.append('g')
.call(monthAxis)
.call(quarterAxis)
.call(yearAxis);
</script>
</body>
</html>
这增加了三个轴,一个在另一个下面。多年来的轴需要更多的工作来处理直线和圆圈。
输出如下: