You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用图片naturalWidth和naturalHeight在HTML5 Canvas绘制多图且不丢分辨率、防覆盖

问题根源
  1. 画布被重置:每次修改canvas的width或height属性时,浏览器会自动清空整个画布并重置绘图上下文。所以当img2加载完成后设置尺寸,会直接把之前img1画的内容清掉。
  2. 异步加载顺序不可控:图片加载是异步操作,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 09:24:58