操作上没有太大的难度,但难点在于如何让整个动画更加流畅。其中一个主要的问题就是动画的滞后性:当下载进度到达某一点的时候,你用250ms的动画来进行过渡,已经很慢了,所以很多人可能因为这个原因或者麻烦就不直接做动画了,在进度事件触发的时候直接更新进度条的对应位置,但是我们可以尝试实现。
最后,效果如下图所示:
《狗跑》动画是来自codepen的洛蒂动画。
1.获取下载进度
您可以在ajax中获得下载进度,如以下代码所示:
让xhr=new XMLHttpRequest();const DownLoadURl=' installer . dmg ';xhr.open('GET ',downloadUrl,true);Xhr。addeventlistener ('progress ',function(event){//响应头应该有内容长度if (event。长度可计算){让完成百分比=事件。已加载/事件。总计;console.log(百分比完成);//最后一个输出是1} },false);xhr . send();前提是响应头中有一个Content-Length字段,用于通知当前文件中的字节总数,如下图所示:
一般cdn都有这个字段。得到下载进度后,可以用来转换宽度或位置。
2.无动画加载
如果我们不制作动画并直接设置平移位置,它看起来像这样:
代码如下:
让percent complete=event . loaded/event . total;let left=containerWidth * percent complete;//直接为狗的位置设置translatedgbox . style . transform=` translatex($ { left } px)';//进度条的位置也会被翻译。一开始是用translateX(-100%)移到外面的。current progress bar . style . transform=` Translatex($ { percent complete * 100-100 } %)`;在我们的例子中,会特别突兀,没有上面的狗,一张一张卡的感觉可能会更好。所以我们给它添加了一个变换动画。3.添加变换动画
如何做变换动画?方法有很多:jQuery动画、Web动画、requestAnimationFrame、CSS动画结合JS控件、其他第三方动画库等。我更喜欢使用原生网页动画。
因为进度事件触发很快,如果是动画的话也不需要那么快触发,所以给它加一个油门。如以下代码所示:
//触发函数throttle (func,limit=250){ let in throttle=false;return function(){ const args=args;const context=thisif(!introttle){ func . apply(context,args);inThrottle=truesetTimeout(()=Introttle=false,limit);} } }函数onDownloadProgress(事件){ } xhr . AddEventListener(' progress ',throttle(OnDownLoadProgress));当然,不节流也可以。这只是一个优化。
制作变换动画的逻辑在上面的onDownloadProgress函数中处理,如下面的代码所示:
函数OnDownLoadProgress(event){ let CurrentProgress bar=document . query selector('。当前进度条');让dogBox=document.querySelector('。狗盒’);让containerWidth=document . queryselector('。进度条')。clientWidthif(event . length computable){ let percent complete=event . loaded/event . total;let left=containerWidth * percent complete;//动画时间和节流时间一致const time=250//获取当前运动的位移let last transform=window . getcomputed style(dog box)。transform | |“translate x(0)”;//使用原生web动画dogbox。动画({ transform :[最后一个变换,` translated x($ { left } px)`},{ easy : ' linear ',fill : ' forwards ',duration 3360 time });//进度条类似,只是省略了。}}上面的动画时间是250毫秒,和油门时间一致,所以上一次的动画在下一次触发的时候几乎是刚刚结束(其实有点慢)。并且每次触发动画时,都会获取当前平移位置作为此动画的起点,可以保证动画的连续性。
此外,由于我们使用节流,很可能最后一个100%的触发器会丢失,所以需要在完成时手动调整onProgressDownload,否则不会有完成状态。
在播放进度条的情况下,需要监控视频/音频元素的timeupdate事件,大约250毫秒触发一次(实际测量),没有节流。
效果如下图所示:
我们发现最后总尺寸已经显示出来了,也就是下载已经完成了,但是狗狗离终点还有很长一段路。在我们的例子中,似乎没有那么明显,不仔细看也看不出来。但是,如果下载速度快,这个问题就会变得更加明显。比如进度条很长,但是视频只播放了10秒,应该会更明显。
一个简单的解决方案是假设接下来250ms的下载速度是一致的,每次移动都会提前移动250ms。如果这个假设在播放视频的例子中几乎是正确的,因为它是相对均匀的,下载速度是不可控的,但是我们估计并认为在同样短的时间内是一样的。
这样我们就可以记录最后一个位置,然后再增加一个偏移量,如下代码所示:
让DifX=(event . loaded-LastMb)/event . total * containerWidth;//在原来的基础上增加一个偏移量(不能超过容器的宽度)。let left=math.min(容器宽度,容器宽度*完成百分比diffx);lastMB=downloadedMB这很对,效果如下图所示:
这个案子基本到此为止。这个例子比较简单,但是你可能会觉得网页动画的兼容性不是很好。主要是因为Chrome兼容性更好,其他主流浏览器的新版本已经开始支持了。其他不受支持的浏览器可以使用谷歌官方的polyfill,后者更大。和CSS动画一样,但是可以用JS控制开始暂停,所以和CSS动画一样有优势,比如GPU加速,不占用JS线程。
摘要
以上是边肖介绍的JS中下载进度条和播放进度条的代码。希望对大家有帮助。如果你有任何问题,请给我留言,边肖会及时回复你的!