前言
需求是,进入页面加载多个vtk3d模型文件,因为文件特别大,从服务器下载就比较慢,需要做个进度条优化用户体验。
如下图所示:
思路
1、事件总线传递值 (文件总大小、当前文件加载大小)
由于接口调用封装的太深,与组件难以关联,要么进行vuex保存,要么事件总线(事件监听,事件发布),
当前采用的是事件总线的方式处理(便于理解,后期可以用vuex优化性能)
2、this.$set()动态保存每一个文件进度
多个文件都要分别保存文件总大小、当前文件加载大小,这里采用this.$set()进行数据存储操作
3、对象、数组求和,求出总的文件大小和总的加载大小
4、样式动态化
步骤
-
事件总线
//event-bus.js import Vue from 'vue' export const EventBus = new Vue()
import { EventBus } from "../../../../../../src/components/core/event-bus.js"; // 接口逻辑 xhr.onprogress = (event)=>{ EventBus.$emit(`${url}`,{load:event.loaded,total:event.total}) }; 解释:因为每个文件的url都是唯一的,所以漫游的事件名称就按url作为事件名称,传递的是一个对象 接口逻辑每50ms执行一次,此处不需要做其他事项
完整的接口逻辑 (原生接口请求,对接口熟悉的可以跳过下述)
function openAsyncXHR(method, url, options = {}) { const xhr = new XMLHttpRequest(); xhr.onprogress = (event)=>{ EventBus.$emit(`${url}`,{load:event.loaded,total:event.total}) }; xhr.open(method, url, true); xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); if (options.headers) { // 这个位置是传递token值的 Object.entries(options.headers).forEach(([key, value]) => xhr.setRequestHeader(key, value) ); } console.log('接口位置1', xhr) // 这个位置已经执行完了xhr if (options.progressCallback) { console.log('options.progressCallback',options.progressCallback) // 没有走这个 xhr.addEventListener('progress', options.progressCallback); } return xhr; } function fetchBinary(url, options = {}) { return new Promise((resolve, reject) => { const xhr = openAsyncXHR('POST', url, options); xhr.onreadystatechange = (e) => { if (xhr.readyState === 4) { if (xhr.status === 200 || xhr.status === 0) { resolve(xhr.response); } else { reject({ xhr, e }); } } }; // Make request xhr.responseType = 'arraybuffer'; xhr.send(); }); }
-
组件使用
// 定义属性:data
progress:{}, // 接收漫游的数据
percentage:'', // 百分比
load:0,// 文件总加载大小
sum:0, // 文件总大小
time:'', // 记录时间
loadeveTime:'' //平均速度
mounted钩子函数下
this.fileLists.forEach((item)=>{
//文件遍历 根据每个url定义监听
EventBus.$on(`${item.url}`,(msg)=>{
if(!this.time){ // 记录当前时间、求平均速度
this.time = new Date().getTime();
}
//1、 事件总线获取当前的下载速度
console.log('msg',msg)
// msg是获取的是对象(每个文件的总大小 ,当前加载的大小)
this.$set(this.progress,`${item.url}`,msg)
// 在this.progress动态添加以url作为属性名的对象
//2、对this.progress下的数据进行求和,取出总的文件大小 、总的加载数据大小
this.load = Object.values(this.progress).reduce(function (pre,cur) {
// 对象嵌套对象,对内层对象中相同属性的求和
return pre+cur.load;
},0)
this.loadeveTime = this.load*1000/(new Date().getTime()-this.time);// 平均速度
this.sum = Object.values(this.progress).reduce(function (pre,cur) {
return pre+cur.total;
},0)
this.percentage = ((this.load/this.sum)*99).toFixed(2)
// 3、页面数据渲染
document.getElementById('son').style.width=this.percentage+'%'
})
})
解释:
对Object.values(this.progress).reduce(function (pre,cur) {}求和的理解
我的这篇文章有详细介绍:对象中嵌套对象,求内层对象某个相同属性和 js
页面
<div :class="$style.progress">
{{percentage}}%
<div :class="$style.father">
<div id="son" :class="$style.son"></div>
</div>
{{loadeveTime }}M/s
</div>
.progress{
margin: 0 auto;
margin-top: 25%;
font-size: 16px;
font-family: PingFang SC;
font-weight: bold;
color: #F5F5F5;
}
.father{
width: 364px;
height: 15px;
margin: 0 auto;
position: relative;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.17), rgba(255, 255, 255, 0.17));
}
.son{
position: absolute;
top: 0;
left: 0;
height: 100%;
/* width: 201px; */
background: #1E66B7;
}
3.其他
如果存在total为0的情况