You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chart.js 4.5.1 日期网格与数据点X轴对齐问题排查及解决请求

Fixing X-Axis Grid Line Alignment with Data Points in Chart.js Time Scale

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:

  1. Round ticks to match data dates: Add round: 'day' to the time config to ensure ticks are rounded to the nearest day, aligning perfectly with your daily data points.
  2. Center ticks under data points: Set align: 'center' in the ticks section so each tick (and its grid line) sits directly below its corresponding data point.
  3. Remove unnecessary axis offset: Set offset: false to 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 10:13:16