基于vue-upload-component封装一个图片上传组件的示例
程序员文章站
2023-11-16 15:10:10
需求分析
业务要求,需要一个图片上传控件,需满足
多图上传
点击预览
图片前端压缩
支持初始化数据
相关功能及资源分析
基本功能...
需求分析
业务要求,需要一个图片上传控件,需满足
- 多图上传
- 点击预览
- 图片前端压缩
- 支持初始化数据
相关功能及资源分析
基本功能
先到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 来赋值 src),第二次遍历组件里的 files --> <div class="file-item" v-for="(file, index) in value"> <img :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" :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 imageupload from '@/components/imageupload' ... components: { imageupload }, ... <image-upload :value.sync="pics"/> */ 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.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}) }
最终效果
参考资料
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 我的seo学习心得