最近写项目,遇到一个有意思的需求,分享一下实现过程:
需求简单描述:对现有图片(网络路径)进行裁剪,也可以上传一张图片进行裁剪,旋转,达到自己想要的效果。
应用技术:react hook,ant-design,react-cropper(插件)
项目中需要安装插件:
npm install react-cropper --save-dev
注:这里用的react-crooper 版本号 @1.2.0 ,不同版本存在使用中的差异,所以这里注明当前例子使用的是1.2.0。
组件中引用:
import Cropper from 'react-cropper';
import 'cropperjs/dist/cropper.css';
注:因项目中webpack配置的打包编译在css-loader这一块配置的时候,exclude 了node_module包中对css的编译,导致引入插件后编译通不过,需要 【include: [/[\\/]node_modules[\\/].*cropperjs/],】添加对cropperjs中的css进行编译操作。
父组件操作:
import React from 'react'
import CropperModal from './CropperModal'
import { getBase64Image, main } from 'utils'
const url = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
export default () => {
const [hooksModalFile,setHooksModalFile] = React.useState();
const [modalVisible, setModalVisible] = React.useState();
useEffect(()=>{
main(userDetail.avatarUrl, (base64) => {
const file = base64toFile(base64, '图片名称');
setHooksModalFile(file);
setModalVisible(true);
});
},[hooksModalFile]);
const getNewImgUrl = (event) => {
// 转化后,并且已上传到图片库中的图片路径
};
return {
setModalVisible(false);
}}
onSubmit={getNewImgUrl}
/>
}
具体实现:
import React, { useState, useEffect, useCallback, useRef } from 'react'
import { Button, Upload } from 'antd';
import Cropper from 'react-cropper' // 引入Cropper
import 'cropperjs/dist/cropper.css' // 引入Cropper对应的css
import './index.scss'
export default ({ uploadedImageFile, onClose, onSubmit }) => {
const [src, setSrc] = useState(null);
const [fileList, setFileList] = React.useState([]);
const cropperRef = useRef(null);
const uploadProps = {
beforeUpload: file1 => {
setFileList([file1]);
setIsShow(false);
const fileReader = new FileReader();
fileReader.onload = e => {
const dataURL = e.target.result;
setNewSrc(dataURL);
};
fileReader.readAsDataURL(file1);
return false;
},
multiple: false,
accept: '.png, .jpeg, .jpg',
};
useEffect(() => {
const fileReader = new FileReader()
fileReader.onload = e => {
const dataURL = e.target.result
setSrc(dataURL)
}
fileReader.readAsDataURL(uploadedImageFile)
}, [uploadedImageFile])
const handleSubmit = useCallback(() => {
console.log('正在上传图片')
// TODO: 这里可以尝试修改上传图片的尺寸
cropperRef.current.getCroppedCanvas().toBlob(async blob => {
console.log('blob', blob);
// 创造提交表单数据对象
const formData = new FormData()
// 添加要上传的文件
// formData.append('file', blob, filename)
// 提示开始上传 (因为demo没有后端server, 所以这里代码我注释掉了, 这里是上传到服务器并拿到返回数据的代码)
// 上传图片
// 此处实现上传操作 ajax请求
// 提示上传完毕
//把选中裁切好的的图片传出去
onSubmit(blob)
// 关闭弹窗
onClose()
})
}, [onClose, onSubmit])
return (
点击提交
)
}
工具准备:
1. 需要将拿到的网络图片地址url,转化为文件类型格式(转化方式:a.需要将url转化为base64格式;b. base64格式转为文件类型)
const getBase64Image = (img) => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width, img.height);
const dataURL = canvas.toDataURL('image/png'); // 可选其他值 image/jpeg
return dataURL;
};
const main = (src, cb) => {
const image = new Image();
image.src = `${src}?v=${Math.random()}`; // 处理缓存
image.crossOrigin = '*'; // 支持跨域图片
image.onload = function() {
const base64 = getBase64Image(image);
cb && cb(base64);
};
};
export { getBase64Image, main };
图片添加旋转功能:
const rotateClick = () => {
console.log('cropperRef',cropperRef.current)
cropperRef.current.cropper.rotate(90);
}
index.sass文件:
.hooks-cropper-modal {
position: fixed;
background-color: rgba(#000000, 0.3);
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
.modal-panel {
width: 880px;
height: 500px;
background: white;
padding: 20px;
display: flex;
flex-direction: column;
align-items: stretch;
.button-row {
height: 50px;
flex-shrink: 0;
display: flex;
justify-content: center;
.submit-button {
padding: 0 20px;
height: 100%;
color: #383838;
font-size: 14px;
}
}
.cropper-container-container {
flex: 1;
display: flex;
align-items: stretch;
justify-content: space-between;
height: 100%;
.cropper-container {
flex: 0 0 600px;
margin-right: 20px;
.cropper {
width: 100%;
height: 100%;
}
}
.preview-container {
flex: 1;
display: flex;
align-items: flex-end;
.cropper-preview {
width: 180px;
height: 180px;
overflow: hidden;
border: 1px solid #383838;
}
}
}
}
}
此次实现过程时间较短,功能实现相对粗糙了些,但基本需求效果均已实现,欢迎大佬们提出你的宝贵建议。