如何将一个Canvas的指定区域复制到另一个Canvas?
Canvas指定区域拷贝方案
完全可以实现,Canvas 2D上下文原生支持从源画布裁剪指定区块绘制到目标画布,不需要拷贝整张离屏画布内容。
核心API
直接使用2D上下文的drawImage方法的9参数重载即可,这个重载原生支持指定源画布的截取范围,是浏览器底层优化过的绘制路径,性能远高于整图拷贝、像素读写等方案。
9个参数的定义按顺序为:
- 源绘制对象:可以是Canvas、图片等可绘制资源,这里传入你存全量地图的离屏Canvas即可
- 源区域左上角X坐标:即你要拷贝的区块在离屏画布上的横向偏移,一般对应视口在全地图上的X偏移
- 源区域左上角Y坐标:即要拷贝的区块在离屏画布上的纵向偏移,一般对应视口在全地图上的Y偏移
- 源区域宽度:要拷贝的区块宽度,无缩放场景下直接取主Canvas视口宽度即可
- 源区域高度:要拷贝的区块高度,无缩放场景下直接取主Canvas视口高度即可
- 目标区域左上角X坐标:拷贝内容绘制到主Canvas上的起始横向位置,常规全屏视口场景下取0即可
- 目标区域左上角Y坐标:拷贝内容绘制到主Canvas上的起始纵向位置,常规全屏视口场景下取0即可
- 目标区域绘制宽度:内容在主Canvas上的绘制宽度,无缩放场景下和源区域宽度保持一致即可
- 目标区域绘制高度:内容在主Canvas上的绘制高度,无缩放场景下和源区域高度保持一致即可
每帧渲染代码示例
// 已预渲染好全量地图的离屏画布 const offscreenMap = 你的全地图离屏Canvas实例; // 主画布渲染上下文 const mainCanvasCtx = 主Canvas.getContext('2d'); // 当前视口状态,随玩家移动/缩放更新即可 const currentViewport = { mapOffsetX: 1500, // 当前视口左上角对应全地图的X坐标 mapOffsetY: 900, // 当前视口左上角对应全地图的Y坐标 width: 1920, // 主画布/视口宽度 height: 1080 // 主画布/视口高度 }; // 每帧渲染逻辑:仅拷贝视口覆盖的地图区块 function frameRender() { // 先清空主画布上一帧内容 mainCanvasCtx.clearRect(0, 0, currentViewport.width, currentViewport.height); // 局部拷贝离屏画布的视口对应区块 mainCanvasCtx.drawImage( offscreenMap, currentViewport.mapOffsetX, currentViewport.mapOffsetY, currentViewport.width, currentViewport.height, 0, 0, currentViewport.width, currentViewport.height ); // 后续再绘制其他每帧更新的元素:比如玩家、NPC、特效等 // renderDynamicObjects(); requestAnimationFrame(frameRender); } // 启动渲染循环 requestAnimationFrame(frameRender);
性能注意事项
- 不要用
getImageData+putImageData实现区域拷贝,这两个方法会直接读写像素内存,会阻塞主线程,性能比drawImage差一到两个数量级,仅适合像素特效类场景。 - 如果需要做地图缩放,直接修改目标区域的宽高参数即可,比如缩放至0.5倍就把目标宽高设为原视口宽高的0.5倍,
drawImage原生支持硬件加速缩放,性能损耗极低。 - 如果你的全地图尺寸超过浏览器单Canvas的最大尺寸限制(多数现代浏览器上限为16384像素),可以把全地图拆成多个固定大小(比如2048*2048)的瓦片离屏Canvas,每帧只挑选当前视口覆盖到的瓦片调用
drawImage即可,性能会比单张超大离屏画布更稳定。
内容的提问来源于stack exchange,提问作者Hansane
相关产品推荐
相关产品推荐

