不建议用Canvas从零绘制复杂甘特图,因其需手动处理时间轴对齐、缩放适配、拖拽定位、时区转换、导出打印等大量隐性问题,维护成本高且极易踩坑;成熟库如GanttMaster和gantt-elastic已封装父子嵌套、依赖渲染、跨浏览器滚动等关键能力。

不建议用 Canvas 从零绘制复杂甘特图。时间轴对齐、缩放适配、拖拽定位、时区处理、导出打印等隐性问题极多,实际项目中极易踩坑且维护成本高。
时间轴绘制的核心难点
Canvas 本身不理解“日期”,必须手动完成时间到像素的映射:
- 起止时间需统一转为毫秒时间戳,但
new Date('2026-09-15')默认解析为 UTC 零点,东八区会偏移 8 小时,务必用new Date(2026, 8, 15)构造或显式补时区 - 总跨度 = endMs − startMs;单天像素 = 甘特容器宽度 ÷ 总天数(用
Math.ceil((endMs − startMs) / 86400000)算整数天) - 刻度线位置 = (当前日期时间戳 − timelineStartMs) / 单天毫秒 × 单天像素,不能硬编码
30px/天,否则缩放即崩 - 文字标签需动态计算宽度并防重叠,放大后可能挤成一团,需按缩放比例调整字体大小和间距
任务进度条的定位与交互
每个任务条本质是矩形,但位置和宽高必须严格对应时间范围:
- left = (task.startMs − timelineStartMs) / oneDayMs × pxPerDay
- width = (task.endMs − task.startMs) / oneDayMs × pxPerDay
- 拖拽需监听
dragstart存 ID、dragover调e.preventDefault()、drop反查 x 坐标 → 新时间戳 → 重新计算 left/width - 鼠标 hover 提示需做 canvas 坐标反查:用
getBoundingClientRect()获取画布位置,再减去 clientX/Y,套入相同时间映射公式还原日期
为什么真要落地就别手写
成熟库已封装你必然遇到的问题:
立即学习“前端免费学习笔记(深入)”;
-
GanttMaster:自动处理父子嵌套缩进、依赖关系渲染(如
"7:3,8"表示依赖任务 7 的第 3 个里程碑 + 任务 8 结束)、跨浏览器滚动兼容 -
gantt-elastic:Vue 场景下支持响应式更新(注意
:key和初始tasks不能为空数组)、内置 MutationObserver 监听 DOM 变化 - Canvas 方案里导出图片要调
toDataURL(),打印需适配 media query,键盘导航(方向键移动焦点)全得自己补——这些都不是“画几条线”的事
如果只是轻量展示,可用 CSS Grid 替代
比 Canvas 更可控、更易调试、天然支持响应式:
- 用
grid-template-columns: repeat(var(--days), 1fr)均分时间列,避免像素硬编码 - 任务块用
grid-column: span X表达持续天数,起始列通过 JS 计算偏移天数动态设置 - 悬停提示用绝对定位浮层,内容直接绑定日期对象,无需坐标反查
- IE11 可降级为 Flex 单行布局,兼容性远好于 Canvas



















