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

Canvas drawImage分层受加载速度影响 如何按代码顺序控制图层渲染

Canvas分层图片按指定顺序绘制解决方案

核心实现思路

你之前的实现问题在于每张图片加载完成后立刻绘制,绘制顺序完全由资源加载速度决定。正确的实现逻辑如下:

  • 先按从底层到顶层的期望绘制顺序定义资源序列
  • 将单张图片的加载逻辑封装为Promise,使用Promise.all等待全部资源加载完成(Promise.all返回的结果顺序与传入顺序完全一致,和资源加载速度无关)
  • 所有资源就绪后,按预设顺序依次绘制到画布上

封装图片加载工具函数

// 封装图片加载为Promise,统一处理跨域和加载回调
const loadImage = (src: string): Promise<HTMLImageElement> => {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.crossOrigin = '*'
    img.onload = () => resolve(img)
    img.onerror = (err) => reject(err)
    img.src = src
  })
}

修改后的完整组件代码

import React, { useRef, useEffect } from 'react';

interface CanvasProps {
    width: number;
    height: number;
    base: string;
    eyes: string;
    hat: string;
    backpack: string;
    clothes: string;
    selected: number;
}

// 封装图片加载为Promise,统一处理跨域和加载回调
const loadImage = (src: string): Promise<HTMLImageElement> => {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.crossOrigin = '*'
    img.onload = () => resolve(img)
    img.onerror = (err) => reject(err)
    img.src = src
  })
}

const Canvas = ({ width, height, base, eyes, hat, backpack, clothes, selected }: CanvasProps) => {
    const canvasRef = useRef<HTMLCanvasElement>(null);

    const renderImages = async (context: CanvasRenderingContext2D) => {
        // 按从底到顶的绘制顺序定义资源,调整数组顺序即可修改图层层级
        const layerSources = [base, eyes, hat, clothes, backpack]
        try {
            // 等待所有图片加载完成,返回的图片顺序和layerSources顺序完全一致
            const layerImages = await Promise.all(layerSources.map(src => loadImage(src)))
            // 按顺序绘制所有图层
            layerImages.forEach(img => {
                context.drawImage(img, 0, 0, 650, 650)
            })
        } catch (err) {
            console.error('图层资源加载失败:', err)
            // 可在此处添加加载失败的降级处理逻辑
        }
    }

    useEffect(() => {
        if (canvasRef.current) {
            const canvas = canvasRef.current;
            const context = canvas.getContext('2d');
            if (context) {
                context.clearRect(0, 0, 650, 650);
                renderImages(context);
            }
        }       
    },[selected]);

    return <canvas id="canvas" ref={canvasRef} height={height} width={width} />;
};

Canvas.defaultProps = {
    width: window.innerWidth,
    height: window.innerHeight
};

export default Canvas;

注意事项

  • 原代码存在资源赋值错误:clothes_image.src = backpack; backpacks_image.src = clothes;,修改后的代码已按语义修正了层级顺序,若不符合你的业务需求可直接调整layerSources数组的元素顺序
  • 若需要实现加载进度提示,可单独统计每个Promise的加载完成状态,无需等待全部加载完成再展示
  • 若需要支持某层资源为空的场景,可在loadImage函数中添加空值判断,返回透明占位图即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 09:45:09