解读vant的Uploader上传问题
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.class="lazy" data-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.class="lazy" data-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"/>
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341