最近项目要使用图片裁剪上传,因为项目采用的是react+antd,所以第一时间想到的是ImgCrop插件,但是这不满足项目需求,项目要求的是能够缩放裁剪框,最后确定了采用react-cropper来实现图片的自定义裁剪
安装react-cropper
npm install --save react-cropper
新建一个CropperModal.jsx
import React, { useState, useEffect, useCallback, useRef } from 'react'
import PropTypes from 'prop-types'
import Cropper from 'react-cropper' // 引入Cropper
import 'cropperjs/dist/cropper.css' // 引入Cropper对应的css
import { Modal } from 'antd'
function CropperModal({ uploadedImageFile, onClose, onSubmit, cropperModalVisible }) {
const [src, setSrc] = useState(uploadedImageFile)
const cropperRef = useRef(null)
const [cropperSrc, setCropperSrc] = useState(uploadedImageFile)
// 裁剪
function onCrop() {
setCropperSrc(this.cropper.getCroppedCanvas().toDataURL())
}
// 确定回传裁剪图片
function handleSubmit() {
onSubmit(cropperSrc)
}
return (
<Modal
visible={cropperModalVisible}
onCancel={onClose}
onOk={handleSubmit}
maskClosable={false}
>
<div className={styles['cropper-container']}>
<Cropper
src={src}
className="cropper"
ref={cropperRef}
viewMode={1}
// initialAspectRatio={3}
zoomable={true} // 是否缩放
guides={false}
cropend={onCrop} // 裁剪完成触发
// preview=".cropper-preview" // 预览框
/>
</div>
</Modal>
)
}
CropperModal.propTypes = {
uploadedImageFile: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired
}
export default CropperModal
上面代码会将裁剪图片的base64编码回传给父组件
在父组件中
import React, { useState, useEffect, useRef } from 'react';
import { Button, Modal, Input, Upload, message, Select, } from 'antd';
import './index.less';
import CropperModal from '@/components/CropperModal'
import { compressImage} from '@/utils/utils'
const ToolsModal = props => {
const { dispatch, userInfo } = props;
const [imgUrl, setImgUrl] = useState('')
const toolModal = useRef(null);
// 裁剪图片
const [cropperSrc, setCropperSrc] = useState('')
// 裁剪框
const [cropperModalVisible, setCropperModalVisible] = useState(false)
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
function beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/x-icon';
if (!isJpgOrPng) {
message.error('只支持上传JPG或PNG文件!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('图片必须小于2M');
}
return isJpgOrPng && isLt2M;
}
// 上传logo
const handleChange = info => {
setShow(false)
if (info.file.status === 'done') {
getBase64(info.file.originFileObj, imageUrl => {
setCropperSrc(imageUrl)
setCropperModalVisible(true)
// setImgUrl(imageUrl)
});
}
};
// 关闭裁剪modal
function closeCropperModal() {
setCropperModalVisible(false)
}
// 裁剪回调
function submitCropperModal(imgUrl) {
// console.log(imgUrl.length)
setCropperModalVisible(false)
// 压缩函数
compressImage(imgUrl, 200, useImg)
}
// 压缩回调+
function useImg(base64) {
setImgUrl(base64)
}
return (
<Modal>
<div className='tool-content' ref={toolModal}>
<div className='tool-logo'>
<div className='name'>工具logo</div>
<div className='content' style={{ position: 'relative', height: 150 }}>
<div className='logo'>
<div className='logo-preview'>
<img src={objVal.icon ? objVal.icon : clue} alt="" />
<Button
className='logo-edit'
onClick={showIconList}
icon={<EditOutlined />}></Button>
</div>
</div>
<Upload
method='get'
className='btn'
name='file'
listType='picture'
accept={'image/*'}
beforeUpload={beforeUpload}
onChange={handleChange}
showUploadList={false}
>
<Button>自定义logo上传</Button>
</Upload>
</div>
</div>
</div>
{
cropperModalVisible ?
<CropperModal
uploadedImageFile={cropperSrc}
onClose={closeCropperModal}
onSubmit={submitCropperModal}
cropperModalVisible={cropperModalVisible}
/>
: null
}
</Modal>
)
}
export default connect(({ }) => ({
}))(ToolsModal)
utils.js
//图片压缩函数
/*
base64: 图片base64编码
w:默认宽度
callback: 回调函数,及时监听压缩后的base64编码
*/
export function compressImage(base64, w, callback) {
var newImage = new Image();
var quality = 0.5; //压缩系数0-1之间
newImage.src = base64;
// newImage.setAttribute("crossOrigin", 'Anonymous'); //url为外域时需要
var imgWidth, imgHeight;
newImage.onload = function() {
imgWidth = this.width;
imgHeight = this.height;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
if (Math.max(imgWidth, imgHeight) > w) {
if (imgWidth > imgHeight) {
canvas.width = w;
canvas.height = (w * imgHeight) / imgWidth;
} else {
canvas.height = w;
canvas.width = (w * imgWidth) / imgHeight;
}
} else {
canvas.width = imgWidth;
canvas.height = imgHeight;
quality = 0.5;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 在canvas绘制前填充白色背景
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
var base64 = canvas.toDataURL('image/jpeg', quality); //压缩语句
// 如想确保图片压缩到自己想要的尺寸,如要求在50-150kb之间,请加以下语句,quality初始值根据情况自定
// while (base64.length / 1024 > 150) {
// quality -= 0.01;
// base64 = canvas.toDataURL("image/jpeg", quality);
// }
// 防止最后一次压缩低于最低尺寸,只要quality递减合理,无需考虑
// while (base64.length / 1024 < 50) {
// quality += 0.001;
// base64 = canvas.toDataURL("image/jpeg", quality);
// }
callback(base64); //必须通过回调函数返回,否则无法及时拿到该值
};
}
到这里就完成了对上传图片的裁剪以及压缩