如何利用图片naturalWidth和naturalHeight在HTML5 Canvas绘制多图且不丢分辨率、防覆盖
问题根源
- 画布被重置:每次修改canvas的
width或height属性时,浏览器会自动清空整个画布并重置绘图上下文。所以当img2加载完成后设置尺寸,会直接把之前img1画的内容清掉。 - 异步加载顺序不可控:图片加载是异步操作,
img1和img2谁先加载完谁先画,后加载的会在同一位置绘制,直接覆盖之前的内容(哪怕不重置尺寸也会覆盖)。
解决办法
因为两张图尺寸相同,我们只需要等两张图都加载完成后,一次性设置画布尺寸并绘制两张图即可。
方案一:用Promise统一等待加载
先把图片加载包装成Promise,等所有图片加载完成再操作:
<canvas id="canvas" style="width: 400px"/> <!-- 隐藏图片,避免页面显示 --> <img id="img1" src="myImage1" style="display: none;" /> <img id="img2" src="myImage2" style="display: none;" />
const canvas = document.getElementById("canvas"); const ctx = canvas.getContext('2d'); const img1 = document.getElementById("img1"); const img2 = document.getElementById("img2"); // 把图片加载转成Promise function waitForImage(img) { return new Promise((resolve, reject) => { if (img.complete) { resolve(img); } else { img.onload = () => resolve(img); img.onerror = err => reject(err); } }); } // 等两张图都加载好 Promise.all([waitForImage(img1), waitForImage(img2)]) .then(([imgA, imgB]) => { // 设置画布尺寸(用任意一张的自然尺寸就行) canvas.width = imgA.naturalWidth; canvas.height = imgA.naturalHeight; // 先画第一张 ctx.drawImage(imgA, 0, 0); // 如果是互补效果,比如颜色互补,可以加混合模式 // ctx.globalCompositeOperation = 'difference'; // 按需选择 // 再画第二张 ctx.drawImage(imgB, 0, 0); }) .catch(err => console.log('加载失败:', err));
方案二:用计数器统计加载完成数
不想用Promise的话,用个计数器简单粗暴:
const canvas = document.getElementById("canvas"); const ctx = canvas.getContext('2d'); const img1 = document.getElementById("img1"); const img2 = document.getElementById("img2"); let loadedNum = 0; function onImageLoaded() { loadedNum++; // 等两张都加载完 if (loadedNum === 2) { canvas.width = img1.naturalWidth; canvas.height = img1.naturalHeight; ctx.drawImage(img1, 0, 0); // 互补效果可以加混合模式,比如screen // ctx.globalCompositeOperation = 'screen'; ctx.drawImage(img2, 0, 0); } } // 绑定加载事件 img1.onload = onImageLoaded; img2.onload = onImageLoaded; // 处理图片已经缓存的情况(避免缓存时不触发onload) if (img1.complete) onImageLoaded(); if (img2.complete) onImageLoaded();
重要提示
- 别在单个图片的
onload里改画布尺寸,除非你确定这是最后一次修改。 - 必须等所有要画的图都加载完再开始绘制,不然容易出现只画了一张的情况。
- 如果是要实现互补视觉效果(比如颜色互补),可以试试Canvas的
globalCompositeOperation属性,不同的混合模式会带来不同的叠加效果,比如difference、exclusion、screen这些都适合做互补效果,你可以挨个试试哪个符合需求。
内容的提问来源于stack exchange,提问作者Janio Junior
相关产品推荐
相关产品推荐

