Похоже, requestAnimationFrame
это де-факто способ оживить вещи сейчас. По большей части это сработало довольно хорошо, но сейчас я пытаюсь сделать анимацию холста, и мне стало интересно: есть ли способ убедиться, что он работает с определенным fps? Я понимаю, что цель rAF - постоянно плавные анимации, и я могу рискнуть сделать свою анимацию прерывистой, но сейчас она, кажется, работает на совершенно разных скоростях довольно произвольно, и мне интересно, есть ли способ борьбы это как-то.
Я бы использовал, setInterval
но я хочу оптимизации, которые предлагает rAF (особенно автоматическая остановка, когда вкладка находится в фокусе).
На тот случай, если кто-то захочет взглянуть на мой код, это довольно сильно:
animateFlash: function() {
ctx_fg.clearRect(0,0,canvasWidth,canvasHeight);
ctx_fg.fillStyle = 'rgba(177,39,116,1)';
ctx_fg.strokeStyle = 'none';
ctx_fg.beginPath();
for(var i in nodes) {
nodes[i].drawFlash();
}
ctx_fg.fill();
ctx_fg.closePath();
var instance = this;
var rafID = requestAnimationFrame(function(){
instance.animateFlash();
})
var unfinishedNodes = nodes.filter(function(elem){
return elem.timer < timerMax;
});
if(unfinishedNodes.length === 0) {
console.log("done");
cancelAnimationFrame(rafID);
instance.animate();
}
}
Где Node.drawFlash () - это просто некоторый код, который определяет радиус на основе переменной счетчика, а затем рисует круг.
источник
requestAnimationFrame
(как подсказывает название) запрашивает кадр анимации только тогда, когда это необходимо. Допустим, вы показываете статический черный холст, вы должны получить 0 кадров в секунду, потому что новый кадр не требуется. Но если вы отображаете анимацию, которая требует 60 кадров в секунду, вы должны получить это тоже.rAF
позволяет просто «пропустить» ненужные кадры, а затем сохранить процессор.Ответы:
Как регулировать requestAnimationFrame для определенной частоты кадров
Демонстрация при 5 FPS: http://jsfiddle.net/m1erickson/CtsY3/
Этот метод работает, проверяя время, прошедшее с момента выполнения последнего цикла кадра.
Ваш код рисования выполняется только после истечения указанного интервала FPS.
В первой части кода устанавливаются некоторые переменные, используемые для вычисления прошедшего времени.
И этот код является фактическим циклом requestAnimationFrame, который рисует с указанным вами FPS.
источник
Обновление 2016/6
Проблема регулирования частоты кадров заключается в том, что экран имеет постоянную частоту обновления, обычно 60 кадров в секунду.
Если нам нужно 24 кадра в секунду, мы никогда не получим истинные 24 кадра в секунду на экране, мы можем рассчитать время как таковое, но не отобразить его, поскольку монитор может отображать только синхронизированные кадры со скоростью 15 кадров в секунду, 30 кадров в секунду или 60 кадров в секунду (некоторые мониторы также имеют 120 кадров в секунду) ).
Тем не менее, для целей синхронизации мы можем рассчитывать и обновлять, когда это возможно.
Вы можете построить всю логику для управления частотой кадров путем инкапсуляции вычислений и обратных вызовов в объект:
Затем добавьте контроллер и код конфигурации:
использование
Это становится очень просто - теперь все, что нам нужно сделать, это создать экземпляр, установив функцию обратного вызова и желаемую частоту кадров, вот так:
Затем запустите (это может быть поведение по умолчанию, если это необходимо):
Вот и все, вся логика обрабатывается внутри.
демонстрация
Старый ответ
Основная цель
requestAnimationFrame
- синхронизировать обновления с частотой обновления монитора. Это потребует от вас анимирования на частоте кадров монитора или ее факторе (т. Е. 60, 30, 15 кадров в секунду для типичной частоты обновления при 60 Гц).Если вам нужен более произвольный FPS, то нет смысла использовать rAF, поскольку частота кадров в любом случае никогда не будет соответствовать частоте обновления монитора (только кадр здесь и там), который просто не может дать вам плавную анимацию (как со всеми режимами кадровой синхронизации). ) и вы можете также использовать
setTimeout
илиsetInterval
вместо.Это также хорошо известная проблема в индустрии профессионального видео, когда вы хотите воспроизвести видео на другом FPS, чем на устройстве, на котором оно обновляется. Было использовано много методов, таких как смешивание кадров и сложная повторная синхронизация, воссоздание промежуточных кадров на основе векторов движения, но для холста эти методы недоступны, и результатом всегда будет прерывистое видео.
Причина, по которой мы ставим на
setTimeout
первое место (и почему некоторые местаrAF
занимают первое место при использовании полизаполнения), заключается в том, что это будет более точным, посколькуsetTimeout
будет ставить в очередь событие сразу после запуска цикла, чтобы независимо от того, сколько времени будет использовать оставшийся код (при условии, что он не превышает интервал тайм-аута) следующий вызов будет происходить с интервалом, который он представляет (для чистого rAF это несущественно, поскольку в любом случае rAF попытается перейти на следующий кадр).Также стоит отметить, что размещение его первым также может привести к тому, что вызовы будут накапливаться, как при
setInterval
.setInterval
может быть немного более точным для этого использования.И вы можете использовать
setInterval
вместо этого вне цикла, чтобы сделать то же самое.И чтобы остановить цикл:
Чтобы уменьшить частоту кадров, когда вкладка размыта, вы можете добавить такой фактор:
Таким образом, вы можете уменьшить FPS до 1/4 и т. Д.
источник
requestAnimationFrame
- это синхронизация операций DOM (чтение / запись), поэтому, если вы не используете его, это снизит производительность при доступе к DOM, поскольку операции не будут поставлены в очередь для одновременного выполнения и вынудят перерисовку макета без необходимости.Я предлагаю заключить ваш звонок
requestAnimationFrame
вsetTimeout
:Вам нужно звонить
requestAnimationFrame
изнутриsetTimeout
, а не наоборот, потому чтоrequestAnimationFrame
вы планируете запускать вашу функцию прямо перед следующей перерисовкой, и если вы отложите обновление дальше,setTimeout
вы пропустите это временное окно. Однако делать обратное - это разумно, поскольку вы просто ждете некоторое время, прежде чем сделать запрос.источник
Это все хорошие идеи в теории, пока вы не углубитесь. Проблема в том, что вы не можете ограничить RAF, не отменив синхронизацию, не победив его цель для существования. Таким образом , вы дайте ему поработать на полной скорости, и обновлять данные в отдельном цикле , или даже отдельный поток!
Да, я сказал это. Вы можете сделать многопоточный JavaScript в браузере!
Я знаю, что есть два метода, которые работают очень хорошо без рывков, используют гораздо меньше сока и создают меньше тепла. Точное человеческое масштабирование и эффективность машины - чистый результат.
Извинения, если это немного многословно, но здесь идет ...
Способ 1: обновить данные через setInterval и графику через RAF.
Используйте отдельный setInterval для обновления значений перемещения и поворота, физики, столкновений и т. Д. Сохраняйте эти значения в объекте для каждого анимированного элемента. Присвойте строку преобразования переменной в объекте каждый setInterval 'frame'. Храните эти объекты в массиве. Установите ваш интервал на желаемый fps в мс: ms = (1000 / fps). Это сохраняет постоянные часы, которые позволяют одинаковые кадры в секунду на любом устройстве, независимо от скорости RAF. Не назначайте преобразования для элементов здесь!
В цикле requestAnimationFrame итерируйте свой массив с циклом старой школы for - не используйте новые формы здесь, они медленные!
В вашей функции rafUpdate получите строку преобразования из вашего объекта js в массиве и идентификатор его элементов. У вас уже должны быть элементы «спрайта», прикрепленные к переменной или легкодоступные с помощью других средств, чтобы вы не теряли время на «получение» их в RAF. Хранить их в объекте, названном в честь их html-идентификаторов, довольно хорошо. Настройте эту часть еще до того, как она попадет в ваш SI или RAF.
Используйте RAF для обновления только ваших преобразований , используйте только 3D-преобразования (даже для 2d) и установите css "will-change: transform;" на элементы, которые будут меняться. Это обеспечивает максимально возможную синхронизацию ваших преобразований с собственной частотой обновления, запускает графический процессор и сообщает браузеру, где сосредоточиться больше всего.
Таким образом, вы должны иметь что-то вроде этого псевдокода ...
Это сохраняет ваши обновления объектов данных и строк преобразования синхронизированными с желаемой частотой кадров в SI, а фактические назначения преобразования в RAF синхронизированы с частотой обновления GPU. Таким образом, фактические графические обновления находятся только в RAF, но изменения в данных и построении строки преобразования находятся в SI, таким образом, нет никаких затей, а «время» течет с желаемой частотой кадров.
Поток:
Способ 2. Поместите СИ в веб-работника. Этот FAAAST и гладкий!
То же, что и в методе 1, но поместите SI в web-работника. Тогда он будет работать в совершенно отдельном потоке, оставляя страницу для работы только с RAF и пользовательским интерфейсом. Передайте массив спрайтов назад и вперед как «передаваемый объект». Это быстро. Клонирование или сериализация не требуют времени, но это не похоже на передачу по ссылке, поскольку ссылка с другой стороны уничтожается, поэтому вам нужно будет передать обе стороны на другую сторону и обновлять их только при наличии, сортировать как передавать записку туда и обратно со своей девушкой в старшей школе.
Только один может читать и писать одновременно. Это нормально, если они проверяют, не является ли оно неопределенным, чтобы избежать ошибки. RAF является БЫСТРЫМ и немедленно отбросит его, а затем пройдёт через кучу кадров GPU, просто проверяя, отправлено ли оно ещё. SI в web-работнике будет большую часть времени иметь массив спрайтов, обновлять данные о положении, движении и физике, а также создавать новую строку преобразования, а затем передавать ее обратно в RAF на странице.
Это самый быстрый способ, которым я знаю, для анимации элементов с помощью скрипта. Эти две функции будут работать как две отдельные программы в двух отдельных потоках, используя преимущества многоядерных процессоров так, как этого не делает один js-скрипт. Многопоточная анимация JavaScript.
И это будет происходить плавно без рывков, но с фактической заданной частотой кадров, с очень небольшим расхождением.
Результат:
Любой из этих двух способов гарантирует, что ваш скрипт будет работать с одинаковой скоростью на любом ПК, телефоне, планшете и т. Д. (Конечно, в пределах возможностей устройства и браузера).
источник
visibilitychange
событие.Как легко дросселировать до определенного FPS:
Источник: Подробное объяснение циклов и времени игры в JavaScript от Исаака Сукина
источник
Пропуск requestAnimationFrame вызывает не сглаженную (желаемую) анимацию при пользовательских кадрах в секунду.
Оригинальный код от @tavnab.
источник
источник
Я всегда делаю это очень простым способом, не путаясь с метками времени:
источник
Вот хорошее объяснение, которое я нашел: CreativeJS.com , чтобы обернуть вызов setTimeou) внутри функции, переданной в requestAnimationFrame. Моя проблема с «простым» requestInimationFrame будет: «Что если я хочу, чтобы он анимировался только три раза в секунду?» Даже с requestAnimationFrame (в отличие от setTimeout) он по- прежнему тратит (некоторое) количество «энергии» (что означает, что код браузера что-то делает, и, возможно, замедляет работу системы) 60 или 120 или столько раз в секунду, как в противоположность только два или три раза в секунду (как вы могли бы хотеть).
Большую часть времени я запускаю свои браузеры с отключенным JavaScript именно по этой причине. Но я использую Yosemite 10.10.3, и я думаю, что с ним есть какая-то проблема с таймером - по крайней мере, на моей старой системе (относительно старой - имеется в виду 2011).
источник