所有堆叠的 Chartjs 堆叠条单独的工具提示
Chartjs stacked bar separate tooltip for all stacked
我想为每个堆叠条制作一个单独的工具提示,例如。我的演示“2022-01-17”有两个带有四个值的堆叠条,但我总共需要 Stack 1 组和 Stack 2 组
我已经查看了 chartjs 中的大部分选项 https://www.chartjs.org/docs/3.5.1/samples/bar/stacked-groups.html
var barChartData = {
labels: ["2022-01-17","2022-01-18","2022-01-19","2022-01-20","2022-01-21","2022-01-22","2022-01-23","2022-01-24","2022-01-25","2022-01-26","2022-01-27","2022-01-28","2022-01-29","2022-01-30"],
datasets: [{"label":"Product 2","data":["292.53","328.5","273.83","305.44","260.33","251.87","118.15","253.95","86.64","87.78","116.68","295.49","61.32","83.78"],"backgroundColor":"#66bb6a","borderColor":"#66bb6a","pointBackgroundColor":"#66bb6a","stack":"Stack 0"},{"label":"Product ","data":["1522.27","1844.83","1581.01","2767.68","2821.36","2940.31","2876.1","2037.79","1593.01","1900.86","1607.21","2188.92","2428.74","2508.81"],"backgroundColor":"#1b5e20","borderColor":"#1b5e20","pointBackgroundColor":"#1b5e20","stack":"Stack 0"},{"label":"Product 2","data":["200","4.14","28.51","13.68","0","0","19.93","0","0","0","10.47","23.05","9.42","10.58"],"backgroundColor":"#ffcdd2","borderColor":"#ffcdd2","pointBackgroundColor":"#ffcdd2","stack":"Stack 1"},{"label":"Product ","data":["680.2","536.51","524.41","479.69","453.19","521.87","530.57","485.13","440.25","591.29","722.73","711.58","686.63","510.72"],"backgroundColor":"#ef9a9a","borderColor":"#ef9a9a","pointBackgroundColor":"#ef9a9a","stack":"Stack 1"}]
};
const footer = (tooltipItems) => {
let sum = 0;
tooltipItems.forEach(function(tooltipItem) {
sum += tooltipItem.parsed.y;
});
return 'Sum: ' + sum;
};
var ctx = document.getElementById("canvas").getContext("2d");
var myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
options: {
interaction: {
intersect: false,
mode: 'index',
},
plugins: {
tooltip: {
callbacks: {
footer: (tooltipItem) => {
let sum = 0;
tooltipItem.forEach(function(tooltipItem) {
sum += tooltipItem.parsed.y;
});
return 'Sum: ' + sum;
}
}
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
<canvas id="canvas" height="100"></canvas>
要获取每个堆栈的总数,您可以使用在工具提示上下文中找到的dataPoints
并使用数据集标签按每个堆栈分组
// group stacks
const groups = {};
tooltip.dataPoints.forEach(function (point) {
if (groups.hasOwnProperty(barChartData.datasets[point.datasetIndex].label)) {
groups[barChartData.datasets[point.datasetIndex].label] += parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
} else {
groups[barChartData.datasets[point.datasetIndex].label] = parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
}
});
例如--> {"Product 2":492.53,"Product ":2202.4700000000003}
然后使用 external
选项创建自定义工具提示
请参阅以下工作片段...
$(document).ready(function() {
var barChartData = {
labels: ["2022-01-17","2022-01-18","2022-01-19","2022-01-20","2022-01-21","2022-01-22","2022-01-23","2022-01-24","2022-01-25","2022-01-26","2022-01-27","2022-01-28","2022-01-29","2022-01-30"],
datasets: [{"label":"Product 2","data":["292.53","328.5","273.83","305.44","260.33","251.87","118.15","253.95","86.64","87.78","116.68","295.49","61.32","83.78"],"backgroundColor":"#66bb6a","borderColor":"#66bb6a","pointBackgroundColor":"#66bb6a","stack":"Stack 0"},{"label":"Product ","data":["1522.27","1844.83","1581.01","2767.68","2821.36","2940.31","2876.1","2037.79","1593.01","1900.86","1607.21","2188.92","2428.74","2508.81"],"backgroundColor":"#1b5e20","borderColor":"#1b5e20","pointBackgroundColor":"#1b5e20","stack":"Stack 0"},{"label":"Product 2","data":["200","4.14","28.51","13.68","0","0","19.93","0","0","0","10.47","23.05","9.42","10.58"],"backgroundColor":"#ffcdd2","borderColor":"#ffcdd2","pointBackgroundColor":"#ffcdd2","stack":"Stack 1"},{"label":"Product ","data":["680.2","536.51","524.41","479.69","453.19","521.87","530.57","485.13","440.25","591.29","722.73","711.58","686.63","510.72"],"backgroundColor":"#ef9a9a","borderColor":"#ef9a9a","pointBackgroundColor":"#ef9a9a","stack":"Stack 1"}]
};
var ctx = document.getElementById("canvas").getContext("2d");
var myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
options: {
interaction: {
intersect: false,
mode: 'index',
},
plugins: {
tooltip: {
enabled: false,
position: 'nearest',
external: function (context) {
// init
const {chart, tooltip} = context;
// remove old tooltip
var container = chart.canvas.parentNode.querySelector('.tooltip');
if (container) {
chart.canvas.parentNode.removeChild(container);
}
// determine if tooltip exists
if (tooltip.opacity === 0) {
return;
}
// group stacks
const groups = {};
tooltip.dataPoints.forEach(function (point) {
if (groups.hasOwnProperty(barChartData.datasets[point.datasetIndex].label)) {
groups[barChartData.datasets[point.datasetIndex].label] += parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
} else {
groups[barChartData.datasets[point.datasetIndex].label] = parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
}
});
// build tooltip rows
var rows = '';
Object.keys(groups).forEach(function (groupName) {
rows += renderTemplate('template-tooltip-row', {
group: groupName,
value: groups[groupName].toLocaleString(undefined, {minimumFractionDigits: 2})
});
});
// build tooltip
chart.canvas.parentNode.insertAdjacentHTML('beforeEnd', renderTemplate('template-tooltip', {
rows: rows,
title: tooltip.title[0]
}));
// position tooltip
const {offsetLeft: positionX, offsetTop: positionY} = chart.canvas;
container = chart.canvas.parentNode.querySelector('.tooltip');
container.style.left = positionX + tooltip.caretX + 'px';
container.style.top = positionY + tooltip.caretY + 'px';
container.style.font = tooltip.options.bodyFont.string;
container.style.padding = tooltip.options.padding + 'px ' + tooltip.options.padding + 'px';
}
}
}
}
});
/**
* render html template
* @param {string} templateId - id of html template
* @param {object} templateValues - values for each template placeholder
* @return {string} template content
*/
function renderTemplate(templateId, templateValues) {
var propHandle; // property key
var templateText; // html template content
var templateValue; // value for template placeholder
// get template content, replace each placeholder with value
templateText = document.querySelector('#' + templateId).innerHTML;
if (templateValues) {
for (propHandle in templateValues) {
if (templateValues.hasOwnProperty(propHandle)) {
templateValue = '';
// convert template value to string
if (templateValues[propHandle] !== null) {
if (templateValues[propHandle].hasOwnProperty('results')) {
templateValue = encodeURIComponent(JSON.stringify(templateValues[propHandle].results));
} else {
templateValue = templateValues[propHandle].toString();
}
}
// handle dollar sign in template value
if (templateValue.indexOf('$') > -1) {
templateValue = templateValue.replace(new RegExp('\$', 'g'), '$$$');
}
// replace template placeholder(s) with template value
if (templateText.indexOf('{{' + propHandle + '}}') > -1) {
templateText = templateText.replace(
new RegExp('{{' + propHandle + '}}', 'g'),
templateValue
);
}
}
}
}
return templateText.trim();
}
});
.align-right {
text-align: right;
}
.table {
border-collapse: separate;
border-spacing: 0vw 0vw;
display: table;
}
.table-body {
display: table-row-group;
}
.table-cell {
display: table-cell;
padding: 4px;
}
.table-foot {
display: table-footer-group;
}
.table-head {
display: table-header-group;
}
.table-row {
display: table-row;
}
.title {
font-weight: bold;
}
.tooltip {
background-color: rgba(0, 0, 0, 0.85);
border-radius: 3px;
color: #ffffff;
pointer-events: none;
position: absolute;
transform: translate(-50%, 0);
transition: all 0.1s ease;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
<canvas id="canvas" height="100"></canvas>
<script id="template-tooltip" type="text/html">
<div class="tooltip">
<div class="title">{{title}}</div>
<div class="table">
<div class="table-body">{{rows}}</div>
</div>
</div>
</script>
<script id="template-tooltip-row" type="text/html">
<div class="table-row">
<div class="table-cell title">{{group}}:</div>
<div class="table-cell align-right">{{value}}</div>
</div>
</script>
我想为每个堆叠条制作一个单独的工具提示,例如。我的演示“2022-01-17”有两个带有四个值的堆叠条,但我总共需要 Stack 1 组和 Stack 2 组
我已经查看了 chartjs 中的大部分选项 https://www.chartjs.org/docs/3.5.1/samples/bar/stacked-groups.html
var barChartData = {
labels: ["2022-01-17","2022-01-18","2022-01-19","2022-01-20","2022-01-21","2022-01-22","2022-01-23","2022-01-24","2022-01-25","2022-01-26","2022-01-27","2022-01-28","2022-01-29","2022-01-30"],
datasets: [{"label":"Product 2","data":["292.53","328.5","273.83","305.44","260.33","251.87","118.15","253.95","86.64","87.78","116.68","295.49","61.32","83.78"],"backgroundColor":"#66bb6a","borderColor":"#66bb6a","pointBackgroundColor":"#66bb6a","stack":"Stack 0"},{"label":"Product ","data":["1522.27","1844.83","1581.01","2767.68","2821.36","2940.31","2876.1","2037.79","1593.01","1900.86","1607.21","2188.92","2428.74","2508.81"],"backgroundColor":"#1b5e20","borderColor":"#1b5e20","pointBackgroundColor":"#1b5e20","stack":"Stack 0"},{"label":"Product 2","data":["200","4.14","28.51","13.68","0","0","19.93","0","0","0","10.47","23.05","9.42","10.58"],"backgroundColor":"#ffcdd2","borderColor":"#ffcdd2","pointBackgroundColor":"#ffcdd2","stack":"Stack 1"},{"label":"Product ","data":["680.2","536.51","524.41","479.69","453.19","521.87","530.57","485.13","440.25","591.29","722.73","711.58","686.63","510.72"],"backgroundColor":"#ef9a9a","borderColor":"#ef9a9a","pointBackgroundColor":"#ef9a9a","stack":"Stack 1"}]
};
const footer = (tooltipItems) => {
let sum = 0;
tooltipItems.forEach(function(tooltipItem) {
sum += tooltipItem.parsed.y;
});
return 'Sum: ' + sum;
};
var ctx = document.getElementById("canvas").getContext("2d");
var myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
options: {
interaction: {
intersect: false,
mode: 'index',
},
plugins: {
tooltip: {
callbacks: {
footer: (tooltipItem) => {
let sum = 0;
tooltipItem.forEach(function(tooltipItem) {
sum += tooltipItem.parsed.y;
});
return 'Sum: ' + sum;
}
}
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
<canvas id="canvas" height="100"></canvas>
要获取每个堆栈的总数,您可以使用在工具提示上下文中找到的dataPoints
并使用数据集标签按每个堆栈分组
// group stacks
const groups = {};
tooltip.dataPoints.forEach(function (point) {
if (groups.hasOwnProperty(barChartData.datasets[point.datasetIndex].label)) {
groups[barChartData.datasets[point.datasetIndex].label] += parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
} else {
groups[barChartData.datasets[point.datasetIndex].label] = parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
}
});
例如--> {"Product 2":492.53,"Product ":2202.4700000000003}
然后使用 external
选项创建自定义工具提示
请参阅以下工作片段...
$(document).ready(function() {
var barChartData = {
labels: ["2022-01-17","2022-01-18","2022-01-19","2022-01-20","2022-01-21","2022-01-22","2022-01-23","2022-01-24","2022-01-25","2022-01-26","2022-01-27","2022-01-28","2022-01-29","2022-01-30"],
datasets: [{"label":"Product 2","data":["292.53","328.5","273.83","305.44","260.33","251.87","118.15","253.95","86.64","87.78","116.68","295.49","61.32","83.78"],"backgroundColor":"#66bb6a","borderColor":"#66bb6a","pointBackgroundColor":"#66bb6a","stack":"Stack 0"},{"label":"Product ","data":["1522.27","1844.83","1581.01","2767.68","2821.36","2940.31","2876.1","2037.79","1593.01","1900.86","1607.21","2188.92","2428.74","2508.81"],"backgroundColor":"#1b5e20","borderColor":"#1b5e20","pointBackgroundColor":"#1b5e20","stack":"Stack 0"},{"label":"Product 2","data":["200","4.14","28.51","13.68","0","0","19.93","0","0","0","10.47","23.05","9.42","10.58"],"backgroundColor":"#ffcdd2","borderColor":"#ffcdd2","pointBackgroundColor":"#ffcdd2","stack":"Stack 1"},{"label":"Product ","data":["680.2","536.51","524.41","479.69","453.19","521.87","530.57","485.13","440.25","591.29","722.73","711.58","686.63","510.72"],"backgroundColor":"#ef9a9a","borderColor":"#ef9a9a","pointBackgroundColor":"#ef9a9a","stack":"Stack 1"}]
};
var ctx = document.getElementById("canvas").getContext("2d");
var myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
options: {
interaction: {
intersect: false,
mode: 'index',
},
plugins: {
tooltip: {
enabled: false,
position: 'nearest',
external: function (context) {
// init
const {chart, tooltip} = context;
// remove old tooltip
var container = chart.canvas.parentNode.querySelector('.tooltip');
if (container) {
chart.canvas.parentNode.removeChild(container);
}
// determine if tooltip exists
if (tooltip.opacity === 0) {
return;
}
// group stacks
const groups = {};
tooltip.dataPoints.forEach(function (point) {
if (groups.hasOwnProperty(barChartData.datasets[point.datasetIndex].label)) {
groups[barChartData.datasets[point.datasetIndex].label] += parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
} else {
groups[barChartData.datasets[point.datasetIndex].label] = parseFloat(barChartData.datasets[point.datasetIndex].data[point.dataIndex]);
}
});
// build tooltip rows
var rows = '';
Object.keys(groups).forEach(function (groupName) {
rows += renderTemplate('template-tooltip-row', {
group: groupName,
value: groups[groupName].toLocaleString(undefined, {minimumFractionDigits: 2})
});
});
// build tooltip
chart.canvas.parentNode.insertAdjacentHTML('beforeEnd', renderTemplate('template-tooltip', {
rows: rows,
title: tooltip.title[0]
}));
// position tooltip
const {offsetLeft: positionX, offsetTop: positionY} = chart.canvas;
container = chart.canvas.parentNode.querySelector('.tooltip');
container.style.left = positionX + tooltip.caretX + 'px';
container.style.top = positionY + tooltip.caretY + 'px';
container.style.font = tooltip.options.bodyFont.string;
container.style.padding = tooltip.options.padding + 'px ' + tooltip.options.padding + 'px';
}
}
}
}
});
/**
* render html template
* @param {string} templateId - id of html template
* @param {object} templateValues - values for each template placeholder
* @return {string} template content
*/
function renderTemplate(templateId, templateValues) {
var propHandle; // property key
var templateText; // html template content
var templateValue; // value for template placeholder
// get template content, replace each placeholder with value
templateText = document.querySelector('#' + templateId).innerHTML;
if (templateValues) {
for (propHandle in templateValues) {
if (templateValues.hasOwnProperty(propHandle)) {
templateValue = '';
// convert template value to string
if (templateValues[propHandle] !== null) {
if (templateValues[propHandle].hasOwnProperty('results')) {
templateValue = encodeURIComponent(JSON.stringify(templateValues[propHandle].results));
} else {
templateValue = templateValues[propHandle].toString();
}
}
// handle dollar sign in template value
if (templateValue.indexOf('$') > -1) {
templateValue = templateValue.replace(new RegExp('\$', 'g'), '$$$');
}
// replace template placeholder(s) with template value
if (templateText.indexOf('{{' + propHandle + '}}') > -1) {
templateText = templateText.replace(
new RegExp('{{' + propHandle + '}}', 'g'),
templateValue
);
}
}
}
}
return templateText.trim();
}
});
.align-right {
text-align: right;
}
.table {
border-collapse: separate;
border-spacing: 0vw 0vw;
display: table;
}
.table-body {
display: table-row-group;
}
.table-cell {
display: table-cell;
padding: 4px;
}
.table-foot {
display: table-footer-group;
}
.table-head {
display: table-header-group;
}
.table-row {
display: table-row;
}
.title {
font-weight: bold;
}
.tooltip {
background-color: rgba(0, 0, 0, 0.85);
border-radius: 3px;
color: #ffffff;
pointer-events: none;
position: absolute;
transform: translate(-50%, 0);
transition: all 0.1s ease;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
<canvas id="canvas" height="100"></canvas>
<script id="template-tooltip" type="text/html">
<div class="tooltip">
<div class="title">{{title}}</div>
<div class="table">
<div class="table-body">{{rows}}</div>
</div>
</div>
</script>
<script id="template-tooltip-row" type="text/html">
<div class="table-row">
<div class="table-cell title">{{group}}:</div>
<div class="table-cell align-right">{{value}}</div>
</div>
</script>