Как реализовать индикатор загрузки/воспроизведения

JavaScript анимация

Не много сложностей в технике, сколько сложно места сделать всю анимацию плавной. Основная проблема заключается в задержке анимации: когда в следующий раз происходит переход к определенной точке, вы использовали анимацию 250 мс для перехода. На этот раз время медленное, поэтому многие люди могут не анимироваться по этой причине или обеспокоены. запускается событие прогресса, индикатор выполнения обновляется напрямую, но мы можем попробовать.

Окончательный результат показан на следующем рисунке:

Анимация бега щенка - это анимация Лотти из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, чтобы сообщить общее количество байтов в текущем файле, как показано на следующем рисунке:

Как правило, это поле есть в CDN. После получения хода загрузки его можно использовать для преобразования ширины или положения.

2. Загрузка без анимации

Если мы не анимируем и не устанавливаем положение перевода напрямую, это выглядит так:

Код выглядит следующим образом:

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}%)`;

В нашем примере она будет особенно резкой, и ощущение одной карты и одной карты может быть лучше без собаки выше. Поэтому мы добавляем к нему анимацию трансформации.

3. Добавьте анимацию преобразования

Как сделать трансформирующую анимацию? Существует много методов: анимация jQuery, веб-анимация, requestAnimationFrame, анимация CSS в сочетании с управлением JS, другие сторонние библиотеки анимации и т. д. Я предпочитаю использовать нативную веб-анимацию.

Поскольку срабатывает событие прогресса, если вам не нужно запускать его так быстро, вам не нужно запускать его так быстро, поэтому дайте ему газ. Как показано в следующем коде:

// 最快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));

Можно конечно и без троттлинга добавить, это просто оптимизация.

Логика выполнения анимации преобразования обрабатывается в функции 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
    });
    // 进度条类似,省略
  }
}

Приведенное выше время анимации составляет 250 мс, а время регулирования постоянно, так что последняя анимация почти завершается, когда она запускается в следующий раз (на самом деле она немного медленнее). И каждый раз, когда запускается анимация, текущая позиция перевода получается в качестве начальной точки этой анимации, что может обеспечить непрерывность анимации.

Кроме того, поскольку мы используем троттлинг, последний 100% триггер, скорее всего, будет потерян, поэтому нам нужно вручную настроить onProgressDownload, когда он завершится, иначе не будет состояния завершения.

Если это пример воспроизведения индикатора выполнения, вам необходимо отслеживать событие timeupdate элемента видео/аудио.250ms(Фактическое измерение) Запуск один раз, дросселирование не требуется.

Результаты, как показано ниже:

Мы обнаружили, что загрузка завершена, когда итоговое число показывает общий размер, но собака все еще находится на некотором расстоянии от конечной точки.В нашем случае это не кажется таким очевидным, и трудно сказать, если вы не невнимательно. Но если скорость загрузки высокая, эта проблема будет более очевидной.В примере с воспроизведением индикатора выполнения, если индикатор выполнения очень длинный, но воспроизводится видео всего 10 секунд, то это должно быть более очевидным.

Простое решение состоит в том, чтобы предположить, что скорость загрузки следующих 250 мс одинакова, и каждый раз перемещаться на 250 мс вперед.Если это предположение почти верно в примере с воспроизведением видео, потому что скорость относительно однородна, скорость загрузки неконтролируемая, но в тот же короткий период времени мы оцениваем и думаем, что это то же самое.

Таким образом, мы можем записать последнюю позицию, а затем добавить смещение, как показано в следующем коде:

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

Это более правильно, и эффект показан на следующем рисунке:

Этот случай в основном является концом введения здесь.Этот пример относительно прост, но вы можете почувствовать, что совместимость с веб-анимацией не очень хороша. Основная причина в том, что совместимость Chrome лучше, и новые версии других популярных браузеров также начали поддерживать. Другие неподдерживаемые браузеры могут использовать официальный браузер Google.polyfillЭто большой бит. Это то же самое, что и анимация CSS, но вы можете использовать JS для управления запуском паузы, поэтому она имеет преимущество ускорения графического процессора и не использует преимущества потоков JS, таких как анимация CSS.