在 ChartJS 中显示错误数据的工具提示

Tooltips showing wrong data in ChartJS

我有一个显示来自 API 的数据的条形图,在数据规范化后我遇到了工具提示问题,如果我将鼠标悬停在像“9:00”这样的集合上,工具提示会显示“8:00” ' 向其中添加不属于该时间集的其他数据的数据。

我的代码如下所示:

const API = [{
  "totpag": 98.04,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 96.17,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 307.87,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 164.22,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 382.21,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 56.39,
  "descrpag": "NON RISCOSSO",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(24, 244, 114)"
}, {
  "totpag": 62.16,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 270.42,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 31.62,
  "descrpag": "NON RISCOSSO",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(24, 244, 114)"
}, {
  "totpag": 126.09,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 260.54,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 289.46,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 448.67,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 595.38,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 378.44,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 226.74,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 165.18,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 154.11,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 201.39,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 24.26,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 227.75,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 10.01,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 32.51,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 253.36,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 133.95,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 101.15,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 166.33,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 298.99,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 82.96,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 68.47,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T19:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 164.06,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T19:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 36.33,
  "descrpag": "BANCOMAT",
  "data": "2022-02-07T08:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 16.2,
  "descrpag": "CONTANTI",
  "data": "2022-02-07T08:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 15.5,
  "descrpag": "BANCOMAT",
  "data": "2022-02-07T09:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 25.43,
  "descrpag": "CONTANTI",
  "data": "2022-02-07T09:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}]



const optionsPagamentiBar = {
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: {
      display: false
    },
    tooltip: {
      mode: 'index',
      intersect: 0,
      usePointStyle: true,
      callbacks: {
        label: function(context) {
          return context.dataset.label + ": " + "€" + context.parsed.y.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,').replace(/[,.]/g, m => (m === ',' ? '.' : ','));
        }
      }
    }
  },
  scales: {
    y: {
      ticks: {
        display: true,
        beginAtZero: true,
        callback: function(value, index, values) {
          return "€" + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
        }
      },
      grid: {
        drawBorder: false,
        zeroLineColor: "transparent",
      }
    },
    x: {
      display: 1,
      ticks: {
        padding: 10,
        display: true,
        fontSize: 10
      },
      grid: {
        display: false
      }
    }
  }
}


const chartBarPayments = new Chart(document.getElementById("chartBarPayments").getContext('2d'), {
  type: 'bar',
  data: {
    labels: [],
    datasets: [{
      data: [],
    }]
  },
  options: optionsPagamentiBar
});


let periodo = 'giorno'

function pagamentiPerFascia(pagamenti) {
  let datasets = [];
  pagamenti.forEach((pagamento, i) => {
    if (pagamento.totpag !== 0) {
      let date = "";
      if (periodo == "anno") {
        date = moment(pagamento.data).format("MMM YYYY");
        labels.push();
      } else if (periodo == "mese") {
        date = moment(pagamento.data).format("DD MMM");
      } else {
        date = moment(pagamento.data).format('HH:mm');
      }

      let dataset = datasets.find(data => data.label === pagamento.descrpag)
      if (dataset) {
        dataset.data.push({
          x: date,
          y: pagamento.totpag
        })
      } else {
        datasets.push({
          label: pagamento.descrpag,
          data: [{
            x: date,
            y: pagamento.totpag
          }], backgroundColor: pagamento.backgroundColor
        })
      }

    }
  })

  chartBarPayments.data = {
    datasets
  };
  chartBarPayments.update();
}

pagamentiPerFascia(API)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
<canvas id="chartBarPayments"></canvas>

如果您尝试将鼠标悬停在“9:00”的第 4 个柱上,它会在“8:00”显示工具提示,其中包含来自“9:00”的一些数据,但甚至其他数据集也会出现错误。

这是什么问题,我该如何解决?

此问题是由数据集中的空白引起的。出于某种原因,这让 ChartJS 感到困惑。如果您为“NON RISCOSSO”中的缺失值输入零值并删除阻止将这些值添加到数据集的 if (pagamento.totpag !== 0) 它有效。

让我知道这是否是可接受的解决方法。否则可能会进一步深入了解 ChartJS 以弄清楚发生了什么。

下面是一些将零值添加到数据集中的代码,因此没有间隙。我不得不重写你的数据规范化代码。

const API = [{
  "totpag": 98.04,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 96.17,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 307.87,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 164.22,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 382.21,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 56.39,
  "descrpag": "NON RISCOSSO",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(24, 244, 114)"
}, {
  "totpag": 62.16,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 270.42,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 31.62,
  "descrpag": "NON RISCOSSO",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(24, 244, 114)"
}, {
  "totpag": 126.09,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 260.54,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 289.46,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 448.67,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 595.38,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 378.44,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 226.74,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 165.18,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 154.11,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 201.39,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 24.26,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 227.75,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 10.01,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 32.51,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 253.36,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 133.95,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 101.15,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 166.33,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 298.99,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 82.96,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 68.47,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T19:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 164.06,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T19:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 36.33,
  "descrpag": "BANCOMAT",
  "data": "2022-02-07T08:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 16.2,
  "descrpag": "CONTANTI",
  "data": "2022-02-07T08:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 15.5,
  "descrpag": "BANCOMAT",
  "data": "2022-02-07T09:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 25.43,
  "descrpag": "CONTANTI",
  "data": "2022-02-07T09:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}];

let periodo = "giorno";

const optionsPagamentiBar = {
  responsive: true,
  maintainAspectRatio: false,
  interaction: {
    intersect: false,
    mode: 'index'
  },
  plugins: {
    legend: {
      display: false
    },
    tooltip: {
      usePointStyle: true,
      callbacks: {
        label: function(context) {
          return context.dataset.label + ": " + "€" + context.parsed.y.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,').replace(/[,.]/g, m => (m === ',' ? '.' : ','));
        }
      }
    }
  },
  scales: {
    y: {
      ticks: {
        display: true,
        beginAtZero: true,
        callback: function(value, index, values) {
          return "€" + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
        }
      },
      grid: {
        drawBorder: false,
        zeroLineColor: "transparent",
      }
    },
    x: {
      display: 1,
      ticks: {
        padding: 10,
        display: true,
        fontSize: 10
      },
      grid: {
        display: false
      }
    }
  }
}

// Grafico pagamenti per fascia
const chartBarPayments = new Chart(document.getElementById("chartBarPayments").getContext('2d'), {
  type: 'bar',
  data: {
    labels: [],
    datasets: [{
      data: [],
    }]
  },
  options: optionsPagamentiBar
});

function getColorsByPayments(pagamenti) {
  const colorScale = d3.interpolateSinebow;
  const colorRangeInfo = {
    colorStart: 0.2,
    colorEnd: 1,
    useEndAsStart: true,
  };

  const tipiPagamenti = pagamenti.reduce((acc, pag) => {
    if (acc.includes(pag.descrpag)) return acc;
    acc.push(pag.descrpag);
    return acc
  }, [])
  let COLORS = interpolateColors(tipiPagamenti.length, colorScale, colorRangeInfo);

  return COLORS.map((color, index) => {
    return {
      pagamento: tipiPagamenti[index],
      backgroundColor: color
    }
  });
}


function pagamentiPerFascia(pagamenti) {
  //const COLORS = getColorsByPayments(pagamenti);
  let datasets = [];
  let timePeriods = [];
  let datasetLabels = [];
  let indexedData = {};
  let backgroundColors = {};

  pagamenti.forEach((pagamento, i) => {
    let date = "";
    if (periodo == "anno") {
      date = moment(pagamento.data).format("MMM YYYY");
      labels.push();
    } else if (periodo == "mese") {
      date = moment(pagamento.data).format("DD MMM");
    } else {
      date = moment(pagamento.data).format('HH:mm');
    }
    if (!timePeriods.includes(date)) {
       timePeriods.push(date);
    }
    if (!datasetLabels.includes(pagamento.descrpag)) {
       datasetLabels.push(pagamento.descrpag);
    }    
    backgroundColors[pagamento.descrpag] = pagamento.backgroundColor;
    indexedData[pagamento.descrpag + date] = pagamento.totpag;
  });
  datasetLabels.forEach(label => {
    const dataset = {
      label: label,
      backgroundColor: backgroundColors[label],
      data: []
    };
    datasets.push(dataset);
    timePeriods.forEach(date => {
      dataset.data.push({
        x: date,
        y: indexedData[label+date] || 0
      })       
    });
  })  
  chartBarPayments.data = {
    datasets
  };
  chartBarPayments.update();
}

pagamentiPerFascia(API);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
<canvas id="chartBarPayments"></canvas>