vue-upload-component如何封装一个图片上传组件
这篇文章主要介绍了vue-upload-component如何封装一个图片上传组件,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
需求分析
业务要求,需要一个图片上传控件,需满足
多图上传
点击预览
图片前端压缩
支持初始化数据
相关功能及资源分析
基本功能
先到https://www.npmjs.com/search?q=vue+upload
上搜索有关上传的控件,没有完全满足需求的组件,过滤后找到 vue-upload-component
组件,功能基本都有,自定义也比较灵活,就以以此进行二次开发。
预览
因为项目是基于 vant
做的,本身就提供了 ImagePreview
的预览组件,使用起来也简单,如果业务需求需要放大缩小,这个组件就不满足了。
压缩
可以通过 canvas
相关api来实现压缩功能,还可以用一些第三方库来实现, 例如image-compressor.js
数据
因为表单页面涉及编辑的情况,上传组件为了展示优雅点,需要做点处理。首先就先要对数据格式和服务端进行约定,然后在处理剩下的
开发
需求和实现思路基本确定,开始进入编码,先搭建可运行可测试的环境
第一步,创建相关目录
|- components
|- ImageUpload
|- ImageUpload.vue
|- index.js
第二步,安装依赖
$ npm i image-compressor.js -S
$ npm i vue-upload-component -S
第三步,编写核心主体代码
// index.js
import ImageUpload from './ImageUpload'
export default ImageUpload
// ImageUpload.vue
<template>
<div class="m-image-upload">
<!--
这里分为两段遍历,理由是:在编辑情况下要默认为组件添加默认数据,虽然说组件提供了 `add` 方法,
但在编辑状态下,需要把 url 形式的图片转换成 File 之后才可以添加进去,略微麻烦。
所以分两次遍历,一次遍历表单对象里的图片(直接用img标签展示,新上传的图片可以通过 blob 来赋值 class="lazy" data-src),第二次遍历组件里的 files
-->
<div
class="file-item"
v-for="(file, index) in value">
<img
:class="lazy" data-src="file.thumb || file.url"
@click="preview(index)"
/>
<van-icon
name="clear"
class="close"
@click="remove(index, true)"/> <!-- 把图片从数组中删除 -->
</div>
<div
:class="{'file-item': true, 'active': file.active, 'error': !!file.error}"
v-for="(file, index) in files"> <!-- 加几个样式来控制 `上传中` 和 `上传失败` 的样式-->
<img
v-if="file.blob"
:class="lazy" data-src="file.blob"
/>
<div class="uploading-shade">
<p>{{ file.progress }} %</p>
<p>正在上传</p>
</div>
<div class="error-shade">
<p>上传失败!</p>
</div>
<van-icon
name="clear"
class="close"
@click="remove(index)"
/>
</div>
<file-upload
ref="uploader"
v-model="files"
multiple
:thread="10"
extensions="jpg,gif,png,webp"
post-action="http://localhost:3000/file/upload"
@input-file="inputFile"
@input-filter="inputFilter"
>
<van-icon name="photo"/>
</file-upload>
</div>
</template>
<script>
import uploader from 'vue-upload-component'
import ImageCompressor from 'image-compressor.js';
import { ImagePreview } from 'vant';
export default {
name: 'ImageUpload',
props: {
value: Array // 通过`.sync`来添加更新值的语法题,通过 this.$emit('update:value', this.value) 来更新
},
data() {
return {
files: [] // 存放在组件的file对象
}
},
components: {
'file-upload': uploader
},
methods: {
// 当 add, update, remove File 这些事件的时候会触发
inputFile(newFile, oldFile) {
// 上传完成
if (newFile && oldFile && !newFile.active && oldFile.active) {
// 获得相应数据
if (newFile.xhr && newFile.xhr.status === 200) {
newFile.response.data.thumb = newFile.thumb // 把缩略图转移
this.value.push(newFile.response.data) // 把 uploader 里的文件赋值给 value
this.$refs.uploader.remove(newFile) // 删除当前文件对象
this.$emit('update:value', this.value) // 更新值
}
}
// 自动上传
if (Boolean(newFile) !== Boolean(oldFile) || oldFile.error !== newFile.error) {
if (!this.$refs.uploader.active) {
this.$refs.uploader.active = true
}
}
},
// 文件过滤,可以通过 prevent 来阻止上传
inputFilter(newFile, oldFile, prevent) {
if (newFile && (!oldFile || newFile.file !== oldFile.file)) {
// 自动压缩
if (newFile.file && newFile.type.substr(0, 6) === 'image/') { // && this.autoCompress > 0 && this.autoCompress < newFile.size(小于一定尺寸就不压缩)
newFile.error = 'compressing'
// 压缩图片
const imageCompressor = new ImageCompressor(null, {
quality: .5,
convertSize: Infinity,
maxWidth: 1000,
})
imageCompressor.compress(newFile.file).then((file) => {
// 创建 blob 字段 用于图片预览
newFile.blob = ''
let URL = window.URL || window.webkitURL
if (URL && URL.createObjectURL) {
newFile.blob = URL.createObjectURL(file)
}
// 缩略图
newFile.thumb = ''
if (newFile.blob && newFile.type.substr(0, 6) === 'image/') {
newFile.thumb = newFile.blob
}
// 更新 file
this.$refs.uploader.update(newFile, {error: '', file, size: file.size, type: file.type})
}).catch((err) => {
this.$refs.uploader.update(newFile, {error: err.message || 'compress'})
})
}
}
},
remove(index, isValue) {
if (isValue) {
this.value.splice(index, 1)
this.$emit('update:value', this.value)
} else {
this.$refs.uploader.remove(this.files[index])
}
},
preview(index) {
ImagePreview({
images: this.value.map(item => (item.thumb || item.url)),
startPosition: index
});
}
}
}
</script>
图片压缩也可以自己来实现,主要是理清各种文件格式的转换
compress(imgFile) {
let _this = this
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.onload = e => {
let img = new Image()
img.class="lazy" data-src = e.target.result
img.onload = () => {
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
canvas.width = img.width
canvas.height = img.height
// 铺底色
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 0, 0, img.width, img.height)
// 进行压缩
let ndata = canvas.toDataURL('image/jpeg', 0.3)
resolve(_this.dataURLtoFile(ndata, imgFile.name))
}
}
reader.onerror = e => reject(e)
reader.readAsDataURL(imgFile)
})
}
// base64 转 Blob
dataURLtoBlob(dataurl) {
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], {type: mime})
},
// base64 转 File
dataURLtoFile(dataurl, filename) {
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, {type: mime})
}
最终效果
感谢你能够认真阅读完这篇文章,希望小编分享的“vue-upload-component如何封装一个图片上传组件”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网行业资讯频道,更多相关知识等着你来学习!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341