我在 Chart.js 中有一个条形图(使用最新版本),我想当鼠标悬停在类别标签上时进行一些视觉变化。我将如何实现以下视觉变化中的一个或两个?
相关问题在这里:如何检测图表js 3.7.1轴标签上的点击?。但是,我的问题是如何将鼠标悬停在标签上,而不单击标签。
在下面的示例中,我希望将鼠标悬停在这些文本上时发生一些事情:
Item A
、Item B
、Item C
。
window.onload = function() {
var ctx = document.getElementById('myChart').getContext('2d');
window.myBar = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Item A', 'Item B', 'Item C'],
datasets: [{
data: [1, 2, 3],
backgroundColor: 'lightblue'
}]
},
options: {
responsive: true,
indexAxis: 'y',
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false
},
}
}
});
};
.chart-container {
position: relative;
height: 90vh;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<div class="chart-container">
<canvas id="myChart"></canvas>
</div>
要使光标悬停在标签上时成为指针,您可以尝试在触发悬停时将 CSS 光标值分配给
event.native.target.style.cursor
。
event.native.target.style.cursor = 'pointer';
要在标签悬停时使标签具有不同的颜色,您可以尝试此操作
myChart.config.options.scales.y.ticks.color = hoverColors; // ['black','red','black'], ['black','black','red'], ['red','black','black']
更新
感谢LeeLenalee给出了几乎正确的答案。我已经编辑了上面的代码,使其符合问题的要求。不要忘记更改 HTML 中的库源:
https://cdn.jsdelivr.net/npm/[email protected]
至:
https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js
更新代码:
window.onload = function() {
const findLabel = (labels, evt) => {
let found = false;
let res = null;
try {
labels.forEach(l => {
l.labels.forEach((label, index) => {
if (evt.x > label.x && evt.x < label.x2 && evt.y > label.y && evt.y < label.y2) {
res = {
label: label.label,
index
};
found = true;
}
});
});
} catch (e) {}
return [found, res];
};
const getLabelHitboxes = (scales) => {
try {
return Object.values(scales).map((s) => ({
scaleId: s.id,
labels: s._labelItems.map((e, i) => ({
x: e.translation[0] - s._labelSizes.widths[i],
x2: e.translation[0] + s._labelSizes.widths[i] / 2,
y: e.translation[1] - s._labelSizes.heights[i] / 2,
y2: e.translation[1] + s._labelSizes.heights[i] / 2,
label: e.label,
index: i
}))
}));
} catch (e) {}
};
const changeCursorAndLabelColor = (event, chart, index, hoverMode) => {
// your hover color here
// const hoverColor = '#ff0000';
const hoverColor = 'red';
const hoverColors = [];
for (let i = 0; i < myChart.data.datasets[0].data.length; i++) {
if (hoverMode) {
// change cursor
event.native.target.style.cursor = 'pointer';
if (index === i) {
hoverColors.push(hoverColor);
} else {
hoverColors.push(defaultLabelColor);
}
} else {
// change cursor
event.native.target.style.cursor = 'default';
hoverColors.push(defaultLabelColor);
}
}
// change label to your hover color
myChart.config.options.scales.y.ticks.color = hoverColors;
// update chart when hover is triggered
myChart.update();
}
let foundMode = false;
const plugin = {
id: 'customHover',
afterEvent: (chart, event, opts) => {
const evt = event.event;
if (evt.type !== 'mousemove') {
return;
}
const [found, labelInfo] = findLabel(getLabelHitboxes(chart.scales), evt);
if (found && myChart.data.labels.includes(labelInfo.label)) {
changeCursorAndLabelColor(evt, chart, labelInfo.index, true);
foundMode = true;
} else {
if (foundMode) changeCursorAndLabelColor(evt, chart, null, false);
foundMode = false;
}
}
}
Chart.register(plugin);
var ctx = document.getElementById('myChart');
const myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Item A', 'Item B', 'Item C'],
datasets: [{
label: 'My Data',
data: [1, 2, 3],
backgroundColor: 'lightblue'
}]
},
options: {
responsive: true,
indexAxis: 'y',
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false
},
},
onHover: (event, chart) => {
if (foundMode) changeCursorAndLabelColor(event, chart, null, false);
foundMode = false;
}
}
});
const defaultLabelColor = myChart.config.options.scales.y.ticks.color;
};
.chart-container {
position: relative;
height: 90vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
<div class="chart-container">
<canvas id="myChart"></canvas>
</div>
您可以使用该问题中的自定义插件并忽略除鼠标移动事件之外的所有内容,而不是忽略除单击事件之外的所有内容:
const findLabel = (labels, evt) => {
let found = false;
let res = null;
labels.forEach(l => {
l.labels.forEach((label, index) => {
if (evt.x > label.x && evt.x < label.x2 && evt.y > label.y && evt.y < label.y2) {
res = {
label: label.label,
index
};
found = true;
}
});
});
return [found, res];
};
const getLabelHitboxes = (scales) => (Object.values(scales).map((s) => ({
scaleId: s.id,
labels: s._labelItems.map((e, i) => ({
x: e.translation[0] - s._labelSizes.widths[i],
x2: e.translation[0] + s._labelSizes.widths[i] / 2,
y: e.translation[1] - s._labelSizes.heights[i] / 2,
y2: e.translation[1] + s._labelSizes.heights[i] / 2,
label: e.label,
index: i
}))
})));
const plugin = {
id: 'customHover',
afterEvent: (chart, event, opts) => {
const evt = event.event;
if (evt.type !== 'mousemove') {
return;
}
const [found, labelInfo] = findLabel(getLabelHitboxes(chart.scales), evt);
if (found) {
console.log(labelInfo);
}
}
}
Chart.register(plugin);
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderColor: 'pink'
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderColor: 'orange'
}
]
},
options: {}
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
</body>
要将鼠标悬停在 Chart.js 条形图中的类别标签上时将光标更改为指针,您可以添加:
options: {
plugins: {
tooltip: {
mode: 'index',
intersect: false
},
},
interaction: {
mode: 'index',
intersect: false
},
onHover: function(evt, elements) {
if (elements.length) {
document.getElementById("myChart").style.cursor = "pointer";
} else {
document.getElementById("myChart").style.cursor = "default";
}
},
// ...
}
要更改悬停在标签上时的颜色,您可以添加:
options: {
plugins: {
tooltip: {
mode: 'index',
intersect: false
},
},
interaction: {
mode: 'index',
intersect: false
},
onHover: function(evt, elements) {
if (elements.length) {
var chart = evt.chart;
var datasetIndex = elements[0].datasetIndex;
var index = elements[0].index;
chart.data.labels[index] = '<span style="color: red;">' + chart.data.labels[index] + '</span>';
chart.update();
} else {
var chart = evt.chart;
chart.data.labels = ['Item A', 'Item B', 'Item C'];
chart.update();
}
},
// ...
}
我能够获取的数据是console.log,现在我想在y轴上显示工具提示,因为有时我的标签太长,我已经缩短了它,我已经添加了代码。鼠标悬停有效,但在图表上。update('none');我收到错误,
core.mjs:8400 ERROR RangeError: Maximum call stack size exceeded
at Object.ownKeys (helpers.segment.js:1628:13)
at Reflect.ownKeys (<anonymous>)
at Object.ownKeys (helpers.segment.js:1672:22)
at Function.keys (<anonymous>)
at he (helpers.segment.js:136:25)
at me (helpers.segment.js:158:19)
at ue (helpers.segment.js:176:7)
at ue (chartjs-plugin-datalabels.esm.js:1103:13)
at Object.afterDatasetUpdate (chartjs-plugin-datalabels.esm.js:1245:18)
at ee (helpers.segment.js:92:15)
if (found) {
ctx.save();
let lblText = labelInfo.label.match(/.{1,15}/g);
let lblLen = lblText.length;
ctx.fillStyle = 'rgba(51, 51, 51, 1)';
ctx.roundRect(labelInfo.x - 5, labelInfo.y - 10, 100, lblLen * 15, 10);
ctx.fill();
ctx.fillStyle = '#fff';
ctx.font = '9 px Arial';
for (let i = 0; i < lblText.length; i++) {
ctx.fillText(lblText[i], labelInfo.x, labelInfo.y + i * 15);
}
ctx.restore();
} else {
chart.update('none');
}