先记住这个答案
指针设备可能比浏览器派发 pointermove 的频率采样得更密。浏览器把若干更新合成一次公开事件以减少事件处理开销,getCoalescedEvents() 让绘图应用在这次回调里拿到更多中间坐标,从而画出更细腻的轨迹。它改善的是轨迹精度,不保证屏幕刷新率或应用每秒绘制帧数变高。
- 浏览器可合并高频指针更新
- 方法返回被合并的指针事件样本
- 绘图可减少快速移动时的折线缺口
- 需检查浏览器支持与安全上下文
手写笔快速划线为何会断角
如果每次 pointermove 只画公开事件的一个坐标,高速移动时相邻点距离很大,曲线容易变成明显折线。读取合并样本后,可以按顺序把中间点接上。
const pad = document.querySelector('#pad')
const ctx = pad.getContext('2d')
let drawing = false, lastX = 0, lastY = 0
pad.addEventListener('pointerdown', event => {
drawing = true; pad.setPointerCapture(event.pointerId)
const box = pad.getBoundingClientRect()
lastX = event.clientX - box.left; lastY = event.clientY - box.top
})
pad.addEventListener('pointermove', event => {
if (!drawing) return
const box = pad.getBoundingClientRect()
ctx.beginPath()
ctx.moveTo(lastX, lastY)
for (const point of event.getCoalescedEvents?.() ?? [event]) {
lastX = point.clientX - box.left; lastY = point.clientY - box.top
ctx.lineTo(lastX, lastY)
}
ctx.stroke() // 每批样本只描画新线段
})
for (const type of ['pointerup', 'pointercancel']) pad.addEventListener(type, () => { drawing = false })不支持该方法时使用当前事件作为回退,至少能继续绘图。示例用 getBoundingClientRect() 把视口坐标转成画布坐标,并假定画布的绘图尺寸与 CSS 显示尺寸相同;若再用 CSS 缩放画布,还需按两者比例换算。setPointerCapture() 让按下后的指针暂时离开画布时,后续移动仍能交给这张画布处理。
每次移动先从上一批的末点开启一条新路径,只描画本批新增线段;若不调用 beginPath(),半透明笔刷会在后续移动中反复描粗旧线段。拿到更多采样点也意味着更多绘制工作,复杂画板可累计点后按帧批量绘制。
帧率与采样精度不是同一指标
屏幕刷新、浏览器绘制帧和指针硬件采样各有频率。getCoalescedEvents() 提供的是更完整的输入轨迹,不会改变显示器刷新速度。它的收益主要体现在笔迹平滑和快速手势分析。
该 API 的支持范围有差异,面向普通网页时要留降级路径,并在 HTTPS 等目标运行环境实测。并非所有指针动作都需要读取细粒度样本;按钮点击完全没有这个需求。
画布还要区分 CSS 尺寸与绘图缓冲区尺寸。高 DPR 设备上若只把样式设成 500 像素宽、却让内部位图保持默认尺寸,笔迹即使采样很密也会模糊或错位。实际画板可按设备像素比设置缓冲区,再统一坐标换算;getCoalescedEvents() 只补输入点,不能替代这一步。
若存在多指触摸,单个 drawing 布尔值也不够,需要用 pointerId 追踪正在绘制的那支笔。示例只讨论单指笔迹,生产组件应在取消、丢失指针捕获等路径收尾,避免下一次移动接到上一条线段。
容易答错的地方
- 说它能直接提升浏览器帧率
- 它提供合并前的输入样本;帧率还取决于绘制成本和显示管线。
- 不检查支持就直接调用
- 部分环境没有这个方法或受安全上下文限制,需要回退。
面试官还会怎么问?
与普通 pointermove 的最后坐标有什么区别?
普通事件给一个合并后的公开位置,细分样本补充这次合并期间经过的中间位置。
可以用于鼠标吗?
它属于 Pointer Events,鼠标也可产生指针事件;高采样手写笔场景通常更能体现收益。
怎样避免处理太多点卡顿?
可缓存点、按帧批量绘制,并只在绘图状态下读取样本。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。