JS中实现一个下载进度条及播放进度条的代码


Posted in Javascript onJune 10, 2019

术上没太大难度,有难度的地方是怎么让整个动画比较流畅。一个主要问题是动画的滞后性:当下载进度到某个点的时候,你再用250ms的动画过渡过去,这个时候已经慢了,所以很多人可能因为这个原因或者嫌麻烦,直接就不做动画了,在进度事件触发的时候直接更新进度条相应的位置,不过我们可以尝试实现一下。

最后做出来的效果如下图所示:

JS中实现一个下载进度条及播放进度条的代码 

小狗奔跑的动画是一个lottie动画,来自 codepen 。

1. 获取下载进度

ajax里面可以拿到下载进度,如下代码所示:

let xhr = new XMLHttpRequest();
 const downloadUrl = 'installer.dmg';
 xhr.open('GET', downloadUrl, true);
 xhr.addEventListener('progress', function (event) {
  // 响应头要有Content-Length
  if (event.lengthComputable) {
   let percentComplete = event.loaded / event.total;
   console.log(percentComplete); // 最后输出1
  }
 }, false);
 xhr.send();

前提是响应头里面有Content-Length这个字段告知当前文件的总字节数,如下图所示:

JS中实现一个下载进度条及播放进度条的代码 

一般CDN都会有这个字段。拿到下载进度之后便可用来换算宽度或者位置。

2. 没有动画的loading

如果我们不做动画,直接设置translate位置,那么看起来是这样的:

JS中实现一个下载进度条及播放进度条的代码 

代码如下所示:

let percentComplete = event.loaded / event.total;
let left = containerWidth * percentComplete;
// 狗的位置直接设置translate
dogBox.style.transform = `translateX(${left}px)`;
// 进度条的位置也是translate,一开始是用translateX(-100%)挪到外面去
currentProgressBar.style.transform = `translateX(${percentComplete * 100 - 100}%)`;
在我们这个例子里面会显得特别突兀,一卡一卡的感觉,如果没有上面那条狗可能还会好一点。所以我们给它加个transform动画。

3. 加上transform动画

transform动画怎么做呢?方法有很多:jQuery的animate、Web Animation、requestAnimationFrame、CSS动画结合JS控制、其它第三方动画库等等,我比较喜欢用原生Web Animation。

由于progress event触发得比较快,加上做动画的话不需要触发得那么快,所以给它加一个节流。如下代码所示:

// 最快250ms触发一次
function throttle (func, limit = 250) {
 let inThrottle = false;
 return function() {
  const args = arguments;
  const context = this;
  if (!inThrottle) {
   func.apply(context, args);
   inThrottle = true;
   setTimeout(() => inThrottle = false, limit);
  }
 }
}
function onDownloadProgress (event) {
 
}
xhr.addEventListener('progress', throttle(onDownloadProgress));

当然你不加节流也是可以的,这里只是一个优化。

做transform动画的逻辑便在上面的onDownloadProgress这个函数里面处理,如下代码所示:

function onDownloadProgress (event) {
 let currentProgressBar = document.querySelector('.current-progress-bar');
 let dogBox = document.querySelector('.dog-box');
 let containerWidth = document.querySelector('.progress-bar').clientWidth;
 
 if (event.lengthComputable) {
  let percentComplete = event.loaded / event.total;
  let left = containerWidth * percentComplete;
  // 动画时间和节流时间保持一致
  const time = 250;
  // 获取到当前运动的位移
  let lastTransform = window.getComputedStyle(dogBox).transform || 'translateX(0)';
  // 使用原生web animation
  dogBox.animate({
   transform: [lastTransform, `translateX(${left}px)`]
  }, {
   easing: 'linear',
   fill: 'forwards',
   duration: time
  });
  // 进度条类似,省略
 }
}

上面动画的时间为250ms和节流的时间保持一致,这样下次触发的时候上次的动画差不多刚好做完(实际上是慢了一点)。并且每次触发动画的时候都是获取当前的translate位置,做为本次动画的起点,这样可以保证动画的连贯性。

另外,由于我们使用了节流很可能会导致最后的那次100%的触发丢了,所以需要在完成的时候手动调一下onProgressDownload,否则会没有完成态。

如果是播放进度条的例子,需要监听video/audio元素的timeupdate事件,这个事件的触发约 250ms (实测)触发一次,可以不用节流。

效果如下图所示:

JS中实现一个下载进度条及播放进度条的代码 

我们发现在最后数字已经显示总大小了即已经下载完成了,但是那条狗离终点还有段距离,在我们这个例子似乎没那么明显,不仔细看还看不太出来。但如果下载速度很快的时候这个问题会更加明显,在播放进度条的例子便是如果进度条很长,但是播放的视频只有10几秒,那么应该也会比较明显。

一个简单的解决方法是假定下一个250ms的下载速度保持一致,每次运动的时候都提前运动250ms,如果在播放video的例子里面这个假定几乎是对的,因为比较匀速,而下载速度不可控,但在连续相同很短的时间内我们估且认为是一样。

所以我们可以记录一下上一次的位置,然后加多一个偏移,如下代码所示:

let diffX = (event.loaded - lastMB) / event.total * containerWidth;
// 在原本的基础上再加多一个偏移(且不能超过容器的宽度)
let left = Math.min(containerWidth, containerWidth * percentComplete + diffX);
lastMB = downloadedMB;

这样就比较对得上了,效果如下图所示:

JS中实现一个下载进度条及播放进度条的代码 

这个案例到这里基本就介绍结束,这个例子比较简单,不过你可能会觉得web animation的兼容性不太好。主要是在Chrome的兼容性比较好,其它主流的浏览器的新版本也已经开始支持了。其它不支持的浏览器可以使用谷歌官方的一个 polyfill ,就是比较大一点。它和CSS动画一样,但是可以用JS去控制开始暂停等,所以它和CSS动画一样具有GPU加速,不占用JS线程等优势。

总结

以上所述是小编给大家介绍的JS中实现一个下载进度条及播放进度条的代码,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Javascript 相关文章推荐
JavaScript 应用技巧集合[推荐]
Aug 30 Javascript
JavaScript 学习笔记之一jQuery写法图片等比缩放以及预加载
Jun 28 Javascript
js防止表单重复提交实现代码
Sep 05 Javascript
Jquery封装tab自动切换效果的具体实现
Jul 13 Javascript
JavaScript中的常见问题解决方法(乱码,IE缓存,代理)
Nov 28 Javascript
JavaScript sup方法入门实例(把字符串显示为上标)
Oct 20 Javascript
浅谈JavaScript的事件
Feb 27 Javascript
jquery简单的弹出层浮动层代码
Apr 27 Javascript
获取当前月(季度/年)的最后一天(set相关操作及应用)
Dec 27 Javascript
Echarts基本用法_动力节点Java学院整理
Aug 11 Javascript
vue-router判断页面未登录自动跳转到登录页的方法示例
Nov 04 Javascript
js实现每日签到功能
Nov 29 Javascript
vuex 中插件的编写案例解析
Jun 10 #Javascript
使用webpack搭建vue项目及注意事项
Jun 10 #Javascript
详解iview的checkbox多选框全选时校验问题
Jun 10 #Javascript
前端路由&webpack基础配置详解
Jun 10 #Javascript
在Vue中用canvas实现二维码和图片合成海报的方法
Jun 10 #Javascript
vue中使用 pako.js 解密 gzip加密字符串的方法
Jun 10 #Javascript
移动端 Vue+Vant 的Uploader 实现上传、压缩、旋转图片功能
Jun 10 #Javascript
You might like
最新的php 文件上传模型,支持多文件上传
2009/08/13 PHP
老版本PHP转义Json里的特殊字符的函数
2015/06/08 PHP
PHP unlink与rmdir删除目录及目录下所有文件实例代码
2018/02/07 PHP
laravel model模型处理之修改查询或修改字段时的类型格式案例
2019/10/17 PHP
php使用fputcsv实现大数据的导出操作详解
2020/02/27 PHP
Node.js和MongoDB实现简单日志分析系统
2015/04/25 Javascript
jQuery仿京东商城楼梯式导航定位菜单
2016/07/25 Javascript
javascript之IE版本检测超简单方法
2016/08/20 Javascript
使用3D引擎threeJS实现星空粒子移动效果
2020/09/13 Javascript
js 毫秒转天时分秒的实例
2017/11/17 Javascript
ES6 Set结构的应用实例分析
2019/06/26 Javascript
Node.js中出现未捕获异常的处理方法
2020/06/29 Javascript
[52:06]FNATIC vs NIP 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
python改变日志(logging)存放位置的示例
2014/03/27 Python
Python类的多重继承问题深入分析
2014/11/09 Python
Tensorflow模型实现预测或识别单张图片
2019/07/19 Python
pymysql模块的使用(增删改查)详解
2019/09/09 Python
Python使用贪婪算法解决问题
2019/10/22 Python
Python数据可视化实现漏斗图过程图解
2020/07/20 Python
如何基于matlab相机标定导出xml文件
2020/11/02 Python
html5 input输入实时检测以及延时优化
2018/07/18 HTML / CSS
凯特方迪化妆品官网:Kat Von D Beauty
2016/11/15 全球购物
全球知名的婚恋交友网站:Match.com
2017/01/05 全球购物
城野医生官方海外旗舰店:风靡亚洲毛孔收敛水
2018/04/26 全球购物
英国异国风情旅游网站:Travel Talk Tours(团体旅游、探险旅游、帆船假期)
2018/07/26 全球购物
美国室内盆栽植物购买网站:Plants.com
2020/04/24 全球购物
vue路由实现登录拦截
2021/03/24 Vue.js
外贸公司实习自我鉴定
2013/09/24 职场文书
物流专业大学生职业生涯规划书范文
2014/01/15 职场文书
保险专业自荐信范文
2014/02/20 职场文书
单位承诺书格式
2014/05/21 职场文书
党员个人剖析材料2014
2014/10/08 职场文书
党支部先进事迹材料
2014/12/24 职场文书
2016中考冲刺决心书
2015/09/22 职场文书
《绝招》教学反思
2016/02/20 职场文书
Python中else的三种使用场景
2021/06/16 Python