Chart.js 4.5.1 日期网格与数据点X轴对齐问题排查及解决请求
The misalignment you're seeing happens because auto-generated time-scale ticks (especially the major month grid lines) are positioned at the start of each month, while your data points fall on specific dates—near the end of a month, these points can look like they're in the next month's region, especially in small chart containers. Here's how to fix it:
Key Configuration Adjustments
Modify your X-axis settings to sync ticks and grid lines with your data points:
- Round ticks to match data dates: Add
round: 'day'to thetimeconfig to ensure ticks are rounded to the nearest day, aligning perfectly with your daily data points. - Center ticks under data points: Set
align: 'center'in thetickssection so each tick (and its grid line) sits directly below its corresponding data point. - Remove unnecessary axis offset: Set
offset: falseto eliminate padding that might shift the alignment of grid lines relative to data points.
Modified X-Axis Configuration
Here's the updated scales.x section:
x: { type: 'time', offset: false, // Add this to remove axis padding time: { unit: 'day', round: 'day', // Round ticks to nearest day displayFormats: { month: 'MMM yyyy', day: 'd/M', }, tooltipFormat: 'yyyy-MM-dd' }, grid: { color: line => { return (line.tick?.major ? 'rgba(0, 0, 0, 0.5)' : 'rgba(0, 0, 0, 0.1)') } }, ticks: { source: 'auto', align: 'center', // Center ticks under data points major: { enabled: true, }, font: (ctx) => { return { weight: ctx.tick?.major ? 'bold' : '' } } }, }
Full Modified Code
Here's the complete updated chart initialization code:
let combinedTransactions = [ { date: new Date('2024-01-01'), balanceAfterCents: 100000 }, { date: new Date('2024-01-10'), balanceAfterCents: 105000 }, { date: new Date('2024-01-28'), balanceAfterCents: 102000 }, { date: new Date('2024-01-31'), balanceAfterCents: 102000 }, { date: new Date('2024-02-01'), balanceAfterCents: 100000 }, { date: new Date('2024-02-04'), balanceAfterCents: 110000 }, { date: new Date('2024-02-10'), balanceAfterCents: 108000 }, { date: new Date('2024-02-20'), balanceAfterCents: 115000 }, { date: new Date('2024-03-01'), balanceAfterCents: 120000 }, { date: new Date('2024-03-15'), balanceAfterCents: 118000 }, { date: new Date('2024-03-30'), balanceAfterCents: 125000 }, ]; let dates = combinedTransactions.map(tx => tx.date); let canvas = document.getElementById('graph'); new Chart(canvas, { type: 'bar', data: { labels: dates, datasets: [ { _id: 'balance', label: 'Kontosaldo', data: combinedTransactions.map(tx => tx.balanceAfterCents / 100), type: 'line', borderColor: '#2196F3', backgroundColor: 'rgba(33, 149, 243, 0.7)', pointRadius: 0, order: 1, trendlineExponential: { colorMin: '#ff6b6b', colorMax: '#ff6b6b', width: 2, lineStyle: 'dashed', } }, ], }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { type: 'time', offset: false, time: { unit: 'day', round: 'day', displayFormats: { month: 'MMM yyyy', day: 'd/M', }, tooltipFormat: 'yyyy-MM-dd' }, grid: { color: line => { return (line.tick?.major ? 'rgba(0, 0, 0, 0.5)' : 'rgba(0, 0, 0, 0.1)') } }, ticks: { source: 'auto', align: 'center', major: { enabled: true, }, font: (ctx) => { return { weight: ctx.tick?.major ? 'bold' : '' } } }, }, y: { beginAtZero: false, grid: { color: line => (line.tick.value === 0 ? 'black' : 'rgba(0, 0, 0, 0.1)') } } }, interaction: { mode: 'index', intersect: false, }, plugins: { tooltip: { callbacks: { label: (ctx) => { let valFormat = { style: 'currency', currency: 'SEK' }; let labels = ''; if (ctx.dataset.stack === 'transactions') { valFormat.signDisplay = 'exceptZero'; let txs; if (ctx.dataset._id === 'deposits') { txs = combinedTransactions[ctx.dataIndex].deposits; } else { txs = combinedTransactions[ctx.dataIndex].withdrawals; } labels = txs.map(tx => new Intl.NumberFormat('sv-SE', valFormat).format(tx.amountCents / 100) + ' ' + tx.description); } else { labels = new Intl.NumberFormat('sv-SE', valFormat).format(ctx.parsed.y); } return labels; } } }, }, } });
The HTML remains unchanged:
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-trendline@3.2.0/dist/chartjs-plugin-trendline.min.js"></script> <div class="chart-container" style="position: relative; margin: auto; width: 400px; height: 400px;"> <canvas id="graph"></canvas> </div>
Alternative: Grid Lines Exclusively at Data Points
If you want grid lines to appear only at your data points (instead of auto-generated daily ticks), change ticks.source: 'auto' to ticks.source: 'data'. This will make every data point's date a tick with a corresponding grid line, ensuring perfect alignment. Note that this will hide the month major ticks unless you add them manually via a tick callback.
内容的提问来源于stack exchange,提问作者thomasa88

