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
相关产品推荐
相关产品推荐

