分片绘制是将大批量图形绘制任务按帧拆分,每帧只处理固定数量(如250个)图形,避免单帧超时掉帧;配合对象池复用图形实例,减少GC压力,从而稳定维持60fps。

分片策略不是“把一大块画布切成小块”,而是把“一次性干完的活”拆成多帧渐进完成,让浏览器有喘息空间。Canvas 本身不卡,卡的是你一帧里塞了太多 draw 调用、太多计算、太多状态切换——尤其当你要画几千个圆、上万个点或动态连线时。
为什么一帧画太多会掉帧
浏览器每秒最多执行 60 次渲染(16.6ms/帧)。如果你在 requestAnimationFrame 回调里一口气调用 5000 次 ctx.fillRect(),再加坐标计算、颜色判断、范围裁剪,很容易超时。一旦超时,这一帧就丢掉了,用户感知就是“卡顿”或“抖动”。这不是 Canvas 性能差,是你没给它留出调度余地。
分片绘制的核心思路:按帧切任务,非按图形切区域
关键不是“把地图分成 10×10 的格子”,而是“这 10000 个点,我这次只画前 200 个,下一帧再画 200 个……直到画完”。这样每帧耗时可控,主线程不被长期阻塞,动画也能保持连贯。
- 维护一个全局索引(如
let nextIndex = 0),记录当前画到第几个图形 - 每次
requestAnimationFrame中只处理固定数量(比如 100–300 个)图形的绘制逻辑 - 若未画完,继续下一帧;若全部完成,则停止分片流程,进入常规更新循环
- 避免在分片过程中反复清空整个 canvas(
clearRect),可只清局部区域或用离屏 canvas 预合成
结合对象池 + 分片,效果更稳
光分片还不够——如果每次都要新建图形对象(比如每个点都 new 一个 Circle 类),GC 压力大,也会卡。推荐配合对象池复用实例:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 提前创建好 2000 个图形对象(位置、样式、状态等属性可重置),存入池中
- 分片绘制时,从池里取对象,填入数据,调用
draw(ctx)方法,画完后标记为“已使用”,不销毁 - 后续帧或交互更新时,直接复用已有对象,跳过构造/垃圾回收开销
例如:画 8000 个带呼吸动画的小圆点,不用分片+对象池时帧率常跌破 40;加上后稳定在 58–60,内存波动平缓。
实际代码片段(简化示意)
以下是一个带进度控制的分片绘制函数:
const POINTS = generatePoints(8000); // 假设是坐标+动画参数数组
let nextIndex = 0;
const BATCH_SIZE = 250;
<p>function renderBatch() {
const ctx = canvas.getContext('2d');
const end = Math.min(nextIndex + BATCH_SIZE, POINTS.length);</p><p>// 只清局部区域(可选,若全图动态则需全清)
ctx.clearRect(0, 0, canvas.width, canvas.height);</p><p>for (let i = nextIndex; i < end; i++) {
const p = POINTS[i];
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius <em> (0.8 + 0.2 </em> Math.sin(Date.now() <em> 0.002 + p.offset)), 0, Math.PI </em> 2);
ctx.fillStyle = p.color;
ctx.fill();
}</p><p>nextIndex = end;</p><p>if (nextIndex < POINTS.length) {
requestAnimationFrame(renderBatch); // 继续下一帧
} else {
// 分片完成,进入常规动画循环
requestAnimationFrame(animateLoop);
}
}</p><p>// 启动
renderBatch();

















