所有堆叠的 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>