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

react-image-crop图片裁剪问题:渲染异常与保存时drawImage报错求助

React-Image-Crop 裁剪报错解决方案

问题场景

使用react-image-crop实现图片裁剪时遇到两个问题:

  • 直接使用<ReactCrop src={src} onImageLoaded={setImage} crop={crop} onChange={setCrop} />时,图片无法正常渲染
  • 改用嵌套img的写法<ReactCrop crop={crop} onChange={setCrop}><img src={src} onLoad={setImage} alt="crop" /></ReactCrop>后,图片显示正常且可裁剪,但点击保存裁剪结果时抛出错误:

Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLCanvasElement or HTMLImageElement or HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement or VideoFrame)'

错误原因

img标签的onLoad事件默认传递的是事件对象,而非图片元素本身。原代码中直接将setImage绑定到onLoad,导致image状态存储的是事件对象,而非HTMLImageElement实例,后续调用drawImage时自然会因参数类型不匹配报错。

修复方案

修改img的onLoad回调,手动将图片元素传递给setImage:

// 原写法
<img src={src} onLoad={setImage} alt="crop" />

// 修改后
<img src={src} onLoad={(e) => setImage(e.target)} alt="crop" />

额外优化点:

  • 裁剪前增加参数校验,避免空值触发报错
  • 新增useEffect清理createObjectURL创建的对象URL,防止内存泄漏

修改后的完整代码

import { useState, useEffect } from 'react';
import ReactCrop from 'react-image-crop';
import 'react-image-crop/dist/ReactCrop.css';

function ImageCropper() {
    const [src, setSrc] = useState(null);
    const [crop, setCrop] = useState({ aspect: 16 / 9 });
    const [image, setImage] = useState(null);
    const [output, setOutput] = useState(null);

    // 清理对象URL,防止内存泄漏
    useEffect(() => {
        return () => {
            if (src) {
                URL.revokeObjectURL(src);
            }
        };
    }, [src]);

    const selectImage = (file) => {
        if (file) {
            setSrc(URL.createObjectURL(file));
        }
    };

    const cropImageNow = () => {
        // 校验必要参数,避免空值报错
        if (!image || !crop.width || !crop.height) return;

        const canvas = document.createElement('canvas');
        const scaleX = image.naturalWidth / image.width;
        const scaleY = image.naturalHeight / image.height;
        const pixelRatio = window.devicePixelRatio;

        canvas.width = crop.width * pixelRatio;
        canvas.height = crop.height * pixelRatio;
        const ctx = canvas.getContext('2d');

        ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
        ctx.imageSmoothingQuality = 'high';

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

        const base64Image = canvas.toDataURL('image/jpeg');
        setOutput(base64Image);
    };

    return (
        <div className="App">
            <center>
                <input
                    type="file"
                    accept="image/*"
                    onChange={(e) => {
                        selectImage(e.target.files[0]);
                    }}
                />
                <br />
                <br />
                <div>
                    {src && (
                        <div>
                            <ReactCrop crop={crop} onChange={setCrop}>
                                {/* 修改onLoad回调,传递图片元素 */}
                                <img src={src} onLoad={(e) => setImage(e.target)} alt="crop" />
                            </ReactCrop>
                            <br />
                            <button onClick={cropImageNow}>Crop</button>
                            <br />
                            <br />
                        </div>
                    )}
                </div>
                <div>{output && <img src={output} />}</div>
            </center>
        </div>
    );
}
export default ImageCropper;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 01:53:29