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

ReactCrop组件裁剪预览无法在视口正常渲染

问题原因

ReactCrop预览无法正常渲染是四个典型问题导致的:

  • 初始crop配置缺少必填的unit字段,裁剪区域宽高计算值异常,导致预览层尺寸为0或直接溢出视口
  • 高版本react-image-crop(v10+)废弃了直接通过onImageLoaded回传图片DOM的用法,没有正确绑定图片ref会导致裁剪层无法挂载
  • 未给图片和裁剪组件设置最大宽高约束,大分辨率原图加载后会直接超出视口范围,视觉上表现为预览不显示
  • 裁剪逻辑中canvas宽高被重复赋值,且缺少空值判断,会触发JS错误中断渲染
修复方案

直接替换为以下可运行代码,核心调整点:

  1. 补全crop初始配置,设置默认裁剪区域占比,避免初始尺寸计算异常
  2. 用useRef绑定图片DOM,通过onComplete回调获取最终裁剪参数,适配高版本API
  3. 给图片和裁剪容器添加最大宽高限制,保证内容始终在视口内渲染
  4. 修复canvas尺寸计算逻辑,增加裁剪前的空值判断,避免运行时报错
  5. 保留官方样式引入,保证裁剪遮罩、选框样式正常加载
import { useState, useRef } from 'react';
import ReactCrop from 'react-image-crop';
import 'react-image-crop/dist/ReactCrop.css';

function App() {
  const [src, setSrc] = useState(null);
  const [crop, setCrop] = useState({ 
    unit: '%',
    width: 80,
    aspect: 16 / 9 
  });
  const [completedCrop, setCompletedCrop] = useState(null);
  const imgRef = useRef(null);
  const [output, setOutput] = useState(null);

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

  const cropImageNow = () => {
    if (!imgRef.current || !completedCrop?.width || !completedCrop?.height) return;
    
    const image = imgRef.current;
    const canvas = document.createElement('canvas');
    const scaleX = image.naturalWidth / image.width;
    const scaleY = image.naturalHeight / image.height;
    const ctx = canvas.getContext('2d');
    const pixelRatio = window.devicePixelRatio || 1;

    canvas.width = Math.floor(completedCrop.width * scaleX * pixelRatio);
    canvas.height = Math.floor(completedCrop.height * scaleY * pixelRatio);
    ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
    ctx.imageSmoothingQuality = 'high';

    ctx.drawImage(
      image,
      completedCrop.x * scaleX,
      completedCrop.y * scaleY,
      completedCrop.width * scaleX,
      completedCrop.height * scaleY,
      0,
      0,
      completedCrop.width * scaleX,
      completedCrop.height * scaleY,
    );
    
    setOutput(canvas.toDataURL('image/jpeg', 0.9));
  };

  return (
    <div style={{ padding: '20px', maxWidth: '1000px', margin: '0 auto' }}>
      <input
        type="file"
        accept="image/*"
        onChange={(e) => e.target.files?.[0] && selectImage(e.target.files[0])}
      />
      <br /><br />
      {src && (
        <div>
          <ReactCrop 
            crop={crop} 
            onChange={setCrop}
            onComplete={setCompletedCrop}
            style={{ maxWidth: '100%' }}
          >
            <img 
              ref={imgRef}
              src={src}
              alt="裁剪预览"
              style={{ maxWidth: '100%', maxHeight: '70vh' }}
            />
          </ReactCrop>
          <br />
          <button onClick={cropImageNow} style={{ padding: '8px 16px' }}>确认裁剪</button>
          <br /><br />
        </div>
      )}
      {output && (
        <div>
          <h4>裁剪结果</h4>
          <img src={output} alt="裁剪结果" style={{ maxWidth: '100%', border: '1px solid #eee' }} />
        </div>
      )}
    </div>
  );
}

export default App;

补充说明:如果使用v9及以下旧版本react-image-crop,可以保留原有onImageLoaded写法,但建议升级到最新稳定版,旧版本存在移动端触摸裁剪偏移、高DPI屏幕裁剪结果模糊的已知问题。

内容的提问来源于stack exchange,提问作者CSS-Romeo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 07:51:19