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

如何在单个canvas上绘制多张图片,解决后图覆盖前图的问题

问题原因

你遇到的是异步回调变量作用域污染问题:

  • 循环中用var声明的tx/ty/tw/th属于函数级作用域变量,每次循环迭代都会覆盖旧值
  • img.onload是异步触发的回调,等图片加载完成执行绘制逻辑时,整个for循环早已执行完毕,此时所有回调读取到的坐标/尺寸都是循环最后一次迭代的值,所有图片都被绘制到同一个位置,就出现了后绘制的图覆盖之前的图的现象。

解决方案

方案1:使用块级作用域声明变量(最简单)

将循环内的变量声明从var改为let,let具备块级作用域,每次循环迭代都会生成独立的变量副本,回调会捕获当前迭代的变量值,不会被后续迭代覆盖:

var canvas = document.getElementById("paint");    
var ctx = canvas.getContext("2d");
function loadImages(data, targetX, targetY, targetWidth, targetHeight) {

    data = data.replace(/'/g, '"');
    targetX = targetX.replace(/'/g, '"');
    targetY = targetY.replace(/'/g, '"');
    targetWidth = targetWidth.replace(/'/g, '"');
    targetHeight = targetHeight.replace(/'/g, '"');

    data = JSON.parse(data);
    targetX = JSON.parse(targetX);
    targetY = JSON.parse(targetY);
    targetWidth = JSON.parse(targetWidth);
    targetHeight = JSON.parse(targetHeight);

    for(var i = 0; i < data.length; i++) {
        // 用let声明块级作用域变量
        let img = new Image();
        let tx = parseInt(targetX[i]);
        let ty = parseInt(targetY[i]);
        let tw = parseInt(targetWidth[i]);
        let th = parseInt(targetHeight[i]);
        img.src = data[i];
        img.onload = function() {
            ctx.drawImage(this, tx, ty, tw, th);
        };
    }
}

方案2:保证绘制顺序与数组顺序一致

如果对图片绘制顺序有严格要求(比如底层图必须先画,上层图后画),因为不同图片加载速度不同,可能出现后定义的图片先加载完成先绘制的问题,可以先预加载所有图片,再按顺序统一绘制:

var canvas = document.getElementById("paint");    
var ctx = canvas.getContext("2d");
async function loadImages(data, targetX, targetY, targetWidth, targetHeight) {
    data = data.replace(/'/g, '"');
    targetX = targetX.replace(/'/g, '"');
    targetY = targetY.replace(/'/g, '"');
    targetWidth = targetWidth.replace(/'/g, '"');
    targetHeight = targetHeight.replace(/'/g, '"');

    data = JSON.parse(data);
    targetX = JSON.parse(targetX);
    targetY = JSON.parse(targetY);
    targetWidth = JSON.parse(targetWidth);
    targetHeight = JSON.parse(targetHeight);

    // 预加载所有图片
    const loadTasks = data.map((src, index) => {
        return new Promise((resolve) => {
            const img = new Image();
            img.onload = () => resolve({
                img,
                tx: parseInt(targetX[index]),
                ty: parseInt(targetY[index]),
                tw: parseInt(targetWidth[index]),
                th: parseInt(targetHeight[index])
            });
            img.src = src;
        })
    })

    // 等待所有图片加载完成后按数组顺序绘制
    const imageList = await Promise.all(loadTasks);
    imageList.forEach(item => {
        ctx.drawImage(item.img, item.tx, item.ty, item.tw, item.th);
    })
}

内容的提问来源于stack exchange,提问作者Ravish Jha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 17:45:02