antd 官方提供的剪切组件
仅支持本地图片的剪切和上传
import { Upload } from 'antd';
import ImgCrop from 'antd-img-crop';
import type { RcFile, UploadFile, UploadProps } from 'antd/es/upload/interface';
import React, { useState } from 'react';
const App: React.FC = () => {
const [fileList, setFileList] = useState<UploadFile[]>([
{
uid: '-1',
name: 'image.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
]);
const onChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
setFileList(newFileList);
};
const onPreview = async (file: UploadFile) => {
let src = file.url as string;
if (!src) {
src = await new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(file.originFileObj as RcFile);
reader.onload = () => resolve(reader.result as string);
});
}
const image = new Image();
image.src = src;
const imgWindow = window.open(src);
imgWindow?.document.write(image.outerHTML);
};
return (
<ImgCrop rotate>
<Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
listType="picture-card"
fileList={fileList}
onChange={onChange}
onPreview={onPreview}
>
{fileList.length < 5 && '+ Upload'}
</Upload>
</ImgCrop>
);
};
export default App;
Demo
demo地址: antd 官方提供的剪切组件
react-easy-corp
antd的剪切组件就是基于此依赖封装
import React, { useState, useCallback } from 'react';
import Cropper from 'react-easy-crop';
const yourImage = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
const Demo = () => {
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
console.log(croppedArea, croppedAreaPixels);
}, []);
return (
<div style={{ position: 'relative', width: 300, height: 300 }}>
<Cropper
image={yourImage}
crop={crop}
zoom={zoom}
aspect={4 / 3}
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={setZoom}
/>
</div>
);
};
export default Demo;
Demo
demo地址: react-easy-crop
react-easy-corp-showImg
基于react-easy-corp,增加了生成剪切后图片blob功能
import { Button, message } from 'antd';
import React, { useState, useEffect, useCallback } from 'react';
import Cropper from 'react-easy-crop';
import type { Area } from 'react-easy-crop/types';
import styles from './ReactEasyCropShowImg.less';
const yourImage = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
const PREFIX = 'tds';
export interface ReactEasyCropShowImgProps extends React.HTMLAttributes<HTMLDivElement> {}
const ReactEasyCropShowImg = (props: ReactEasyCropShowImgProps) => {
const { className = '', ...otherProps } = props;
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [resultUrl, setResultUrl] = useState('');
const [croppedAreaPixels, setCroppedAreaPixels] = useState({ x: 0, y: 0, width: 0, height: 0 });
const onCropComplete = useCallback((croppedArea: Area, croppedAreaPixels: Area) => {
console.log(croppedArea, croppedAreaPixels);
setCroppedAreaPixels(croppedAreaPixels);
}, []);
useEffect(() => {
const imgSource = document.querySelector(`.${PREFIX}-media`) as HTMLImageElement;
imgSource.setAttribute('crossOrigin', 'Anonymous'); // 处理跨域
imgSource.onload = () => {
// 加载图像完成之后
console.log('图片已添加允许跨域的属性');
};
}, []);
const onOk = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
const { width: cropWidth, height: cropHeight, x: cropX, y: cropY } = croppedAreaPixels;
const imgSource = document.querySelector(`.${PREFIX}-media`) as HTMLImageElement;
canvas.width = cropWidth;
canvas.height = cropHeight;
// ctx.fillStyle = fillColor;
ctx.fillRect(0, 0, cropWidth, cropHeight);
ctx.drawImage(imgSource, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
try {
canvas.toBlob((blob) => {
if (blob) {
console.log('blob', blob);
const blobUrl = window.URL.createObjectURL(blob);
console.log('blobUrl', blobUrl);
setResultUrl(blobUrl);
}
});
} catch (error) {
message.error('无法导出受污染的画布');
console.log('无法导出受污染的画布', error);
}
};
return (
<div className={`${styles.root} ${className}`} {...otherProps}>
<h2>Crop 操作区</h2>
<div className={styles.cropBox}>
<Cropper
image={yourImage}
showGrid={false}
crop={crop}
zoom={zoom}
aspect={4 / 3}
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={setZoom}
classes={{
containerClassName: `${PREFIX}-container`,
mediaClassName: `${PREFIX}-media`,
}}
/>
</div>
<div style={{ marginTop: 12, marginBottom: 12 }}>
<Button onClick={onOk}>生成剪切结果</Button>
</div>
<h2>剪切结果</h2>
<img className={styles.resultImg} src={resultUrl}></img>
</div>
);
};
export default ReactEasyCropShowImg;
Demo
demo地址: react-easy-crop-showImg