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>
.