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

React项目中react-image-cropper无法加载图片问题求助

ReactCrop组件无法加载图片的解决方案

问题现象

选择图片后能正常获取文件对象,但ReactCrop组件无法渲染图片进行裁剪操作。

解决方案

1. 确保依赖版本兼容

运行命令更新到最新稳定版的react-image-crop:

npm install react-image-crop@latest

2. 修复图片样式缺失

在App.css中添加ReactCrop图片的基础样式,确保图片正确渲染:

.ReactCrop__image {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
}

3. 修正裁剪画布尺寸计算错误

原代码中画布尺寸直接使用crop的百分比数值,导致裁剪后的图片尺寸异常,同时移除重复的状态更新逻辑:

修改getCroppedImg函数:

const getCroppedImg = (image, crop, fileName) => {
  const canvas = document.createElement("canvas");
  const scaleX = image.naturalWidth / image.width;
  const scaleY = image.naturalHeight / image.height;
  // 基于原图比例计算实际裁剪尺寸
  canvas.width = crop.width * scaleX;
  canvas.height = crop.height * scaleY;
  const ctx = canvas.getContext("2d");

  ctx.drawImage(
    image,
    crop.x * scaleX,
    crop.y * scaleY,
    crop.width * scaleX,
    crop.height * scaleY,
    0,
    0,
    canvas.width,
    canvas.height
  );

  return new Promise((resolve, reject) => {
    canvas.toBlob((blob) => {
      if (!blob) {
        console.error("Canvas is empty");
        reject(new Error("Canvas is empty"));
        return;
      }
      blob.name = fileName;
      const imageUrl = window.URL.createObjectURL(blob);
      resolve(imageUrl);
    }, "image/jpeg");
  });
};

4. 优化状态更新逻辑

将裁剪后的URL状态更新统一移到makeClientCrop中,避免重复触发状态更新:

const makeClientCrop = async (crop) => {
  if (imageRef.current && crop.width && crop.height) {
    try {
      // 清理旧的对象URL,避免内存泄漏
      if (croppedImageUrl) {
        window.URL.revokeObjectURL(croppedImageUrl);
      }
      const newCroppedUrl = await getCroppedImg(
        imageRef.current,
        crop,
        "newFile.jpeg"
      );
      setCroppedImageUrl(newCroppedUrl);
    } catch (err) {
      console.error("裁剪失败:", err);
    }
  }
};

5. 完整修改后的代码

import React, { useState, useRef } from "react";
import ReactCrop from "react-image-crop";
import "react-image-crop/dist/ReactCrop.css";

import "./App.css";

function App() {
  const [src, setSrc] = useState(null);
  const [crop, setCrop] = useState({
    unit: "%",
    x: 0,
    y: 0,
    width: 50,
    height: 50,
  });
  const [croppedImageUrl, setCroppedImageUrl] = useState(null);
  const imageRef = useRef(null);

  const onSelectFile = (e) => {
    if (e.target.files && e.target.files.length > 0) {
      const reader = new FileReader();
      reader.addEventListener("load", () => {
        setSrc(reader.result);
        // 重置裁剪框为默认值
        setCrop({
          unit: "%",
          x: 0,
          y: 0,
          width: 50,
          height: 50,
        });
        // 清理之前的裁剪结果
        if (croppedImageUrl) {
          window.URL.revokeObjectURL(croppedImageUrl);
          setCroppedImageUrl(null);
        }
      });
      reader.readAsDataURL(e.target.files[0]);
    }
  };

  const onImageLoaded = (image) => {
    imageRef.current = image;
  };

  const onCropComplete = (crop) => {
    makeClientCrop(crop);
  };

  const onCropChange = (crop) => {
    setCrop(crop);
  };

  const makeClientCrop = async (crop) => {
    if (imageRef.current && crop.width && crop.height) {
      try {
        if (croppedImageUrl) {
          window.URL.revokeObjectURL(croppedImageUrl);
        }
        const newCroppedUrl = await getCroppedImg(
          imageRef.current,
          crop,
          "newFile.jpeg"
        );
        setCroppedImageUrl(newCroppedUrl);
      } catch (err) {
        console.error("裁剪失败:", err);
      }
    }
  };

  const getCroppedImg = (image, crop, fileName) => {
    const canvas = document.createElement("canvas");
    const scaleX = image.naturalWidth / image.width;
    const scaleY = image.naturalHeight / image.height;
    canvas.width = crop.width * scaleX;
    canvas.height = crop.height * scaleY;
    const ctx = canvas.getContext("2d");

    ctx.drawImage(
      image,
      crop.x * scaleX,
      crop.y * scaleY,
      crop.width * scaleX,
      crop.height * scaleY,
      0,
      0,
      canvas.width,
      canvas.height
    );

    return new Promise((resolve, reject) => {
      canvas.toBlob((blob) => {
        if (!blob) {
          console.error("Canvas is empty");
          reject(new Error("Canvas is empty"));
          return;
        }
        blob.name = fileName;
        const imageUrl = window.URL.createObjectURL(blob);
        resolve(imageUrl);
      }, "image/jpeg");
    });
  };

  return (
    <div className="App">
      <div>
        <input type="file" accept="image/*" onChange={onSelectFile} />
      </div>
      {src && (
        <ReactCrop
          src={src}
          crop={crop}
          ruleOfThirds
          onImageLoaded={onImageLoaded}
          onComplete={onCropComplete}
          onChange={onCropChange}
        />
      )}
      {croppedImageUrl && (
        <img alt="裁剪结果" style={{ maxWidth: "100%", marginTop: "20px" }} src={croppedImageUrl} />
      )}
    </div>
  );
}

export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 12:35:26