A code of downloading progress bar and playing progress bar in JS

Time:2020-1-8

It’s not too difficult technically. What’s the difficulty is how to make the whole animation smooth. One of the main problems is the lag of animation: when the download progress reaches a certain point, you use 250ms of animation to transition to the past, which is already slow, so many people may not do animation directly because of this reason or too much trouble, and update the corresponding position of the progress bar when the progress event is triggered, but we can try to achieve it.

The final effect is as follows:

 

The dog running animation is a Lottie animation from codepen.

1. Get download progress

You can get the download progress in Ajax, as shown in the following code:

let xhr = new XMLHttpRequest();
 const downloadUrl = 'installer.dmg';
 xhr.open('GET', downloadUrl, true);
 xhr.addEventListener('progress', function (event) {
  //The response header should have content length
  if (event.lengthComputable) {
   let percentComplete = event.loaded / event.total;
   Console. Log (percentcomplete); // the last output is 1
  }
 }, false);
 xhr.send();

The precondition is that the content length field in the response header tells the total bytes of the current file, as shown in the following figure:

 

Generally, CDN has this field. After getting the download progress, it can be used to convert the width or location.

2. Loading without animation

If we don’t animate and directly set the translate position, it looks like this:

 

The code is as follows:

let percentComplete = event.loaded / event.total;
let left = containerWidth * percentComplete;
//Set translate directly for dog position
dogBox.style.transform = `translateX(${left}px)`;
//The position of the progress bar is also translate. At the beginning, it was moved to the outside with translatex (- 100%)
currentProgressBar.style.transform = `translateX(${percentComplete * 100 - 100}%)`;
In our case, it will be very abrupt. The feeling of one card one card may be better without the dog above. So let's animate it with transform.

3. Add transform animation

How to do transform animation? There are many methods: jQuery’s animation, web animation, requestanimationframe, CSS animation combined with JS control, other third-party animation libraries, etc. I prefer to use native web animation.

Because progress event is triggered fast, and animation doesn’t need to be triggered so fast, so add a throttle to it. The code is as follows:

//Trigger once in 250ms at most
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));

Of course, you can do without throttling. It’s just an optimization.

The logic of transforming animation is processed in the function ondownloadprogress above, as shown in the following code:

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;
  //Animation time is consistent with throttling time
  const time = 250;
  //Get the displacement of the current motion
  let lastTransform = window.getComputedStyle(dogBox).transform || 'translateX(0)';
  //Using native web animation
  dogBox.animate({
   transform: [lastTransform, `translateX(${left}px)`]
  }, {
   easing: 'linear',
   fill: 'forwards',
   duration: time
  });
  //Similar progress bar, omitted
 }
}

The time of the above animation is 250ms and the time of throttling is the same, so that the last animation is almost finished when the next trigger is triggered (in fact, it is a little slower). And each time the animation is triggered, the current translate position is obtained as the starting point of this animation, which can ensure the continuity of the animation.

In addition, the last 100% trigger will be lost if we use throttling, so we need to manually adjust onprogressdownload when it is finished, otherwise it will not be completed.

For the example of playing progress bar, you need to listen to the timeupdate event of video / audio element. This event is triggered about 250ms (measured) once, without throttling.

The effect is as follows:

 

We found that at the end of the figure, the total size has been displayed, that is, the download has been completed, but the dog is still a long way from the end. In our case, it seems that it is not so obvious, and it can not be seen if we do not look carefully. But if the download speed is very fast, the problem will be more obvious. In the example of playing progress bar, if the progress bar is very long, but the video is only played for 10 seconds, it should also be more obvious.

A simple solution is to assume that the download speed of the next 250ms remains the same and moves 250ms ahead of time each time. If this assumption is almost right in the example of playing video, because the download speed is relatively constant and uncontrollable, but we estimate and think it is the same in the same short time.

So we can record the last position and add another offset, as shown in the following code:

let diffX = (event.loaded - lastMB) / event.total * containerWidth;
//Add another offset to the original (and no more than the width of the container)
let left = Math.min(containerWidth, containerWidth * percentComplete + diffX);
lastMB = downloadedMB;

In this way, it is quite right. The effect is shown in the following figure:

 

This case is basically the end of this introduction. This example is relatively simple, but you may feel that the compatibility of Web animation is not good. Mainly in chrome compatibility is better, other mainstream browser’s new version has also begun to support. Other unsupported browsers can use Google’s official Polyfill, which is a bit larger. It is the same as CSS animation, but it can use js to control the start pause and so on, so it has the same GPU acceleration as CSS animation, and does not occupy JS threads and other advantages.

summary

The above is a JS code for downloading progress bar and playing progress bar introduced by Xiaobian. I hope it can help you. If you have any questions, please leave me a message, and Xiaobian will reply to you in time!