2021.6 Обновление
Эта статья была записана ранее, и часть ее содержания не до конца понятна и даже может вызвать недопонимание. Эту статью можно использовать как справочную, но некоторые детали должны быть оценены читателем.
Когда будет время, я переучусь и организую.
С развитием технологий и оборудования способность пользовательского терминала выражать анимацию становится все сильнее и сильнее, и все больше сцен начинают активно использовать анимацию. В веб-приложениях есть много способов добиться анимационных эффектов, в JavaScript можно использовать таймеры.setTimeout
Для достижения css3 может использоватьtransition
иanimation
Для достижения в html5canvas
также может быть достигнуто. Кроме того, html5 также предоставляет API, предназначенный для запроса анимации, а именноrequestAnimationFrame
Содержание этой статьи не является оригинальным, но изучено и понято при сборе и транспортировке очков знаний.Вы также можете собирать и транспортировать эту статью!
1. Что такое
-
Недавно добавленный API HTML5, похожий на
setTimeout
таймер -
window
метод объекта,window.requestAnimationFrame
partial interface Window { long requestAnimationFrame(FrameRequestCallback callback); void cancelAnimationFrame(long handle); };
-
Браузер (поэтому его можно использовать только в браузере) предоставляет API специально для анимации, так что анимация DOM, анимация Canvas, анимация SVG, анимация WebGL и т. д. имеют унифицированный механизм обновления.
2. что делать
- Скорость перерисовки браузера обычно соответствует частоте обновления монитора. Большинство браузеров принимают рекомендации спецификации W3C, и стандартная частота кадров страницы рендеринга браузера также составляет 60 кадров в секунду (кадров в секунду).
-
Перерисовывать веб-страницу кадр за кадром. Этот метод сообщает браузеру, что он хочет выполнить анимацию, и просит браузер вызвать функцию обратного вызова для обновления анимации перед следующей перерисовкой.
-
Система сама решает, когда будет выполняться функция обратного вызова., браузер автоматически оптимизирует вызов метода во время выполнения.
-
Монитор имеет фиксированную частоту обновления (60 Гц или 75 Гц), что означает, что он может перерисовывать не более 60 или 75 раз в секунду.
requestAnimationFrame
Основная идеяСинхронизируйте частоту перерисовки страницы с этой частотой обновления.Например, если частота обновления экрана монитора составляет 60 Гц, используйте
requestAnimationFrame
API, то функция обратного вызова вызывается каждый1000ms / 60 ≈ 16.7ms
Выполнить один раз; если частота обновления экрана дисплея составляет 75 Гц, функция обратного вызова будет выполняться каждый раз1000ms / 75 ≈ 13.3ms
Выполнить один раз. -
пройти через
requestAnimationFrame
Временной интервал перерисовки или перекомпоновки страницы, вызванный вызовом функции обратного вызова, совпадает с интервалом обновления дисплея. такrequestAnimationFrame
не нужно быть похожимsetTimeout
Интервал передается таким образом, вместо этого браузер получает его из системы и использует частоту обновления экрана.Например, в анимации ширина увеличивается с 0 пикселей плюс один до 100 пикселей. В случае отсутствия эффекта плавности браузер перерисовывается один раз, а ширина увеличивается на 1.
-
3. Использование
Список функций обратного вызова запроса кадра анимации: Каждый документ имеет список функций обратного вызова запроса кадра анимации, который можно рассматривать как список
<handle, callback>
Коллекция кортежей.
handle
целое число, однозначно идентифицирующее позицию кортежа в списке,cancelAnimationFrame()
Через него можно остановить анимациюcallback
— это функция, которая не возвращает никакого значения и принимает значение времени (значение времени, переданное браузером как количество миллисекунд, прошедших с 1 января 1970 года).- Сначала список пуст.
- Когда страница свернута или переключена на фоновую вкладку, страница становится невидимой, и браузер запускает
visibilitychange
событие и установитьdocument.hidden
собственностьtrue
- Когда страница переключается в состояние отображения, она становится видимой и вызывает
visibilitychange
события, настройкиdocument.hidden
собственностьfalse
-
Вызвать действие. и
setTimeout
Аналогично, но не требует установки интервала, принимает функцию обратного вызова в качестве параметра и возвращает целое число больше 0handle = requestAnimationFrame(callback);
- параметр
callback
, — это функция обратного вызова, которая вызывается при следующей перерисовке анимации. Функция обратного вызова получает единственный параметр, который является высокоточной меткой времени (performance.now()
), который относится к текущему времени, когда срабатывает функция обратного вызова (не нужно передавать вручную) - Возвращаемое значение представляет собой
long
ненулевое целое число типа,requestAnimationFrame
Уникальный идентификатор в списке callback-функций, указывающий на номер таймера и не имеющий другого значения
- параметр
-
Отменить операцию
cancelAnimationFrame(handle);
- параметр - это вызов
requestAnimationFrame
возвращаемое значение, когда - Операция отмены не имеет возвращаемого значения
- параметр - это вызов
-
процесс выполнения браузера
-
Судите первым
document.hidden
Является собственностьюtrue
(независимо от того, видна страница или нет), следующие шаги будут выполняться только тогда, когда страница видна. -
Браузер очищает функцию анимации предыдущего раунда
-
requestAnimationFrame
Добавьте функцию обратного вызова в конец списка функций обратного вызова запроса кадра анимации.при исполнении
requestAnimationFrame(callback)
Когда функция обратного вызова не вызывается немедленно, она просто ставится в очередь. Каждая функция обратного вызова имеет логический идентификатор.cancelled
, начальное значение идентификатора равноfalse
, и не виден внешнему миру. -
Когда браузер снова выполняет функцию обратного вызова в списке, он оценивает обратный вызов каждого кортежа.
cancelled
, еслиfalse
, затем выполните обратный вызовкогда страница видна иСписок функций обратного вызова запроса кадра анимацииНе пусто, браузер будет периодически добавлять эти функции обратного вызова в очередь потока пользовательского интерфейса браузера.
-
Блог Паркyyc Юань ЧаостатьяГлубокое понимание requestAnimationFrameПредоставляется псевдокод, иллюстрирующий шаги по выполнению задачи «образец всех анимаций».
var list = {}; var browsingContexts = 浏览器顶级上下文及其下属的浏览器上下文; for (var browsingContext in browsingContexts) { /* !将时间值从 DOMTimeStamp 更改为 DOMHighResTimeStamp 是 W3C 针对基于脚本动画计时控制规范的最新编辑草案中的最新更改, * 并且某些供应商仍将其作为 DOMTimeStamp 实现。 * 较早版本的 W3C 规范使用 DOMTimeStamp,允许你将 Date.now 用于当前时间。 * 如上所述,某些浏览器供应商可能仍实现 DOMTimeStamp 参数,或者尚未实现 window.performance.now 计时函数。 * 因此需要用户进行polyfill */ var time = DOMHighResTimeStamp //从页面导航开始时测量的高精确度时间。DOMHighResTimeStamp 以毫秒为单位,精确到千分之一毫秒。此时间值不直接与 Date.now() 进行比较,后者测量自 1970 年 1 月 1 日至今以毫秒为单位的时间。如果你希望将 time 参数与当前时间进行比较,请使用当前时间的 window.performance.now。 var d = browsingContext 的 active document; //即当前浏览器上下文中的Document节点 //如果该active document可见 if (d.hidden !== true) { //拷贝 active document 的动画帧请求回调函数列表到 list 中,并清空该列表 var doclist = d的动画帧请求回调函数列表 doclist.appendTo(list); clear(doclist); } //遍历动画帧请求回调函数列表的元组中的回调函数 for (var callback in list) { if (callback.cancelled !== true) { try { //每个 browsingContext 都有一个对应的 WindowProxy 对象,WindowProxy 对象会将 callback 指向 active document 关联的 window 对象。 //传入时间值time callback.call(window, time); } //忽略异常 catch (e) { } } } }
-
при звонке
cancelAnimationFrame(handle)
, браузер установит функцию обратного вызова, на которую указывает дескрипторcancelled
заtrue
(независимо от того, находится ли функция обратного вызова в списке функций обратного вызова запроса кадра анимации). Если дескриптор не указывает ни на какую функцию обратного вызова, ничего не происходит.
-
-
рекурсивный вызов. Чтобы добиться полной анимации, функция обратного вызова должна вызываться рекурсивно в функции обратного вызова.
let count = 0; let rafId = null; /** * 回调函数 * @param time requestAnimationFrame 调用该函数时,自动传入的一个时间 */ function requestAnimation(time) { console.log(time); // 动画没有执行完,则递归渲染 if (count < 50) { count++; // 渲染下一帧 rafId = requestAnimationFrame(requestAnimation); } } // 渲染第一帧 requestAnimationFrame(requestAnimation);
-
Если вызывается несколько раз до выполнения функции обратного вызова или список функций обратного вызова запроса кадра анимации документа очищается.
requestAnimationFrame
Вызовите одну и ту же функцию обратного вызова, тогда в списке будет несколько кортежей, указывающих на функцию обратного вызова (их дескрипторы разные, ноcallback
являются функцией обратного вызова), задача «Получить всю анимацию» будет выполнять функцию обратного вызова несколько раз. (аналоговый таймерsetTimeout
)function counter() { let count = 0; function animate(time) { if (count < 50) { count++; console.log(count); requestAnimationFrame(animate); } } requestAnimationFrame(animate); } btn.addEventListener("click", counter, false);
-
Нажмите кнопку несколько раз, и вы обнаружите, что печатаются несколько значений последовательности (на рисунке ниже срабатывают три последовательных триггера и печатаются три последовательности)
-
Если он воздействует на анимацию, анимация будет видоизменяться.
-
4. Совместимость
- международная практикаcaniuse
- MDN(window.requestAnimationFrame)
- Современные версии браузеров включают в себя базовую поддержку мобильных устройств, и неизбежно использование исходной версии, поэтому ее все равно необходимо скачать.изящная деградацияиметь дело с
источник:Polyfill for requestAnimationFrame/cancelAnimationFrame
Выполните следующий код при первой загрузке браузера.
// 使用 Date.now 获取时间戳性能比使用 new Date().getTime 更高效
if (!Date.now)
Date.now = function() {
return new Date().getTime();
};
(function() {
"use strict";
var vendors = ["webkit", "moz"];
for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
var vp = vendors[i];
window.requestAnimationFrame = window[vp + "RequestAnimationFrame"];
window.cancelAnimationFrame =
window[vp + "CancelAnimationFrame"] ||
window[vp + "CancelRequestAnimationFrame"];
}
// 上面方法都不支持的情况,以及IOS6的设备
// 使用 setTimeout 模拟实现
if (
/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) ||
!window.requestAnimationFrame ||
!window.cancelAnimationFrame
) {
var lastTime = 0;
// 和通过时间戳实现节流功能的函数相似
window.requestAnimationFrame = function(callback) {
var now = Date.now();
var nextTime = Math.max(lastTime + 16, now);
// 实际上第1帧是不准确的,首次nextTime - now = 0
return setTimeout(function() {
callback((lastTime = nextTime));
}, nextTime - now);
};
window.cancelAnimationFrame = clearTimeout;
}
})();
5. Преимущества
requestAnimationFrame
Системный временной интервал используется для обеспечения наилучшей эффективности рисования. Это не приведет к перерисовке и увеличению накладных расходов из-за слишком короткого интервала времени, а также не остановит анимацию из-за слишком большого интервала времени.
От реализации функций и методов использования,requestAnimationFrame
с таймеромsetTimeout
похожи, поэтому преимущества одинаковыsetTimeout
по сравнению с полученной анимацией.
а. Повышение производительности и предотвращение потери кадров
- Поток пользовательского интерфейса браузера: браузеры используют один и тот же поток для выполнения JavaScript и обновления пользовательского интерфейса (включая перерисовку и перекомпоновку), называемый «поток пользовательского интерфейса браузера».
- Работа потока пользовательского интерфейса браузера основана на простой системе очередей, где задачи хранятся в очереди до тех пор, пока процесс не станет бездействующим. После освобождения следующая задача в очереди повторно загружается и запускается. Эти задачи либо запускают код JavaScript, либо выполняют обновления пользовательского интерфейса.
-
пройти через
setTimeout
реализовать анимацию-
setTimeout
Установив интервал времени для непрерывного изменения изображения для достижения эффекта анимации. Этот метод может вызвать заикание и дрожание на некоторых недорогих машинах. Как правило, у этого явления есть две причины:-
setTimeout
Время выполнения не является детерминированным.В JavaScript,
setTimeout
Задача помещается в асинхронную очередь, и только после того, как задача в основном потоке будет выполнена, он проверит, нужно ли начинать выполнение задачи очереди. так,setTimeout
Фактическое время выполнения, как правило, позже установленного времени.. Этот операционный механизм определяет, что параметр временного интервала на самом деле просто указывает время для добавления кода анимации в [очередь потоков пользовательского интерфейса браузера] для ожидания выполнения. Если в начало очереди были добавлены другие задачи, код анимации будет выполнен после завершения предыдущих задач.let startTime = performance.now(); setTimeout(() => { let endTime = performance.now(); console.log(endTime - startTime); }, 50); /* 一个非常耗时的任务 */ for (let i = 0; i < 20000; i++) { console.log(0); }
-
На частоту обновления влияет разрешение экрана и размер экрана.Частота обновления экрана на разных устройствах может быть разной.
setTimeout
Можно установить только фиксированный интервал времени, который может отличаться от интервала обновления экрана.
-
-
Обе вышеуказанные ситуации приведут к
setTimeout
Темп выполнения не соответствует темпу обновления экрана, тем самым вызываявыпадающая рамкаФеномен.-
setTimeout
Выполнение изменяет только свойства изображения в памяти, и это изменение не должно обновляться на экране до следующей перерисовки браузера.. Если это не соответствует скорости обновления экрана, это может привести к тому, что некоторые кадры в середине будут пропущены, а изображение следующего кадра будет обновлено напрямую.Если вы используете таймер, чтобы установить интервал 10 мс для выполнения кадра, а интервал обновления браузера составляет 16,6 мс (т.е. 60 кадров в секунду)
Из рисунка видно, что при 20 мс
setTimeout
Вызов функции обратного вызова изменяет атрибуты изображения в памяти, но следующее обновление браузера происходит через 33,2 мс, поэтому изображение, измененное через 20 мс, не обновляется на экране. И в 30 мс,setTimeout
Вызовите функцию обратного вызова еще раз и измените свойства изображения в памяти, после чего браузер обновится, обновленное состояние 20 мс перезапишется изображением 30 мс, а на экране появится изображение 30 мс, поэтому кадр 20 мс потерян. Чем больше кадров теряется, тем экран зависает.
-
-
-
использовать
requestAnimationFrame
Выполнение анимации, самое большое преимуществоМожет гарантировать, что функция обратного вызова выполняется только один раз в каждом интервале обновления экрана., чтобы не было пропущенных кадров и анимация не зависала
Б. Экономьте ресурсы, экономьте энергию
-
использовать
setTimeout
Реализована анимация, таймер при скрытии или сворачивании страницыsetTimeout
Задача анимации по-прежнему выполняется в фоновом режиме, и обновлять анимацию в это время совершенно бессмысленно (на самом деле браузер FireFox/Chrome оптимизирует таймер: при бездействии страницы, если временной интервал меньше 1000 мс, остановить таймер иrequestAnimationFrame
Поведение аналогичное. Если временной интервал >= 1000 мс, таймер все еще выполняется в фоновом режиме)// 在浏览器开发者工具的Console页执行下面代码。 // 当开始输出count后,切换浏览器tab页,再切换回来,可以发现打印的值没有停止,甚至可能已经执行完了 let count = 0; let timer = setInterval(() => { if (count < 20) { count++; console.log(count); } else { clearInterval(timer); timer = null; } }, 2000);
-
использовать
requestAnimationFrame
, когда страница находится в неактивном состоянии, задача обновления экрана страницы будет приостановлена системой, потому чтоrequestAnimationFrame
Продолжает выполняться синхронно с обновлением экрана, поэтому оно также будет приостановлено. Когда страница активирована, анимация продолжается с того места, где она была остановлена, что снижает нагрузку на ЦП.// 在浏览器开发者工具的Console页执行下面代码。 // 当开始输出count后,切换浏览器tab页,再切换回来,可以发现打印的值从离开前的值继续输出 let count = 0; function requestAnimation() { if (count < 500) { count++; console.log(count); requestAnimationFrame(requestAnimation); } } requestAnimationFrame(requestAnimation);
в. Функция регулирования
- Это бессмысленно, когда функция выполняется несколько раз в течение интервала обновления, потому что дисплей обновляется каждые 16,7 мс, и многократные отрисовки не будут отображаться на экране.
- Во время высокочастотных событий (
resize
,scroll
д.), использоватьrequestAnimationFrame
Это может предотвратить выполнение нескольких функций в течение интервала обновления, что обеспечивает беглость и экономит накладные расходы на выполнение функции. - Может использоваться напрямую в некоторых случаях
requestAnimationFrame
Замените функцию Throttle, которая ограничивает частоту выполнения callback-функции.
6. Применение
-
Простая анимация индикатора выполнения
function loadingBar(ele) { // 使用闭包保存定时器的编号 let handle; return () => { // 每次触发将进度清空 ele.style.width = "0"; // 开始动画前清除上一次的动画定时器 // 否则会开启多个定时器 cancelAnimationFrame(handle); // 回调函数 let _progress = () => { let eleWidth = parseInt(ele.style.width); if (eleWidth < 200) { ele.style.width = `${eleWidth + 5}px`; handle = requestAnimationFrame(_progress); } else { cancelAnimationFrame(handle); } }; handle = requestAnimationFrame(_progress); }; }
-
Добавьте эффект смягчения для реализации блока элементов в соответствии с кривой Безье третьего порядка.
ease-in-out
Движение параметра эффекта смягчения.Как использовать Javascript для реализации эффектов плавности
Ослабить анимацию: определяет скорость, с которой выполняется эффект анимации, чтобы сделать его более реалистичным.
/**
* @param {HTMLElement} ele 元素节点
* @param {number} change 改变量
* @param {number} duration 动画持续时长
*/
function moveBox(ele, change, duration) {
// 使用闭包保存定时器标识
let handle;
// 返回动画函数
return () => {
// 开始时间
let startTime = performance.now();
// 防止启动多个定时器
cancelAnimationFrame(handle);
// 回调函数
function _animation() {
// 这一帧开始的时间
let current = performance.now();
let eleTop = ele.offsetLeft;
// 这一帧内元素移动的距离
let left = change * easeInOutCubic((current - startTime) / duration);
ele.style.left = `${~~left}px`;
// 判断动画是否执行完
if ((current - startTime) / duration < 1) {
handle = requestAnimationFrame(_animation);
} else {
cancelAnimationFrame(handle);
}
}
// 第一帧开始
handle = requestAnimationFrame(_animation);
};
}
/**
* 三阶贝塞尔曲线ease-in-out
* @param {number} k
*/
function easeInOutCubic(k) {
return (k *= 2) < 1 ? 0.5 * k * k * k : 0.5 * ((k -= 2) * k * k + 2);
}
7. Связанные
Содержание этой статьи не является оригинальным, но изучено и понято при сборе и транспортировке очков знаний.Вы также можете собирать и транспортировать эту статью!
- developer.Mozilla.org/this-cn/docs/…
- руб. news.com/#search=passionate…
- у-у-у-у. palms.com/WordPress/2…
- JavaScript.Ruan Yifeng.com/HTML API/Энтузиазм…
- блог woo woo woo.cn на.com/smallmatch…
- nuggets.capable/post/684490…
- Блог Woohoo.cn на.com/super-personality/afraid/3…
- woohoo.soft, почему.com/article-720…
- easings.net/zh-cn#
- zhuanlan.zhihu.com/p/25676357
- Блог Wooooooo.cn на.com/one pixel/afraid/…