英文:
Skip chartjs tick label if they overlap
问题
这是从此帖子继续下来的最终部分。
我有以下(持续进行中的)Chart.js 设置(感谢 @winnder_joiner)。
const d0 = new Date("2023-02-15T00:00:00.721Z").getTime();
const d1 = new Date("2023-02-15T00:03:00.721Z").getTime();
const d2 = new Date("2023-02-15T02:30:00.721Z").getTime();
const d3 = new Date("2023-02-15T03:20:00.721Z").getTime();
const d4 = new Date("2023-02-15T05:05:00.721Z").getTime();
let values = [d0, d1, d2, d3, d4];
let data = {
labels: [''],
datasets: [{
label: 'up',
axis: 'y',
data: [d1],
backgroundColor: 'red',
}, {
label: 'down',
//axis: 'y',
data: [d2],
backgroundColor: 'yellow',
}, {
label: 'out',
// axis: 'y',
data: [d3],
backgroundColor: 'green',
}, {
label: 'up',
// axis: 'y',
data: [d4],
backgroundColor: 'red',
}]
};
const config = {
data,
type: 'bar',
options: {
elements: {
bar: {
borderWidth: 0
}
},
ticks: {
display: true
},
interaction: {
mode: 'dataset'
},
// tooltip: {
// mode: 'dataset'
// },
hover: {
mode: 'dataset'
},
plugins: {
legend: {
display: false,
},
title: {
display: false,
},
},
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
border: {
display: false,
},
min: d0,
ticks: {
source: 'auto',
maxRotation: 80,
minRotation: 60,
autoSkip: false,
// autoSkipPadding: 0,
// padding: 0,
// align: 'inner',
// crossAlign: 'near',
callback: function (value, index, ticks) {
return moment(value).format('HH:mm');
}
},
afterBuildTicks: axis => axis.ticks = values.map(v => ({ value: v }))
},
y: {
grid: {
display: false
},
stacked: true
},
}
}
};
new Chart(document.getElementById("chart"), config);
这也可以在这里看到。
回调函数afterBuildTicks
允许在实际数据点处绘制刻度标签,这很好。如果我们不使用afterBuildTicks
,它会自动计算一些点来绘制刻度标签。
唯一的问题是,如果两个数据点非常接近,例如,标签会重叠...
我的问题是...
是否有办法在存在重叠的情况下跳过绘制刻度标签?
英文:
This is the final piece following on from this post
I have the following (ongoing) chartjs setup (thanks to @winnder_joiner)
const d0 = new Date("2023-02-15T00:00:00.721Z").getTime()
const d1 = new Date("2023-02-15T00:03:00.721Z").getTime()
const d2 = new Date("2023-02-15T02:30:00.721Z").getTime()
const d3 = new Date("2023-02-15T03:20:00.721Z").getTime()
const d4 = new Date("2023-02-15T05:05:00.721Z").getTime()
let values = [d0, d1, d2, d3, d4];
let data = {
labels: [''],
datasets: [{
label: 'up',
axis: 'y',
data: [d1],
backgroundColor: 'red',
},{
label: 'down',
//axis: 'y',
data: [d2],
backgroundColor: 'yellow',
},{
label: 'out',
// axis: 'y',
data: [d3],
backgroundColor: 'green',
},{
label: 'up',
// axis: 'y',
data: [d4],
backgroundColor: 'red',
}
]
};
const config = {
data,
type: 'bar',
options:{
elements: {
bar: {
borderWidth: 0
}
},
ticks: {
display: true
},
interaction: {
mode: 'dataset'
},
// tooltip: {
// mode: 'dataset'
// },
hover: {
mode: 'dataset'
},
plugins: {
legend: {
display: false,
},
title: {
display: false,
},
},
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
border: {
display: false,
},
min: d0,
ticks: {
source: 'auto',
maxRotation: 80,
minRotation: 60,
autoSkip: false,
// autoSkipPadding: 0,
//padding: 0,
//align: 'inner',
//crossAlign: 'near',
callback: function(value, index, ticks) {
return moment(value).format('HH:mm');
}
},
afterBuildTicks: axis => axis.ticks = values.map(v => ({ value: v }))
},
y: {
grid: {
display: false
},
stacked: true
},
}
}};
new Chart(document.getElementById("chart"), config);
This can also be seen here
The callback afterBuildTicks
allows the ticks to be draw at the actual data points - which is good. If we don't use afterBuildTicks
it just automatically works out some points to draw the ticks.
The only problem occurs if two data points happen to be very close, eg we get the lables overlapping....
My question here is..
Is there any way to have it skip drawing the ticks label if there is such an overlap?
答案1
得分: 1
以下是翻译好的部分:
"Well As far as I know, you can prevent the overlap with the config (more than you already did), BUT you could do it yourself just calculating the diff between the ticks. You would have to eyeball die minDiff
, and in the tick generation return null if the ticks are too close."
"我了解的情况是,您可以通过配置来防止重叠(比您已经做的更多),但您也可以自己计算刻度之间的差异。您需要自己确定 minDiff
,并在生成刻度时,如果刻度太接近,返回 null。"
"I tried it in the following example:"
"(It is not prefect, because if there are many after each other, that are too short, "too many" ticks might be skipped)"
"> _In this example 11:29
could/should be shown, because 11:00
was not displayed."
"我在以下示例中尝试过:"
"(这不是完美的,因为如果有很多相邻的刻度太短,可能会跳过“太多”刻度)"
"> 在这个示例中,11:29
可能/应该显示,因为 11:00
没有显示。"
接下来是代码部分,不进行翻译。
英文:
Well As far as I know, you can prevent the overlap with the config (more than you already did), BUT you could do it yourself just calculating the diff between the ticks. You would have to eyeball die minDiff
, and in the tick generation return null if the ticks are too close.
I tried it in the following example:
(It is not prefect, because if there are many after each other, that are too short, "too many" ticks might be skipped)
> In this example 11:29
could/should be shown, because 11:00
was not displayed.
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
const d0 = moment.duration('08:50:00').asMinutes();
const d1 = moment.duration('09:00:00').asMinutes();
const d2 = moment.duration('10:45:00').asMinutes();
const d22 = moment.duration('11:00:00').asMinutes();
const d23 = moment.duration('11:29:00').asMinutes();
const d3 = moment.duration('17:35:00').asMinutes();
const d4 = moment.duration('19:00:00').asMinutes();
let values = [d0, d1, d2, d22, d23, d3, d4];
let data = {
labels: [''],
datasets: [{
label: 'up',
axis: 'y',
data: [d1],
backgroundColor: 'red',
},{
label: 'down',
axis: 'y',
data: [d2],
backgroundColor: 'yellow',
},{
label: 'uppsy',
axis: 'y',
data: [d22],
backgroundColor: 'green',
},{
label: 'uppsy1',
axis: 'y',
data: [d23],
backgroundColor: 'red',
},{
label: 'out',
axis: 'y',
data: [d3],
backgroundColor: 'yellow',
},{
label: 'up',
axis: 'y',
data: [d4],
backgroundColor: 'red',
}
]
};
const config = {
data,
type: 'bar',
options:{
plugins: {
tooltip: {
mode: 'dataset',
callbacks: {
label: function(item){
return moment().startOf('day').add({ minute: item.raw}).format('HH:mm');
}
}
},
legend: {
display: false,
},
title: {
display: false,
},
},
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
min: d0,
border: { display: false },
ticks: {
source: 'auto',
maxRotation: 80,
minRotation: 60,
autoSkip: false,
callback: function(value, index, ticks) {
let minDiff = 30;
if(index > 0 && value - ticks[index-1].value < minDiff){
console.info(ticks[index-1].value - value )
return null;
}
return moment().startOf('day').add({ minute: value}).format('HH:mm');
}
},
afterBuildTicks: axis => axis.ticks = values.map(v => ({ value: v }))
},
y: {
stacked: true,
grid: { display: false },
},
}
}};
new Chart(document.getElementById("chart"), config);
<!-- language: lang-html -->
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<script src="//cdn.jsdelivr.net/npm/moment@^2"></script>
<script src="//cdn.jsdelivr.net/npm/chartjs-adapter-moment@^1"></script>
<div class="chart" style="height:84px; width:350px;">
<canvas id="chart" ></canvas>
</div>
<!-- end snippet -->
Update -maybe better solution:
You could filter the possible ticks before drawing the ticks, this makes it easier to see, if the minDiff
is applied in the correct manner.
values = values.reduce( (pre, curr, index) => {
if(index == 0 || (curr - pre[pre.length-1] > minDiff )){
pre.push(curr);
}
return pre;
}, [])
Here the full working demo:
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
const d0 = moment.duration('08:50:00').asMinutes();
const d1 = moment.duration('09:00:00').asMinutes();
const d2 = moment.duration('10:45:00').asMinutes();
const d22 = moment.duration('11:00:00').asMinutes();
const d23 = moment.duration('11:29:00').asMinutes();
const d3 = moment.duration('17:35:00').asMinutes();
const d4 = moment.duration('19:00:00').asMinutes();
let minDiff = 30;
let values = [d0, d1, d2, d22, d23, d3, d4];
/* prepare the ticks */
values = values.reduce( (pre, curr, index) => {
if(index == 0 || (curr - pre[pre.length-1] > minDiff )){
pre.push(curr);
}
return pre;
}, [])
let data = {
labels: [''],
datasets: [{
label: 'up',
axis: 'y',
data: [d1],
backgroundColor: 'red',
},{
label: 'down',
axis: 'y',
data: [d2],
backgroundColor: 'yellow',
},{
label: 'uppsy',
axis: 'y',
data: [d22],
backgroundColor: 'green',
},{
label: 'uppsy1',
axis: 'y',
data: [d23],
backgroundColor: 'red',
},{
label: 'out',
axis: 'y',
data: [d3],
backgroundColor: 'yellow',
},{
label: 'up',
axis: 'y',
data: [d4],
backgroundColor: 'red',
}
]
};
const config = {
data,
type: 'bar',
options:{
plugins: {
tooltip: {
mode: 'dataset',
callbacks: {
label: function(item){
return moment().startOf('day').add({ minute: item.raw}).format('HH:mm');
}
}
},
legend: {
display: false,
},
title: {
display: false,
},
},
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
min: d0,
border: { display: false },
ticks: {
source: 'auto',
maxRotation: 80,
minRotation: 60,
autoSkip: false,
callback: function(value, index, ticks) {
return moment().startOf('day').add({ minute: value}).format('HH:mm');
}
},
afterBuildTicks: axis => axis.ticks = values.map(v => ({ value: v }))
},
y: {
stacked: true,
grid: { display: false },
},
}
}};
new Chart(document.getElementById("chart"), config);
<!-- language: lang-html -->
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<script src="//cdn.jsdelivr.net/npm/moment@^2"></script>
<script src="//cdn.jsdelivr.net/npm/chartjs-adapter-moment@^1"></script>
<div class="chart" style="height:84px; width:350px;">
<canvas id="chart" ></canvas>
</div>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论