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

Chart.js周单位时间横轴数据点置于周中的实现方法

实现方案

针对你的需求,分四个核心部分完成改造,全程适配Chart.js 3.x、date-fns适配器和Alpine.js:

1. 把数据点移到周中:日期转换处理

Chart.js的时间轴会根据数据的x值定位点,所以只要将每个数据点的x值设为周中日期(比如ISO周的周四,即周一开始后第3天),就能让点落在周刻度中间。用date-fns的工具函数快速转换:

// 把周标识(如'2023-W1')转成周中日期
function getWeekMidDate(weekStr) {
  const [year, weekNum] = weekStr.split('-W').map(Number);
  // 先获取该ISO周的周一(ISO周默认周一为起始)
  const weekStart = setISOWeek(new Date(year, 0, 1), weekNum);
  // 加3天得到周四(周中位置)
  return addDays(weekStart, 3);
}

// 处理你的原始数据,替换x值为周中日期
const processedData = yourRawData.map(item => ({
  x: getWeekMidDate(item.week),
  y: item.value
}));

2. 配置X轴显示周标识(如'2023-W1')

在Chart.js的x轴配置里,利用date-fns的格式化能力,把刻度替换成周标识:

options: {
  scales: {
    x: {
      type: 'time',
      time: {
        unit: 'week',
        // 指定周刻度的显示格式
        displayFormats: {
          week: "yyyy-'W'II" // date-fns中II代表ISO周数
        },
        minUnit: 'week' // 确保刻度按周划分
      },
      ticks: {
        autoSkip: false, // 可选:强制显示所有周刻度(数据量小时用)
        maxRotation: 0, // 防止标签旋转
        // 自定义刻度显示文本
        callback: function(value) {
          return format(new Date(value), "yyyy-'W'II");
        }
      }
    }
  }
}

3. 实现周Hover时背景高亮效果

推荐两种方案,结合Alpine.js的响应式特性:

方案1:Canvas直接绘制高亮(无需额外DOM)

通过Chart.js的事件监听和插件机制,在Canvas上绘制半透明矩形作为高亮背景:

<div x-data="{ chart: null, hoveredWeek: null }" x-init="initChart()">
  <canvas id="weeklyChart"></canvas>
</div>

<script>
function initChart() {
  const ctx = document.getElementById('weeklyChart').getContext('2d');
  
  this.chart = new Chart(ctx, {
    type: 'line', // 替换成你的图表类型
    data: {
      datasets: [{
        data: processedData, // 前面处理好的周中日期数据
        // 你的数据集样式配置
      }]
    },
    options: {
      interaction: {
        mode: 'index',
        intersect: false
      }
    },
    plugins: [{
      afterDraw: (chart) => {
        if (!this.hoveredWeek) return;
        const { xStart, xEnd } = this.hoveredWeek;
        const { top, bottom } = chart.chartArea;
        
        // 绘制高亮矩形
        ctx.save();
        ctx.fillStyle = 'rgba(0, 123, 255, 0.1)';
        ctx.fillRect(xStart, top, xEnd - xStart, bottom - top);
        ctx.restore();
      }
    }]
  });

  // 监听鼠标移动,获取hover的周范围
  ctx.canvas.addEventListener('mousemove', (e) => {
    const activePoints = this.chart.getElementsAtEventForMode(e, 'index', { intersect: false }, true);
    if (activePoints.length === 0) return;
    
    const point = activePoints[0];
    const xAxis = this.chart.scales.x;
    const weekStart = startOfISOWeek(new Date(point.parsed.x));
    const weekEnd = endOfISOWeek(new Date(point.parsed.x));
    
    // 转换为Canvas上的像素坐标
    this.hoveredWeek = {
      xStart: xAxis.getPixelForValue(weekStart),
      xEnd: xAxis.getPixelForValue(weekEnd)
    };
    this.chart.update();
  });

  // 鼠标离开时清除高亮
  ctx.canvas.addEventListener('mouseout', () => {
    this.hoveredWeek = null;
    this.chart.update();
  });
}
</script>

方案2:Alpine.js控制叠加DOM高亮

如果不想操作Canvas,可在图表上方放一个透明div,通过Alpine动态调整其位置和宽度:

<div class="relative" x-data="{ chart: null, hoverStyle: {} }" x-init="initChart()">
  <canvas id="weeklyChart"></canvas>
  <!-- 高亮用的透明div -->
  <div class="absolute top-0 bottom-0 bg-blue-100 opacity-30 pointer-events-none" :style="hoverStyle"></div>
</div>

<script>
function initChart() {
  const ctx = document.getElementById('weeklyChart').getContext('2d');
  
  this.chart = new Chart(ctx, {
    // 图表配置同前
  });

  ctx.canvas.addEventListener('mousemove', (e) => {
    const activePoints = this.chart.getElementsAtEventForMode(e, 'index', { intersect: false }, true);
    if (activePoints.length === 0) return;
    
    const point = activePoints[0];
    const xAxis = this.chart.scales.x;
    const weekStart = startOfISOWeek(new Date(point.parsed.x));
    const weekEnd = endOfISOWeek(new Date(point.parsed.x));
    
    const xStart = xAxis.getPixelForValue(weekStart);
    const xEnd = xAxis.getPixelForValue(weekEnd);
    
    // 计算相对于Canvas的位置和宽度
    this.hoverStyle = {
      left: `${xStart}px`,
      width: `${xEnd - xStart}px`
    };
  });

  ctx.canvas.addEventListener('mouseout', () => {
    this.hoverStyle = {};
  });
}
</script>

4. 依赖检查

确保已正确引入所有依赖:

<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js"></script>

内容的提问来源于stack exchange,提问作者Shashank Bhatt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 00:15:25