Canvas画笔顺滑的关键是用贝塞尔曲线重构路径:二次贝塞尔以三点缓存实现自然弧线,三次贝塞尔通过双控点精准控制曲率,配合弧长映射解决匀速问题,并用局部重绘提升实时性能。

要让 Canvas 画笔真正“顺滑”,关键不是堆叠更多采样点,而是用贝塞尔曲线重构路径逻辑——把用户划过的离散点,转化为有方向感、有张力的连续曲线段,同时避开参数化不均带来的速度抖动。
用二次贝塞尔替代直线连接
原始 mousemove 每次只画 lineTo,线条生硬。换成 quadraticCurveTo,以当前点为控制点,前一点为起点,下一点为终点,就能自然“拱起”一段弧线:
- 至少缓存最近 3 个坐标:prev → curr → next
- 首次调用 moveTo(prev.x, prev.y),之后每新增一个点,就用 quadraticCurveTo(curr.x, curr.y, next.x, next.y)
- 控制点可微调:比如 curr 偏移 (next − prev) 的 15% 向量,避免拐角过钝或过锐
- 记得设 ctx.lineCap = 'round' 和 ctx.lineJoin = 'round',衔接处更柔和
三次贝塞尔实现更高阶平滑
当需要更精准的曲率控制(比如模拟手写回锋、图表拟合),用 bezierCurveTo 更合适。它需要两个控制点,能分别约束起始斜率和终止斜率:
- 对锚点序列 P₀, P₁, P₂…,为每段 Pᵢ→Pᵢ₊₁ 计算左控点 C₁ᵢ 和右控点 C₂ᵢ
- 常用启发式:C₁ᵢ = Pᵢ + (Pᵢ₊₁ − Pᵢ₋₁)/3,C₂ᵢ = Pᵢ₊₁ − (Pᵢ₊₂ − Pᵢ)/3(内部点适用)
- 首尾段用延长线法补足,确保起点/终点严格落在原始坐标上
- 绘制时保持 moveTo(P₀),然后循环调用 bezierCurveTo(C₁ᵢ, C₂ᵢ, Pᵢ₊₁)
解决“忽快忽慢”的匀速动画
直接按 t ∈ [0,1] 线性插值贝塞尔点,小球会卡在曲率大的地方——因为贝塞尔是几何非匀速参数化。必须做弧长映射:
立即学习“前端免费学习笔记(深入)”;
- 预计算整条路径:用 De Casteljau 算法采样 120+ 个点,累加相邻距离得到弧长数组
- 构建 s→t 查找表:s 是归一化的进度(0 到 1),查表得对应 t 值(二分查找即可)
- 动画中 t_time 线性递增,但位置由 getBezierPoint(lookup(t_time)) 决定
- 控制点和弧长表只需初始化一次,别放 requestAnimationFrame 里重复算
动态追加与局部重绘技巧
实时书写或滚动图表时,全量重绘既卡顿又闪烁。应只更新末端一小段,并复用上一帧终点:
- 维护双端队列存储坐标,超出长度时 shift() 头部旧点
- 每次新增点后,仅重绘最后 2–3 段曲线(从倒数第 3 个点开始)
- 用 clearRect() 擦除旧线末端矩形区域,而非全屏清空
- 绘制前 ctx.beginPath(),但 stroke() 只调一次,避免多次路径开销



















