[Сегодня вы стали более осведомлены] Волшебный интерфейс API анимации requestAnimationFrame

внешний интерфейс JavaScript
[Сегодня вы стали более осведомлены] Волшебный интерфейс API анимации requestAnimationFrame

предисловие

Сначала последний вопрос на собеседовании: выполнение внешнего интерфейса необходимо для работы с анимацией, так как же можно реализовать анимацию?

  • JavaScript:setTimeoutиsetInterval
  • Css3:transitionиanimation
  • Html:canvasиSVG
  • requestAnimationFrame API
  • ...
  • Знаете ли вы какие-либо другие способы достижения анимации?

requestAnimationFrame API

что

Эта статья в основном изучаетrequestAnimationFrame API, как следует из названия,запрос кадра анимации, также называемыйрамка петля.

размещено в текстеjsВесь код — настоящий код, просто скопируйте и запустите.

Как играть

Когда мы что-то узнаем, мы должны сначала посмотреть на этоMDNкак сказать.

window.requestAnimationFrame()  

Сообщите браузеру, что вы хотите выполнить анимацию, и попросите браузер вызвать указанную функцию обратного вызова, чтобы обновить анимацию перед следующей перерисовкой. Этот метод должен передать функцию обратного вызова в качестве параметра, функция обратного вызова будет выполнена до следующей перерисовки браузера.

Браузерная перерисовка и переформатированиеПозже будет отдельная статья. Неважно, если вы сейчас не понимаете, это не влияет на наше обучение.requestAnimationFrame API.

Давайте сначала познакомимся с ним, согласно документации. Мы передаем ему функцию обратного вызоваtest.

//html代码全文通用,所以只在此贴出一次
<body>
  <h1>requestAnimationFrame API</h1>
  <button id='begin' class="begin">开始</button>
  <button id='end' class="end">停止</button>
</body>

//js
(() => {
  function test() {
    console.log('🚀🚀hello ~ requestAnimationFrame');
  }
  requestAnimationFrame(test)
})()

Как видите, консоль успешно вывела один разlog.

2.gif

Но он выполняется только один раз, как его оживить? не волнуйся, посмотри еще разMDNКак сказать.

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

Получается снова вызывается в callback-функцииrequestAnimationFrameТолько после этого измените код и повторите попытку.

(() => {
  let n = 0
  function test() {
    n++
    console.log(`🚀🚀hello ~ requestAnimationFrame ${n}`);
    requestAnimationFrame(test)
  }
  requestAnimationFrame(test)
})()

Проверьте это, это действительно работает.3.gif

частота выполнения

В это время некоторые друзья собираются спросить, мне не нравитсяsetTimeoutиsetIntervalУстановите время так, почему оно выполняется с интервалами?

Посмотрите, что написано в документации.

Количество выполнений функции обратного вызова обычно в секунду60раз, но в большинстве случаевW3CВ рекомендуемых браузерах количество выполнений функции обратного вызова обычно равноВремя обновления экрана браузерасовпадение.

В этот раз я знаю, получается, что ему вообще не нужно вручную задавать интервал выполнения, а согласноВремя обновления экрана браузераАвтоматически настраивается, то есть столько раз, сколько обновляется экран браузера, столько же раз и выполняется. Я просто хочу что-то сказать, когда увижу этосильно сломан.

Так что жеВремя обновления экрана браузераШерстяная ткань?

Частота обновления экрана (количество раз):Количество раз, которое изображение появляется на экране в секунду. Обычные ноутбуки имеют частоту 60 Гц.

параметр обратного вызова

Старые правила, сначала посмотрите документацию.

Функция обратного вызова будет передана вDOMHighResTimeStampпараметр,DOMHighResTimeStampУказывает, что в настоящее времяrequestAnimationFrame()Время срабатывания отсортированной функции обратного вызова.

Измените код, чтобы увидеть этот параметр.

(() => {
  function test(timestamp) {
    console.log(`🚀🚀hello ~ requestAnimationFrame ${timestamp}`);
    requestAnimationFrame(test)
  }
  requestAnimationFrame(test)
})()

4.gif

Хороший парень, с точностью до трех знаков после запятой. Так что же означает этот текст в документе?

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

Модифицируем код, выполняем два одновременноrequestAnimationFrameПосмотри.

(() => {
  function test1(timestamp) {
    console.log(`🚀🚀hello ~ requestAnimationFrame1 ${timestamp}`);
    requestAnimationFrame(test1)
  }
  function test2(timestamp) {
    console.log(`🚀🚀hello ~ requestAnimationFrame2 ${timestamp}`);
    requestAnimationFrame(test2)
  }
  requestAnimationFrame(test1)
  requestAnimationFrame(test2)

})()

Видно, что дваrequestAnimationFrameВывод метки времени в консоли такой же. То есть, когда браузер обновится один раз, выполните всеrequestAnimationFrame, и их параметры обратного вызова точно такие же.

image.png

Самосохранение браузера

Чтобы улучшить производительность и время работы от батареи, в большинстве браузеров приrequestAnimationFrame()работать в фоновых вкладках или скрыто<iframe>миля,requestAnimationFrame()Вызовы приостанавливаются для повышения производительности и увеличения срока службы батареи.

Это удивительно. Если вы не просматривали страницу в это время и не закрыли ее, тоrequestAnimationFrame() Работает здесь все время, потребляет много производительности. Другие разработчики уже давно об этом подумали, то есть пока вы меня не смотрите, то мне будет лень(是不是跟你们上班一样,领导没盯着你,你就刷掘金摸鱼).

Проверьте предыдущий код. Для удобства я добавилbutton.

  const beginBtn = document.querySelector("#begin")
  beginBtn.addEventListener("click", () => {
    requestAnimationFrame(test)
  })
  
  let n = 0
  function test() {
    n++
    console.log(`🚀🚀hello ~ requestAnimationFrame ${n}`);
    requestAnimationFrame(test)
  }

Как вы можете видеть, когда консоль печатает наполовину (высоту), я вырезаю другую страницу, а когда через несколько секунд я сокращаю, она все еще выводится в половинном положении.

动画.gif

возвращаемое значение

Один longЦелое число, идентификатор запроса, является уникальным идентификатором в списке обратного вызова. является ненулевым значением и не имеет никакого другого значения.

Когда следующий код щелкает для запуска, выводrequestAnimationFrameВозвращаемое значение. Видно, что при каждом его выполнении значение будет+1

(() => {
  const beginBtn = document.querySelector("#begin")
  
  let myRef;
  
  beginBtn.addEventListener("click", () => {
    myRef = requestAnimationFrame(test)
  })
  
  function test() {
    myRef = requestAnimationFrame(test)
    console.log('🚀🚀~ myRef:', myRef);
  }
})()

44.gif

прекратить выполнение

Вы можете передать это значениеwindow.cancelAnimationFrame()для отмены функции обратного вызова.

тогда, если я хочу прекратить при определенных условияхrequestAnimationFrameЧто делать, чиновник тоже дал ответ, т.е.cancelAnimationFrame API. просто поставьrequestAnimationFrameВозвращаемое значение передается в качестве параметраcancelAnimationFrameВот и все.

(() => {
  const beginBtn = document.querySelector("#begin")

  const endBtn = document.querySelector("#end")

  let myRef;

  beginBtn.addEventListener("click", () => {
    myRef = requestAnimationFrame(test)
  })

  endBtn.addEventListener("click", () => {
    cancelAnimationFrame(myRef)
  })

  function test() {
    myRef = requestAnimationFrame(test)
    console.log('🚀🚀~ myRef:', myRef);
  }
})()

Как видите, когда я нажимаю «Пуск», консоль продолжает выводить контент, а когда я нажимаю «Стоп», консоль прекращает вывод.

动画3.gif

На самом деле это не нужноAPIОн также может достичь цели прекращения выполнения, например простогоif语句.

(() => {
  function test(timestamp) {
    console.log(`🚀🚀hello ~ requestAnimationFrame ${timestamp}`);
    if (timestamp < 500) {
      requestAnimationFrame(test)
    }
  }
  requestAnimationFrame(test)
})()

Видно, что когдаtimestampОн останавливается, когда превышает 500. Возможностей, конечно, больше, а пользоваться умными головами - дело друзей.

445.gif

Советы

Таким образом, мы можем передать временной интервал между каждыми двумя выполнениями для внешнего использования. Получив его извне, вы можете делать вещи, например, интервал времени накапливается до1000просто делай что угодно~

(() => {
  let startTime = Date.now();

  function handleTicker() {
    foo(Date.now() - startTime);
    startTime = Date.now();
    requestAnimationFrame(handleTicker);
  }

  requestAnimationFrame(handleTicker);

  let t = 0
  function foo(timeInterval) {
    t += timeInterval
    console.log('🚀🚀~ t:', t);
    if (t > 1000) {
      console.log('🚀🚀~ 搞事情');
      t = 0
    }
  }
})()

Видно, что когдаtСовокупный более1000Когда я что-то делал, то сбрасывалt, и так далее.

image.png

анимация

В заголовке написано, что это анимация артефактаAPIНу, если ты не будешь делать вид, что выкладываешь анимацию, тебе скажут«Автор сенсационных заголовков». Поэтому я решил реализовать простойdemoБар.

<style>
  #box {
    width: 0px;
    height: 50px;
    background-color: blue;
  }
</style>
<body>
  <h1>requestAnimationFrame API</h1>
  <button id='begin' class="begin">开始</button>
  <button id='end' class="end">停止</button>
  <div id='box'></div>
</body>
(() => {
  const beginBtn = document.querySelector("#begin")
  const endBtn = document.querySelector("#end")
  const box = document.querySelector("#box")
  let myRef;

  beginBtn.addEventListener("click", () => {
    myRef = requestAnimationFrame(test)
  })

  endBtn.addEventListener("click", () => {
    cancelAnimationFrame(myRef)
  })

  function test() {
    box.style.width = `${myRef}%`
    myRef = requestAnimationFrame(test)
  }
})()

1.gif

совместимость

image.png

Совместимость показана на рисунке, а ссылка размещена здесь.Если вам нужно проверить детали, нажмите, чтобы найти их самостоятельно.

Can I Use

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

Глубокое понимание requestAnimationFrame

тестовый тест

setTimeout && setInterval

setTimeoutиsetIntervalПроблема в том, что они недостаточно точны. Их внутренний рабочий механизм определяетпараметр интервала временина самом деле просто указывает, что код анимации добавляется вОчередь потоков пользовательского интерфейса браузерав ожидании времени выполнения. Если в начало очереди добавлены другие задачи, код анимации будет ждать предыдущей задачи.После выполнения задачиВыполните еще раз, и если временной интервал будет слишком коротким (менее 16,7 мс), это приведет к потере кадров, поэтому анимация может не выполняться в соответствии с предустановкой, что снижает удобство работы пользователя.

requestAnimationFrameиспользоватьвременной интервал браузера, чтобы поддерживать наилучшую эффективность рисования, это не приведет к чрезмерному рисованию и потреблению производительности, потому что время интервала слишком короткое; это не приведет к зависанию анимации и не будет плавным, потому что время интервала слишком велико, так что различные веб-страницы анимационные эффекты могут иметь одинОбъединитьМеханизм обновления экономит системные ресурсы, повышает производительность системы и улучшает визуальные эффекты.

CSS3 анимация

CSS3изtransitionиanimationМожно сказать, что он очень силен при совместном использовании, но есть также места, до которых нельзя добраться щупальцами, например,scrollTop,Кроме тогоCSS3Кривые Безье, поддерживаемые анимацией, также ограничены.

Так,CSS3Используйте то, что вы не можете сделатьrequestAnimationFrameчтобы решить это.

Ссылаться на

MDN window.requestAnimationFrame

Глубокое понимание requestAnimationFrame

Can I Use