基于 vue-cropper 的简单图片裁剪插件实现
起由
最近在工作中遇到了在 VUE 下需要简单裁剪图片的需求,同时还要对图片进行相关大小和格式的限制
这个时候就想起了在 jq 时代大名鼎鼎的 cropper.js 了
简单的界面设计如下图:
效果图分析
从图中可以看出,我们需要的功能主要有如下几方面的内容:
- 获取到图片资源
- 一个带模板的弹出层
- 截图组件
- 交互区域(这里指图中的确定取消按钮)
- 足够的可扩展性
实现思路
首先需要完成的当然是获取到用户上传的文件,第一个想法是使用原生的方案,也即使用原生的 input 框进行相关事件的获取
<input type="file" />
这样的方式扩展是最灵活的,但是同时也是最繁琐的,事无巨细都需要一一考虑,这不符合我们敏捷开发的原则
考虑到项目中已引入 elementui 的环境,故考虑使用 中的 组件来进行获取文件的操作
项目需求决定只能操作单张图片,进行相关配置后效果图如下:
触发选择框获取到了文件之后首先需要对于 文件类型 和 大小 进行校验,确定其为我们真实希望获得的类型和满足我们对于图片的大小限制
接着我们需要将获取到的图片传递给截图组件,且在用户按下确认或取消时能 立即响应, 完成获取截图的文件和截图等过程,最后将新截的图片加进预览并保存新的图片状态
以上便是我们在这个组件中整个想要完成的功能流程
Show me the code
其实在同样的思路下,会有千万种实现方法,以下仅是我个人对于这一想法的一些实践,欢迎讨论指正
首先是有 标签(此处隐去引入库及挂载过程)
<el-upload
:before-upload="beforeUpload" // 上传前的事件,在这里做文件类型和大小的检查
:file-list="showImgUrl" // 图片预览列表
:http-request="picUpload" // 上传事件,覆盖默认上传事件,应在此处将图片传入截图组件
:limit="1" // 文件数量限制
:multiple="false" // 选择文件时不允许选择多文件
:on-exceed="overFile" // 文件数量超出限制时的钩子函数,应在此处做覆盖以及提示
:on-remove="removeFile" // 图片删除时的钩子函数,应在此时清空图片的缓存相关操作
action // 默认跳转地址,这里设置为空
list-type="picture-card" // 文件选择框的样式以及插入动画
ref="upload" // DOM 钩子
>
<i class="el-icon-plus"></i> // 样式需要的图标
</el-upload>
我们整个流程都可以从 标签的生命周期处体现
接着我们来一个一个看看在这些钩子函数里我们都做了什么吧:
// 图片上传前校验
// 这里对图片类型和大小做出限制
beforeUpload(file) {
const isLt5M = file.size < 5 * 1024 * 1024
const fileType = file.type.split('/')[0]
if (!isLt5M) {
this.$message.error('图片大小不能超过5M')
return false
}
if (fileType !== 'image') {
this.$message.error('只能上传图片格式')
return false
}
},
图片校验通过后即进入裁剪流程,我们是通过覆盖默认上传事件实现的
在这里由于从本地读出时没有相关的 url 信息,所以我们在这里使用 fileReader API 把获取到的图片文件转化成 base64 的格式,然后再进行相关的操作
// 在生命周期挂载完成的时候判断浏览器中是否为现代浏览器
// 若不支持 FileReader API 则提示并退出编辑操作
mounted() {
if (!window.FileReader) {
this.$alert('您的浏览器版本过旧,请更换现代浏览器进行操作', '提示').then(
() => {
this.__cancel()
}
)
}
},
// 图片上传前触发裁剪组件
// 将图片读出并在完成时触发裁剪
picUpload(option) {
let file = option.file
if (file) {
this.fileReader.readAsDataURL(file)
}
this.fileReader.onload = () => {
let src = this.fileReader.result
this.cropperShow = true
this.cropperImg = src
}
},
这里应该调起裁剪插件,猛地发现,插件我都还没新增鸭
可怕!!
于是就回去翻了翻 cropperjs 的文档,emmmm, 真的没有意外,还是 jq 时代的样子,没有办法直接拿下来就用,那么这个轮子究竟有没有人已经造过了呢,目光下移, vue-cropper 进入了我的视线
(这里忽略下载引入的过程,直接从使用开始)
// 图片裁剪引用
// el-dialog 为 element 中弹框组件
// cropperShow: 是否显示 load: 用户是否处于交互状态 cropperImg: 图片源
// 更多配置项请参考 vue-cropper 相关文档
<el-dialog :visible.sync="cropperShow" width="640px" :close-on-click-modal="false" :show-close="false">
<span slot="title">操作图片</span> // 标题文字
<vueCropper
ref="cropper" // Dom钩子,用于获取绑定其上的相关属性及函数
class="cropper"
:img="cropperImg" // 裁剪图片源
:can-scale="!load" // 是否允许滚轮缩放
:auto-crop="true" // 是否默认生成截图框
:fixed-box="true" // 是否固定截图框大小
enlarge="2" // 输出比例倍数
:output-size="option.size" // 生成图片质量
:output-type="option.outputType" // 生成图片格式
:center-box="option.centerBox" // 截图框是否限制在图片内
:fixed-number="[1,1]" // 截图框的宽高比例
></vueCropper>
<div class="cropper-btn">
<el-button type="primary" @click="confirm" :loading="load">确定</el-button>
<el-button @click="cancel" :disabled="load">取消</el-button>
</div>
<el-dialog // 用户交互中弹出的提示弹框
width="30%"
:visible.sync="load"
append-to-body
>
<div slot="title">飞速处理图片中...</div>
</el-dialog>
</el-dialog>
// 配置参数
option: {
size: 0,
outputType: '',
centerBox: false
},
此时我们就能看到文章开头所呈现的相关效果了,并且我们也能通过插件提供的钩子函数获取到相应的图片数据
但是我们想要走完整个流程,还必须添加交互响应事件以及进行相关的上传操作
// 确定选择
// dom.getCropData() 获取当前截图框图像的 base64 格式的数据
// 获取成功数据后调起上传操作
confirm() {
this.$refs.cropper.getCropData(data => {
this.httpRequest(data)
this.load = true
})
},
// 取消
cancel() {
console.log('cancel edit picture')
// 关闭裁剪组件相关操作
}
// 图片裁剪后上传
// 这里可进行进一步优化将 config 请求配置写入 http 请求库中
httpRequest(src) {
let config = {
url: '***', // 这里填后端的图片上传链接
method: 'post',
data: {
// 截取 base64 文件部分
image: src.split(',')[1]
}
}
this.$ehttp
.postUpload(config)
.then(res => {
console.log(res, '*********************************************')
if (res.data.code === 0) {
this.params.properties.imageHash = res.data.data
this.$message.success('图片编辑成功')
this.$refs.upload.clearFiles() // 清空预览列表
this.showImgUrl = [{ name: 'pic', url: src }] // 将新上传图片插入上传列表
this.cropperShow = false
} else {
// this.$message.error('res.dat')
this.$refs.upload.clearFiles()
}
})
.catch(err => {
this.$message.error(err)
})
},
结语
至此我们整个裁剪插件就已经全部完成了,通过上传回参可以任意处理裁剪后的图像了
整个流程下来主要的感觉是对于图片格式的转换会相对的比较繁琐
vue-cropper 插件本身只完成裁剪的功能,方便我们在此基础上开发个性化的裁剪插件
同时由于其基于 cropperjs 开发,所以在截图框的样式方面由插件写死了,若是我们对于截图框的样式也有个性化需求则可 down 下源码进行自定义修改,这已经超出了本文的范围故会在后面的文章中进行相关的研究(坑先挖好,填不填就看心情了hhh)
傲娇.jpg
参考资料
ElementUI: https://element.eleme.cn/#/zh-CN/component/upload
CropperJS: https://github.com/fengyuanchen/cropper
vue-cropper: https://github.com/xyxiao001/vue-cropper
PS
最后当然是日常求赞环节啦~
Clancy
2019.5.30