Chartjs - 在图表工具提示中插入额外的数据。

问题描述 投票:1回答:1

我想在甜甜圈图表中插入额外的数据。控制器传递给视图的数组是这样的。

[
  0 => array:3 [
    "profit" => 20
    "sex" => array:3 [
      0 => 0
      1 => 8
      2 => 0
    ]
    "count" => 8
  ]
  1 => array:3 [
    "profit" => 101.5
    "sex" => array:3 [
      0 => 4
      1 => 4
      2 => 0
    ]
    "count" => 8
  ]
  ...
]

使用 chartjs 和所有数组元素的fied利润,我创建了这个甜甜圈图。doughnut chart

但我会定制这个图的内容 tooltip 使 "性别 "广告的数据可见。我试着用下面的代码,但可变的是 data 只包含图表中的值。

config.options.tooltips.callbacks = {
  title: (tooltipItem, data) => {
    return data['labels'][tooltipItem[0]['index']];
  },
  label: (tooltipItem, data) => {
    return data['datasets'][0]['data'][tooltipItem['index']];
  },
  afterLabel: (tooltipItem, data) => {
    var dataset = data['datasets'][0];
    var percent = Math.round((dataset['data'][tooltipItem['index']] / dataset._meta[4].total) * 100)

    return `${percent} %`;
  },
  backgroundColor: '#FFF',
  titleFontSize: 16,
  titleFontColor: '#0066ff',
  bodyFontColor: '#000',
  bodyFontSize: 14,
  displayColors: false
}

我将数据传递给 config object 以此方式。config.data.datasets[0].data = data.map(el => el.profit); 我如何在工具提示中添加更多的数据,得到这样的东西?enter image description here

这是我的代码。

function createDonatsChart(ctx, title, data, labels, middleText, type) {

  Chart.pluginService.register({
    beforeDraw: function(chart) {
      if (chart.config.options.elements.center) {
        // Get ctx from string
        const ctx = chart.chart.ctx;

        // Get options from the center object in options
        const centerConfig = chart.config.options.elements.center;
        const fontStyle = centerConfig.fontStyle || 'Asap';
        const txt = centerConfig.text;
        const color = centerConfig.color || '#000';
        const maxFontSize = centerConfig.maxFontSize || 75;
        const sidePadding = centerConfig.sidePadding || 20;
        const sidePaddingCalculated = (sidePadding / 100) * (chart.innerRadius * 2)
        // Start with a base font of 30px
        ctx.font = `30px ${fontStyle}`;

        // Get the width of the string and also the width of the element minus 10 to give it 5px side padding
        const stringWidth = ctx.measureText(txt).width;
        const elementWidth = (chart.innerRadius * 2) - sidePaddingCalculated;

        // Find out how much the font can grow in width.
        const widthRatio = elementWidth / stringWidth;
        const newFontSize = Math.floor(30 * widthRatio);
        const elementHeight = (chart.innerRadius * 2);

        // Pick a new font size so it will not be larger than the height of label.
        const fontSizeToUse = Math.min(newFontSize, elementHeight, maxFontSize);
        const minFontSize = centerConfig.minFontSize;
        const lineHeight = centerConfig.lineHeight || 25;
        const wrapText = false;

        if (minFontSize === undefined) {
          minFontSize = 20;
        }

        if (minFontSize && fontSizeToUse < minFontSize) {
          fontSizeToUse = minFontSize;
          wrapText = true;
        }

        // Set font settings to draw it correctly.
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        const centerX = ((chart.chartArea.left + chart.chartArea.right) / 2);
        const centerY = ((chart.chartArea.top + chart.chartArea.bottom) / 2);
        ctx.font = `${fontSizeToUse}px ${fontStyle}`;
        ctx.fillStyle = color;

        if (!wrapText) {
          ctx.fillText(txt, centerX, centerY);
          return;
        }

        const words = txt.split(' ');
        let line = '';
        let lines = [];

        // Break words up into multiple lines if necessary
        for (let n = 0; n < words.length; n++) {
          const testLine = line + words[n] + ' ';
          const metrics = ctx.measureText(testLine);
          const testWidth = metrics.width;
          if (testWidth > elementWidth && n > 0) {
            lines.push(line);
            line = words[n] + ' ';
          } else {
            line = testLine;
          }
        }

        // Move the center up depending on line height and number of lines
        centerY -= (lines.length / 2) * lineHeight;

        for (let n = 0; n < lines.length; n++) {
          ctx.fillText(lines[n], centerX, centerY);
          centerY += lineHeight;
        }

        //Draw text in center
        ctx.fillText(line, centerX, centerY);
      }
    }
  });

  let config = {
    type: 'doughnut',
    data: {
      datasets: [{
        borderColor: '#121212',
        borderWidth: 8,
        backgroundColor: [
          '#49C6E5',
          '#EFC7C2',
          '#00BD9D',
          '#EF476F',
          '#FFD166',
        ]
      }],
      labels: labels
    },
    options: {
      responsive: true,
      tooltips: {

      },
      legend: {
        position: 'top',
        onClick: null
      },
      title: {
        display: true,
        color: '#6c757d',
        text: title,
        fontFamily: "'Asap', san-serif",
        fontSize: 20,
      },
      animation: {
        animateScale: true,
        animateRotate: true,
      },
      elements: {
        center: {
          text: middleText,
          color: '#6c757d',
          fontFamily: "'Asap', san-serif",
          sidePadding: 20,
          minFontSize: 12,
          lineHeight: 25,
        }
      },
    }
  };

  if ( type == 0 ) {
    config.options.events = [];
    config.data.datasets[0].data = data;
  }
  else {
  //   config.data.datasets[0].data = data.map(el => el.profit);
  //   config.options.tooltips.enabled = true;
  //   config.options.tooltips.callbacks = {
  //     title: (tooltipItem, data) => {
  //       return data['labels'][tooltipItem[0]['index']];
  //     },
  //     label: (tooltipItem, data) => {
  //       return data['datasets'][0]['data'][tooltipItem['index']];
  //     },
  //     afterLabel: (tooltipItem, data) => {
  //       var dataset = data['datasets'][0];
  //       var percent = Math.round((dataset['data'][tooltipItem['index']] / dataset._meta[4].total) * 100)
        
  //       return `${percent} %`;
  //     },
  //     backgroundColor: '#FFF',
  //     titleFontSize: 16,
  //     titleFontColor: '#0066ff',
  //     bodyFontColor: '#000',
  //     bodyFontSize: 14,
  //     displayColors: false
  //   }
    config.data.datasets[0].data = data.map(el => el.profit);
  }

  Chart.defaults.global.defaultFontFamily = 'Asap';
  Chart.defaults.doughnut.cutoutPercentage = 80;

  new Chart(ctx, config);
}





const data = [
  {
    count: 8,
    profit: 20,
    sex: [0, 8, 0]
  }, 
  {
    count: 8,
    profit: 101.5,
    sex: [4, 4, 0]
  },
  {
    count: 1,
    profit: 12.5,
    sex: [1, 0, 0]
  },
  {
    count: 2,
    profit: 4,
    sex: [2, 0, 0]
  },
  {
    count: 5,
    profit: 56.5,
    sex: [5, 0, 0]
  }
];


createDonatsChart(
  document.getElementById('profitPerTarget').getContext('2d'),
  'Target (di chi compra)',
  data,
  ['14-17', '18-24', '25-30', '31-40', 'Over 40'],
  `Totale ${(data.map(el => el.profit).reduce((a, b) => a + b, 0))} \u20AC`,
  1
);
html, body {
  background-color: #121212;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<canvas id="profitPerTarget" height="500" style="padding: 10px"></canvas>
javascript chart.js
1个回答
1
投票

你可以在createDonatsChart函数中使用closure。设置const为 const originalData = [...data] 然后你就可以在afterLabel回调中访问数据(如例)。

tooltips: {
    callbacks: {
      afterLabel: function(tooltipItem, data) {
        const sexArray = originalData[tooltipItem['index']].sex
        const precent = sexArray.reduce((a, b) => a + b, 0) // your calculation here          
        return '(' + precent + '%)';
    }
  }
}

请看playground中的例子。https:/jsfiddle.netdenisstukalovupw6asjm63#&commonjs=3CN0LJDjbl。

© www.soinside.com 2019 - 2024. All rights reserved.