D3.js 轴刻度标签旋转基于标签和栏的宽度
D3.js axis tick label rotation based on width of label and bar
我遇到了轴刻度标签长度的问题。例如,当标签比它代表的条宽时,我想旋转我的 x 轴刻度标签。我正在使用不同的数据集,所以希望这种行为因为条形和标签宽度的数量会发生变化。
有没有办法在D3中找到字符串的长度,以便与条的宽度进行比较?
我确实看到了使用 span 标记查找字符串大小的一般建议,但我认为这不是 D3 的选项。
下面的代码片段显示了我想在非常基本的层面上做的事情。
请注意:代码段中的代码不是我的,它是我在网上找到的代码,以便获得问题表示的快速示例。
// https://insights.whosebug.com/survey/2018/#technology-most-loved-dreaded-and-wanted-languages
const sample = [
{
language: 'This text should be rotated',
value: 78.9,
color: '#000000'
},
{
language: 'Python',
value: 68.0,
color: '#fbcb39'
},
{
language: 'Go',
value: 65.6,
color: '#65cedb'
},
{
language: 'JavaScript',
value: 61.9,
color: '#f9de3f'
},
];
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.language))
.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.language))
.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.language) - 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.language) + 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.language))
.attr('width', xScale.bandwidth())
chart.selectAll('#limit').remove()
chart.selectAll('.divergence').remove()
})
barGroups
.append('text')
.attr('class', 'value')
.attr('x', (a) => xScale(a.language) + 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('Languages')
svg.append('text')
.attr('class', 'title')
.attr('x', width / 2 + margin)
.attr('y', 40)
.attr('text-anchor', 'middle')
.text('Example')
svg.append('text')
.attr('class', 'source')
.attr('x', width - margin / 2)
.attr('y', height + margin * 1.7)
.attr('text-anchor', 'start')
.text('')
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;
}
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Bar chart with D3.js</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id='layout'>
<!-- <h2>Bar chart example</h2> -->
<div id='container'>
<svg />
</div>
</div>
</body>
</html>
这是重要的部分。您可以根据自己的喜好进行调整
chart.append('g')
.attr('transform', `translate(0, ${height})`)
.classed('axis', true) // class the x axis for selection
.call(d3.axisBottom(xScale));
d3.select('.axis')
.selectAll('text') // select all the text elements
.each(function(d){
if(this.getBBox().width > 50) { // getBBox gets bouinding box info on svg elements
d3.select(this).attr('transform','rotate(45)')
.style('text-anchor','start')
}})
我遇到了轴刻度标签长度的问题。例如,当标签比它代表的条宽时,我想旋转我的 x 轴刻度标签。我正在使用不同的数据集,所以希望这种行为因为条形和标签宽度的数量会发生变化。
有没有办法在D3中找到字符串的长度,以便与条的宽度进行比较? 我确实看到了使用 span 标记查找字符串大小的一般建议,但我认为这不是 D3 的选项。
下面的代码片段显示了我想在非常基本的层面上做的事情。 请注意:代码段中的代码不是我的,它是我在网上找到的代码,以便获得问题表示的快速示例。
// https://insights.whosebug.com/survey/2018/#technology-most-loved-dreaded-and-wanted-languages
const sample = [
{
language: 'This text should be rotated',
value: 78.9,
color: '#000000'
},
{
language: 'Python',
value: 68.0,
color: '#fbcb39'
},
{
language: 'Go',
value: 65.6,
color: '#65cedb'
},
{
language: 'JavaScript',
value: 61.9,
color: '#f9de3f'
},
];
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.language))
.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.language))
.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.language) - 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.language) + 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.language))
.attr('width', xScale.bandwidth())
chart.selectAll('#limit').remove()
chart.selectAll('.divergence').remove()
})
barGroups
.append('text')
.attr('class', 'value')
.attr('x', (a) => xScale(a.language) + 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('Languages')
svg.append('text')
.attr('class', 'title')
.attr('x', width / 2 + margin)
.attr('y', 40)
.attr('text-anchor', 'middle')
.text('Example')
svg.append('text')
.attr('class', 'source')
.attr('x', width - margin / 2)
.attr('y', height + margin * 1.7)
.attr('text-anchor', 'start')
.text('')
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;
}
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Bar chart with D3.js</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id='layout'>
<!-- <h2>Bar chart example</h2> -->
<div id='container'>
<svg />
</div>
</div>
</body>
</html>
这是重要的部分。您可以根据自己的喜好进行调整
chart.append('g')
.attr('transform', `translate(0, ${height})`)
.classed('axis', true) // class the x axis for selection
.call(d3.axisBottom(xScale));
d3.select('.axis')
.selectAll('text') // select all the text elements
.each(function(d){
if(this.getBBox().width > 50) { // getBBox gets bouinding box info on svg elements
d3.select(this).attr('transform','rotate(45)')
.style('text-anchor','start')
}})