【封装获取图片宽高 hook】

122 阅读1分钟

useImageSize

  1. 入参支持 string 或者 File 类型
  2. 并修复了: imageCover 变化的时候,第一张加载比较慢,第二张加载比快,获取对宽高大小是第一张的,应该是第二张才对
import { message } from "antd";
import { useEffect, useState, useRef } from "react";

export interface ImageSize {
  width: number;
  height: number;
}

/**
 * 自定义钩子函数,用于获取图片的尺寸。
 * @param imageCover - 图片的URL字符串或File对象。
 * @returns 包含imageSize(宽度和高度)和getImageSize函数的对象。
 */
export const useImageSize = (imageCover: string | File) => {
  const [imageSize, setImageSize] = useState<ImageSize | null>(null);
  const currentImageCover = useRef(imageCover); // 引入 useRef 来保存最新的 imageCover

  /**
   * 从URL获取图片的尺寸。
   * @param url - 图片的URL。
   * @returns 一个解析为图片尺寸(宽度和高度)的Promise。
   */
  const getImageSizeFromUrl = (url: string): Promise<ImageSize> => {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.src = url;
      img.onload = () => {
        resolve({ width: img.width, height: img.height });
      };
      img.onerror = (error) => {
        reject(error);
      };
    });
  };

  /**
   * 从File对象获取图片的尺寸。
   * @param file - 图片的File对象。
   * @returns 一个解析为图片尺寸(宽度和高度)的Promise。
   */
  const getImageSizeFromFile = (file: File): Promise<ImageSize> => {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => {
        const img = new Image();
        img.src = reader.result as string;
        img.onload = () => {
          resolve({ width: img.naturalWidth, height: img.naturalHeight });
        };
        img.onerror = () => {
          reject(new Error(`获取文件 ${file.name} 的图片尺寸失败`));
        };
      };
      reader.onerror = () => {
        reject(new Error(`读取文件 ${file.name} 作为数据URL失败`));
      };
    });
  };

  /**
   * 获取图片的尺寸,无论是URL字符串还是File对象。
   * @param input - 图片的URL字符串或File对象。
   * @returns 一个解析为图片尺寸(宽度和高度)的Promise。
   */
  const getImageSize = (input: string | File): Promise<ImageSize> => {
    if (typeof input === "string") {
      return getImageSizeFromUrl(input);
    } else if (input instanceof File) {
      return getImageSizeFromFile(input);
    } else {
      return Promise.reject(new Error("getImageSize 的输入类型无效"));
    }
  };

  useEffect(() => {
    currentImageCover.current = imageCover; // 每次更新时记录最新的 imageCover

    if (imageCover) {
      getImageSize(imageCover)
        .then((size: ImageSize) => {
          // 只有当 imageCover 没有被其他更新覆盖时才设置尺寸
          if (currentImageCover.current === imageCover) {
            setImageSize(size);
          }
        })
        .catch(() => message.error("加载图片失败"));
    }
  }, [imageCover]);

  return {
    imageSize,
    getImageSize,
  };
};