Canvas drawImage分层

Canvas drawImage layering

使用 canvas 呈现分层的图像集合,用户可以随后下载编译后的图像。但是,当使用 image.onload 时,它会根据从 CDN 加载速度最快的图像顺序分层。

我正在努力实现它们在代码行进展方面的分层。无论如何要实现这一目标?我尝试过异步函数,但还没有真正实现任何目标。以下是我目前的代码:

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

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

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

    const renderImages = async (context) => {
        var bg_image = new Image();
        var base_image = new Image();
        var eyes_image = new Image();
        var hats_image = new Image();
        var backpacks_image = new Image();
        var clothes_image = new Image();

        bg_image.crossOrigin="*";
        base_image.crossOrigin="*";
        eyes_image.crossOrigin="*";
        hats_image.crossOrigin="*";
        clothes_image.crossOrigin="*";
        backpacks_image.crossOrigin="*";

        base_image.src = base;
        eyes_image.src = eyes;
        hats_image.src = hat;
        clothes_image.src = backpack;
        backpacks_image.src = clothes;

        base_image.onload = function(){ context.drawImage(base_image, 0, 0, 650, 650) }
        eyes_image.onload = function(){ context.drawImage(eyes_image, 0, 0, 650, 650) }
        hats_image.onload = function(){ context.drawImage(hats_image, 0, 0, 650, 650) }
        clothes_image.onload = function(){ context.drawImage(clothes_image, 0, 0, 650, 650) }
        backpacks_image.onload = function(){ context.drawImage(backpacks_image, 0, 0, 650, 650) }
    }

    useEffect(() => {
        if (canvasRef.current) {
            const canvas = canvasRef.current;
            if (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;

您可以将图像创建包装在 Promise 中,图像加载完成后解析并等待每个图像加载完成,以实现您想要的同步流程。

const getImg = (url: string) => {
  const img = new Image();
  img.crossOrigin="*";
  img.src = url;

  return new Promise((res, rej) =>  {
        img.onload = () => res(img);
        img.onerror = (err) => rej(err);
  });
}

const renderImages = async (context) => {
  const baseImg = await getImg(base);
  context.drawImage(baseImg, 0, 0, 650, 650);

  const eyesImg = await getImg(eyes);
  context.drawImage(eyesImg, 0, 0, 650, 650);

  const hatImg = await getImg(hat);
  context.drawImage(hatImg, 0, 0, 650, 650);

  const backpackImg = await getImg(backpack);
  context.drawImage(backpackImg, 0, 0, 650, 650);

  const clothesImg = await getImg(clothes);
  context.drawImage(clothesImg, 0, 0, 650, 650);
}

我会使用一个数组 image_sources 而不是您正在使用的许多变量
(底座、眼睛、帽子、背包、衣服),这种方法的好处:

  • 这会显着减少渲染函数代码的大小。
  • 我们可以递归调用renderImages顺序绘制所有 图片。
  • 您的代码已准备好处理与 用户需求。

这是我的粗略原型:

const renderImages = (context, index) => {
  if (index < image_sources.length) {
    let image = new Image();
    image.crossOrigin = "*";
    image.src = image_sources[index];
    image.onload = function() {
      context.drawImage(image, 0, 0, 650, 650)
      renderImages(context, index + 1)
    }
  }
}

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