1.安裝模塊
yarn add react-cropper lrz
2.分裝組件
src/components/ImgCropper/index.tsx
/**
* 上傳logo組件
*/
import * as React from 'react';
import { Upload, Icon, message, Modal, Button } from 'antd';
// 圖片剪裁
import Cropper from 'react-cropper';
// 圖片壓縮
// import lrz from 'lrz';
// base64 轉 Blob
import { b64toBlob } from '@utils/b64toBlob';
import { BaseUrl } from '@utils/constants';
import 'cropperjs/dist/cropper.css';
import axios from 'axios';
import './index.less';
const lrz = require('lrz');
interface IProps {
onSuccess?: Function | any;
onChange?: Function | any;
title: string;
aspectRatio?: number;
}
interface IState {
srcCropper: any;
visible: boolean;
confirmLoading: boolean;
}
class ImgCropper extends React.Component<IProps, IState>{
constructor(props: IProps){
super(props);
this.state = {
srcCropper: '',
visible: false,
confirmLoading: false
}
}
beforeUpload = (file: any) => {
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
// 添加文件限制
message.error({ content: '文件大小不能超過10M' });
return false;
}
const reader = new FileReader();
reader.readAsDataURL(file); // 開始讀取文件
// 因為讀取文件需要時間,所以要在回調函數中使用讀取的結果
reader.onload = (e: any) => {
this.setState({
visible: true,
srcCropper: e.target.result, // cropper的圖片路徑
});
};
return false;
};
saveImg = () => {
this.setState({
confirmLoading: true,
});
// 通過refs讀取到Cropper實例,並讀取到裁剪之后的圖片(base64)
const cropper: any = this.refs.cropper;
const url = cropper.getCroppedCanvas().toDataURL();
// 此處使用了lrz組件對裁剪之后的圖片進行壓縮,lrz的API十分簡單,quality是指對壓縮圖片的品質,一般0.6或者0.7即可
lrz(url, { quality: 0.6 }).then((results: any) => {
const { onSuccess, onChange } = this.props;
const fd = new FormData();
// 由於后台接口參數需要一個文件名,所有根據當前時間生成文件名
const imgName = `${new Date().getTime()}.png`;
// 將base64轉化成二進制流
fd.append('file', b64toBlob(results.base64), imgName);
// 發送請求
axios.post(`${BaseUrl}/tools/saveAvatar`, fd).then((res) => {
const { data={} } = res;
if(data.code === 200){
onSuccess(data.data.file);
onChange && onChange(data.data.file);
message.success(data.message || '上傳成功');
}
}).catch((err) => {
message.error('上傳失敗');
}).finally(() => this.onCloseModal())
});
};
// 取消
onCloseModal = () => {
this.setState({
visible: false,
confirmLoading: false
})
}
render() {
// 考慮靠組件復用,裁剪Modal的標題作為屬性從組件外部傳遞進來
const { title, aspectRatio=1 } = this.props;
/**
* srcCropper:cropper組件內部圖片的url
* visible:裁剪Modal的顯示屬性
* confirmLoading:圖片上傳過程中Modal的Loading效果
* */
const { srcCropper, visible, confirmLoading } = this.state;
return (
<div>
<Upload beforeUpload={this.beforeUpload} showUploadList={false}>
<Button>
<Icon type="upload" /> 選擇圖片
</Button>
</Upload>
<Modal
title={title}
visible={visible}
onOk={this.saveImg}
onCancel={this.onCloseModal}
okText="確認上傳"
cancelText="取消"
confirmLoading={confirmLoading}
>
{/* <div className="previewHeader">
{srcCropper ? (
<div className="previewOutter">
<div className="uploadCrop previewContainer" />
<div className="uploadCropcir previewContainer" />
</div>
) : (
''
)}
</div> */}
{srcCropper ? (
<Cropper
ref="cropper"
style={{ height: 400, width: '100%' }}
// 預覽圖的容器
preview=".previewContainer"
guides
// 固定圖片裁剪比例(正方形)
aspectRatio={aspectRatio}
// 要裁剪的圖片的路徑
src={srcCropper}
/>
) : (
''
)}
</Modal>
</div>
);
}
}
export default ImgCropper;
3.頁面調用
<Form.Item label="頭像">
{getFieldDecorator('avatar', {
rules: [{ required: true, message: '請上傳頭像' }],
initialValue: detail['avatar'] || undefined
})(<ImgCropper title="上傳頭像" onSuccess={this.handleSuccess} />)}
{(avatarImg || detail['avatar']) && <img className="avatar-img" src={avatarImg || detail['avatar']} alt="頭像" /> || null}
</Form.Item>
.
