实现FullCalendar在dayGridMonth模式下按时间比例渲染事件
FullCalendar dayGridMonth模式按时间比例渲染事件
需求与问题
- 目标:在只读的dayGridMonth视图中,事件按起止时间的比例渲染,避免同一天内非重叠事件(如11:00结束和15:00开始的事件)重叠;支持跨多日期的事件,不显示时间标签,例如15:00开始的事件从单元格宽度的62.5%位置开始。
- 当前问题:默认渲染会让事件占满整个日期单元格,导致非重叠事件出现重叠;尝试
slotEventOverlap: false、slotDuration: '01:00:00'及eventDidMount/eventPositioned原生回调均未达到预期效果。
解决方案
核心思路是通过eventDidMount回调手动计算每个事件在对应日期单元格内的位置和宽度,基于事件的起止时间与全天时长的比例来调整样式。
完整代码示例
<!DOCTYPE html> <html lang='en'> <head> <meta charset='utf-8' /> <script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.5/index.global.min.js'></script> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/6.1.5/main.min.css' /> <style> #calendar { width: 60vw; height: 70vh; margin: auto; } /* 移除事件默认内边距,避免样式冲突 */ .fc-daygrid-event { padding: 2px !important; border-radius: 2px; } </style> <script> document.addEventListener('DOMContentLoaded', function() { const calendarEl = document.getElementById('calendar'); const DAY_MINUTES = 1440; // 一天总分钟数 const calendar = new FullCalendar.Calendar(calendarEl, { initialView: 'dayGridMonth', headerToolbar: { left: 'prev,next today', center: 'title', right: '' }, eventStartEditable: false, eventDurationEditable: false, defaultAllDay: false, displayEventTime: false, displayEventEnd: false, events: [ { start: '2023-04-16T15:00:00', end: '2023-04-19T11:00:00' }, { start: '2023-04-19T15:00:00', end: '2023-04-22T11:00:00' }, { start: '2023-04-22T15:00:00', end: '2023-04-25T11:00:00' }, { start: '2023-04-25T15:00:00', end: '2023-05-10T11:00:00' } ], eventDidMount: function(info) { const eventEl = info.el; const event = info.event; const startDate = event.start; const endDate = event.end; // 获取事件覆盖的所有日期单元格 const dayEls = eventEl.querySelectorAll('.fc-daygrid-event-harness'); dayEls.forEach((dayEl, index) => { // 计算当前单元格对应的日期 const cellDate = new Date(dayEl.closest('.fc-daygrid-day').dataset.date); cellDate.setHours(0, 0, 0, 0); let startMinutes, endMinutes; if (index === 0) { // 事件起始日期:计算当天内的开始和结束分钟 startMinutes = startDate.getHours() * 60 + startDate.getMinutes(); endMinutes = Math.min(DAY_MINUTES, (endDate > cellDate ? DAY_MINUTES : endDate.getHours() * 60 + endDate.getMinutes())); } else if (index === dayEls.length - 1) { // 事件结束日期:计算当天内的开始和结束分钟 startMinutes = 0; endMinutes = endDate.getHours() * 60 + endDate.getMinutes(); } else { // 中间日期:事件占满全天 startMinutes = 0; endMinutes = DAY_MINUTES; } // 计算位置和宽度比例 const startRatio = (startMinutes / DAY_MINUTES) * 100; const widthRatio = ((endMinutes - startMinutes) / DAY_MINUTES) * 100; // 应用样式 const eventContentEl = dayEl.querySelector('.fc-daygrid-event'); eventContentEl.style.position = 'absolute'; eventContentEl.style.left = `${startRatio}%`; eventContentEl.style.width = `${widthRatio}%`; eventContentEl.style.top = '2px'; eventContentEl.style.bottom = '2px'; }); } }); calendar.render(); }); </script> </head> <body> <div id='calendar'></div> </body> </html>
关键逻辑说明
- 时间比例计算:以一天1440分钟为基准,将事件的起止时间转换为分钟数,计算出在当天的占比。
- 分日期处理:
- 起始日期:事件从当天的
startRatio位置开始,宽度为当天持续时间的比例。 - 结束日期:事件从当天0点开始,宽度为当天持续时间的比例。
- 中间日期:事件占满整个单元格。
- 起始日期:事件从当天的
- 样式调整:通过绝对定位设置事件的
left和width,确保按比例渲染。
注意事项
- 确保事件的
allDay属性为false,FullCalendar才会识别为定时事件。 - 替换了旧版本的FullCalendar样式文件,避免版本不兼容问题。
- 自定义样式时移除默认事件的内边距,保证定位准确。
内容的提问来源于stack exchange,提问作者Andrew at the beach
相关产品推荐
相关产品推荐

