解读vant的Uploader上传问题
作者:lemon_sweety
vant的Uploader上传
vant的uploader组件的交互是点击上传之后才会触发一系列回调函数,如果想要实现点击uploader的时候先出现弹框提示,选择了确定之后才进行文件上传这该怎么做到呢?
大致是,给uploader组件外面包裹一层元素,然后给组件设为禁用模式,当外层的元素状态改变时,就改变组件的disabled值,再使用vant提供的chooseFile通过 ref 可以获取到 Uploader 实例并调用实例的方法重新调起文件选择。
主要的步骤如下
首先,我们可以在uploader组件外面包裹一层div
<span @click="handleClick" v-if="isconfirm" class="message" ></span> <van-uploader v-model="fileList" :after-read="afterRead" :disabled="isconfirm" ref="uploadImg" />
然后在data以及methods中进行定义
data() { return { fileList: [], isconfirm: true }; methods: { handleClick() { this.$dialog .confirm({ message: "test,test,test" }) .then(() => { this.isconfirm = false; this.$refs.uploadImg.chooseFile(); }) .catch(() => { this.isconfirm = true; }); }, }
看到这里要注意chooseFile这个方法支持的版本是v2.5.6以上的,如果出现了没有效果的话,先检查一下自己安装的版本是否是符合要求的。
检查之后,版本也符合要求,但是this.$refs.uploadImg.chooseFile()就是没有效果,这是怎么一回事呢?
原来跟浏览器执行机制event loop有关,每当执行到choosefile的时候,组件仍为禁用模式,无法调起,其实isconfirm状态还没有进行改变,所以调起文件选择看不见效果,可以使用一个setTimeout或者是vue中的this.$nextTick()进行解决。
setTimeout(() => { this.$refs.uploadImg.chooseFile(); }, 0); this.$nextTick(() => { this.$refs.uploadImg.chooseFile(); });
vant文件上传Uploader图片压缩
为什么要对图片进行压缩?
随着科技的发展,手机像素越来越好,拍摄的图片也越来越清晰。图片越清晰,体积也就越大。当移动端上传大图时就会发生卡顿,请求超时的情况。
当图片过大时,图片预览也会非常慢,所以需要在图片上传时对图片进行压缩。
vant中文件上传组件Uploader并不支持图片压缩,因为业务场景中有多个地方需要上传图片时对图片进行压缩,所以我基于Uploader封装了一个新的组件。
Uploader组件封装
此组件封装 基于vant文件上传Uploader
API
属性名 | 属性说明 | 默认值 |
---|---|---|
quality | 压缩质量【0-1】 | 0.5 |
compressSwitch | 是否开启压缩 | false |
threshold | 达到此大小开始压缩【500k】 | 500 |
Vant 文件上传Uploader属性请参照 vant官网
模板部分
<template> <van-uploader :fileList="$attrs.value" :before-read="beforeReadFn" v-bind="$attrs" v-on="$listeners"/> </template>
Javascript部分
export default { name: 'van-small-upload', props: { quality:{ type:Number, default:0.1 }, compressSwitch:{ type:Boolean, default:false }, threshold:{ type:Number, default:500 }, beforeRead:{ type: Function, default:()=>true } }, data() { return { } }, methods: { // 处理图片 imgPreview(file,index) { console.log('处理图片Fn...'); let self = this // 看支持不支持FileReader if (!file || !window.FileReader) return; const size = file.size/1024 console.log(`图片大小 ===> ${file.size/1024}k`); console.log('图片压缩:',this.compressSwitch?'开':'关'); console.log('图片压缩阈值:',this.threshold+'k'); console.log('图片压缩降帧值:',this.quality); if (/^image/.test(file.type) && size >= this.threshold && this.compressSwitch) { // 创建一个reader let reader = new FileReader() // 将图片2将转成 base64 格式 reader.readAsDataURL(file) // 读取成功后的回调 reader.onloadend = function() { let result = this.result let img = new Image() img.src = result img.onload = function() { // 压缩 let data = self.compress(img,file.name,file.type) console.log(`压缩后 ===>${data.fileData.size/1024}k`); self.$attrs.value[index].content = data.base64Data self.$attrs.value[index].file = data.fileData } } } }, // 压缩图片 compress(img, name, type) { let canvas = document.createElement('canvas') let ctx = canvas.getContext('2d') //瓦片canvas let tCanvas = document.createElement('canvas') let tctx = tCanvas.getContext('2d') // let initSize = img.src.length; let width = img.width let height = img.height //如果图片大于四百万像素,计算压缩比并将大小压至400万以下 let ratio if ((ratio = (width * height) / 4000000) > 1) { // console.log("大于400万像素"); ratio = Math.sqrt(ratio) width /= ratio height /= ratio } else { ratio = 1 } canvas.width = width canvas.height = height // 铺底色 ctx.fillStyle = '#fff' ctx.fillRect(0, 0, canvas.width, canvas.height) //如果图片像素大于100万则使用瓦片绘制 let count if ((count = (width * height) / 1000000) > 1) { // console.log("超过100W像素"); count = ~~(Math.sqrt(count) + 1) //计算要分成多少块瓦片 // 计算每块瓦片的宽和高 let nw = ~~(width / count) let nh = ~~(height / count) tCanvas.width = nw tCanvas.height = nh for (let i = 0; i < count; i++) { for (let j = 0; j < count; j++) { tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh) ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh) } } } else { ctx.drawImage(img, 0, 0, width, height) } //进行压缩 let ndata = canvas.toDataURL('image/jpeg', this.quality) tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0; return {base64Data:ndata,fileData:this.dataURLtoFile(ndata,name,type)} }, //将base64转换为文件 dataURLtoFile(dataurl,name,type) { name = name ? name : '图片' type = type ? type : 'jpg' var arr = dataurl.split(','), bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new File([u8arr], name, { type: type }) }, beforeReadFn(file,detail){ const {index} = detail this.imgPreview(file,index) return this.beforeRead(...arguments); } }, mounted(){ } };
使用示例
<SmUpload v-model="fileList" :before-read="beforeRead" :compressSwitch="true" :quality="0.5"/>
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。