基于Next.js+TypeScript的可复用图片上传裁剪组件需求
集成Cropper的可复用Dropzone组件实现(Next.js + TypeScript)
1. 安装依赖
首先安装所需的核心库及类型定义:
npm install react-dropzone react-cropper cropperjs npm install -D @types/cropperjs
2. 封装可复用组件 ImageCropperDropzone.tsx
创建客户端组件(Next.js 13+ 需添加'use client'指令),完整实现如下:
'use client'; import { useState, useRef } from 'react'; import { useDropzone } from 'react-dropzone'; import Cropper from 'react-cropper'; import 'cropperjs/dist/cropper.css'; // 组件Props定义 interface ImageCropperDropzoneProps { onCropComplete: (croppedFile: File) => void; accept?: string; cropAspectRatio?: number; dropzoneText?: string; } export default function ImageCropperDropzone({ onCropComplete, accept = 'image/*', cropAspectRatio = 1 / 1, dropzoneText = '拖拽图片到此处或点击上传', }: ImageCropperDropzoneProps) { const [selectedFile, setSelectedFile] = useState<File | null>(null); const [cropModalOpen, setCropModalOpen] = useState(false); const cropperRef = useRef<Cropper>(null); // Dropzone核心逻辑 const { getRootProps, getInputProps, isDragActive } = useDropzone({ accept, multiple: false, onDrop: (acceptedFiles) => { if (acceptedFiles.length > 0) { setSelectedFile(acceptedFiles[0]); setCropModalOpen(true); } }, }); // 裁剪完成处理 const handleCropSave = () => { const cropper = cropperRef.current?.cropper; if (!cropper || !selectedFile) return; cropper.getCroppedCanvas().toBlob((blob) => { if (!blob) return; const croppedFile = new File([blob], selectedFile.name, { type: selectedFile.type, }); onCropComplete(croppedFile); // 重置状态 setCropModalOpen(false); setSelectedFile(null); }); }; // 关闭模态框时重置状态 const handleModalClose = () => { setCropModalOpen(false); setSelectedFile(null); }; return ( <> {/* Dropzone上传区域 */} <div {...getRootProps()} style={{ border: '2px dashed #ccc', borderRadius: '8px', padding: '40px', textAlign: 'center', cursor: 'pointer', backgroundColor: isDragActive ? '#f0f0f0' : 'white', }} > <input {...getInputProps()} /> <p>{dropzoneText}</p> </div> {/* 裁剪模态框 */} {cropModalOpen && ( <div style={{ position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', backgroundColor: 'rgba(0,0,0,0.8)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', zIndex: 1000, }}> <div style={{ width: '80%', maxWidth: '800px', position: 'relative' }}> {selectedFile && ( <Cropper ref={cropperRef} src={URL.createObjectURL(selectedFile)} aspectRatio={cropAspectRatio} viewMode={1} dragMode='move' autoCropArea={0.8} restore={false} guides={true} center={true} highlight={true} cropBoxMovable={true} cropBoxResizable={true} toggleDragModeOnDblclick={true} style={{ width: '100%', height: '450px' }} /> )} <div style={{ display: 'flex', gap: '16px', marginTop: '20px', justifyContent: 'flex-end', }}> <button onClick={handleModalClose} style={{ padding: '8px 16px', borderRadius: '4px', border: 'none', cursor: 'pointer' }} > 取消 </button> <button onClick={handleCropSave} style={{ padding: '8px 16px', borderRadius: '4px', border: 'none', backgroundColor: '#0070f3', color: 'white', cursor: 'pointer' }} > 确认裁剪 </button> </div> </div> </div> )} </> ); }
3. 组件使用示例
在任意Next.js页面或组件中导入并使用:
'use client'; import ImageCropperDropzone from '@/components/ImageCropperDropzone'; export default function UploadPage() { const handleCropComplete = (croppedFile: File) => { // 此处处理裁剪后的图片,比如上传到后端、预览等 console.log('裁剪完成的文件:', croppedFile); // 示例:创建预览URL const previewUrl = URL.createObjectURL(croppedFile); console.log('预览URL:', previewUrl); }; return ( <div style={{ maxWidth: '600px', margin: '40px auto' }}> <h2>图片上传与裁剪</h2> <ImageCropperDropzone onCropComplete={handleCropComplete} cropAspectRatio={16/9} dropzoneText='拖拽图片到此处,或点击选择图片' /> </div> ); }
4. 关键逻辑说明
- Dropzone交互:通过
react-dropzone监听文件拖拽/上传事件,选中文件后自动打开裁剪模态框 - Cropper配置:默认设置1:1比例,可通过
cropAspectRatioprops自定义;启用裁剪框拖拽、缩放等基础交互 - 裁剪结果处理:通过
getCroppedCanvas()获取裁剪后的画布,转为File对象后通过onCropComplete回调传递给父组件 - 状态清理:关闭模态框时自动重置选中文件状态,避免内存泄漏
内容的提问来源于stack exchange,提问作者Gaurav Maharwal
相关产品推荐
相关产品推荐

