如何修复Chart.js折线图多数据笔实时数据添加位置异常问题
问题
我使用Chart.js折线图展示多笔随时间变化的数据,代码逻辑为:先给每笔数据添加30秒的历史数据,之后通过每秒执行的OnTimer()函数追加当前时间的实时数据点。
当仅启用1笔数据(const _PENS = 1;)时,实时数据会正确追加到历史数据的图表右侧,符合预期;但启用2笔数据(const _PENS = 2;)时,实时数据并未追加到历史数据之后,反而被添加到图表左侧。
以下是出现问题的代码:
<html> <body> <canvas hidden="0" id="myLineChart"></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.1/dist/chartjs-adapter-moment.min.js"></script> <script> // --- define my data set var MyDataset = { datasets: [ { label: 'Red pen (seconds)', borderColor: 'red', backgroundColor: 'red', }, { label: 'Orange pen (seconds+1)', borderColor: 'orange', backgroundColor: 'orange', }, ], }; // --- define my chart configuration var MyChartConfig = { type: "line", data: MyDataset, options: { plugins: { title: { display: true, text: 'My Line Chart' } }, scales: { x: { type: 'time', time: { unit: 'second', displayFormats: { second: 'YYYY-MM-DD, HH:mm:ss', // Format for the unit } }, }, y: { position: 'left', type: 'linear', display: true, title: { display: true, // Set to true to display the title text: 'Left axis', // The text content of the y-axis title }, }, }, }, }; // --- set the number of pens to use // using only 1 pen produces the desired and correct effect - realtime data is appended to the historical data on the right hand side of the chart // using 2 pens gives a whacky result - realtime data is added to the left hand side of the chart !!! const _PENS = 2; // pens const _HISTORY = 30; // seconds // --- add an OnTimer function to execute every second // this function will add realtime data to the pens function OnTimer() { // --- add a new value to each pen at the current timestamp dt = new Date(); MyLineChart.data.labels.push(dt); for (i = 0; i < _PENS; i++) { MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i); } MyLineChart.update(); } // --- create the chart var MyLineChart = new Chart(document.getElementById('myLineChart'), MyChartConfig); // --- add _HISTORY seconds of historical data to all pens on chart for (i = 0; i < _PENS; i++) { // --- get the time _HISTORY secs ago dt = new Date(new Date().getTime() - (_HISTORY*1000)); for (j = 0; j < _HISTORY; j++) { // add one second dt = new Date(dt.getTime() + 1000); // add the new data point to the chart MyLineChart.data.labels.push(dt); MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i); } MyLineChart.update(); } // --- execute the OnTimer function every second setInterval(OnTimer, 1000); </script> </body> </html>
修复方案
问题原因
初始化历史数据时,你按数据集循环,给每个数据集单独往labels数组重复添加了30个时间戳。例如2笔数据时,labels数组会被塞入60个时间值(30*2),但每个数据集的data数组只有30个值。这种标签与数据长度不匹配的情况,会导致Chart.js在渲染时出现时间轴错位,后续新增的实时时间戳会被错误识别为更早的时间,从而显示在图表左侧。
修复步骤
- 统一生成历史时间标签:只生成一次30秒的连续时间戳数组,一次性赋值给
labels,避免重复添加。 - 同步填充数据集数据:基于同一组时间标签,分别给每个数据集填充对应的数据点,确保每个数据集的
data长度与labels完全一致。
修正后的完整代码
<html> <body> <canvas hidden="0" id="myLineChart"></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.1/dist/chartjs-adapter-moment.min.js"></script> <script> // --- define my data set var MyDataset = { datasets: [ { label: 'Red pen (seconds)', borderColor: 'red', backgroundColor: 'red', }, { label: 'Orange pen (seconds+1)', borderColor: 'orange', backgroundColor: 'orange', }, ], }; // --- define my chart configuration var MyChartConfig = { type: "line", data: MyDataset, options: { plugins: { title: { display: true, text: 'My Line Chart' } }, scales: { x: { type: 'time', time: { unit: 'second', displayFormats: { second: 'YYYY-MM-DD, HH:mm:ss', // Format for the unit } }, }, y: { position: 'left', type: 'linear', display: true, title: { display: true, // Set to true to display the title text: 'Left axis', // The text content of the y-axis title }, }, }, }, }; // --- set the number of pens to use const _PENS = 2; // pens const _HISTORY = 30; // seconds // --- add an OnTimer function to execute every second function OnTimer() { // --- add a new value to each pen at the current timestamp const dt = new Date(); MyLineChart.data.labels.push(dt); for (let i = 0; i < _PENS; i++) { MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i); } MyLineChart.update(); } // --- create the chart var MyLineChart = new Chart(document.getElementById('myLineChart'), MyChartConfig); // --- add _HISTORY seconds of historical data to all pens on chart // 第一步:统一生成历史时间标签 const historicalLabels = []; let dt = new Date(new Date().getTime() - (_HISTORY*1000)); for (let j = 0; j < _HISTORY; j++) { dt = new Date(dt.getTime() + 1000); historicalLabels.push(dt); } // 一次性赋值给labels MyLineChart.data.labels = historicalLabels; // 第二步:给每个数据集填充对应历史数据 for (let i = 0; i < _PENS; i++) { const datasetData = []; for (const time of historicalLabels) { datasetData.push(time.getSeconds() + i); } MyLineChart.data.datasets[i].data = datasetData; } MyLineChart.update(); // --- execute the OnTimer function every second setInterval(OnTimer, 1000); </script> </body> </html>
内容的提问来源于stack exchange,提问作者MoGuLza
相关产品推荐
相关产品推荐

