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
相关产品推荐
相关产品推荐

