У меня есть куча длительных CSS-анимаций на странице. Я хочу приостановить их, когда пользователь переключается на другую вкладку, и возобновить их, когда пользователь снова вернется на исходную вкладку. Для простоты я пока не стремлюсь к кроссбраузерному решению; заставить его работать в Chrome должно быть достаточно.
document.addEventListener("visibilitychange", function() {
if (document.hidden) {
document.querySelector("#test").classList.add("paused");
} else {
document.querySelector("#test").classList.remove("paused");
}
});div#test {
width: 50px;
height: 50px;
background-color: red;
position: absolute;
left: 10vw;
animation-name: move;
animation-duration: 5s;
animation-fill-mode: forwards;
animation-timing-function: linear;
}
@keyframes move {
to {
left: 90vw
}
}
.paused {
animation-play-state: paused !important;
-webkit-animation-play-state: paused !important;
-moz-animation-play-state: paused !important;
}<div id = "test"></div>Приведенный выше код перемещает красный прямоугольник по горизонтали. Для завершения анимации прямоугольнику требуется 5 секунд.
Эта проблема: после запуска анимации перейти на другую вкладку браузера; через некоторое время (более 5 секунд) вернитесь к первой вкладке.
Ожидаемый результат: прямоугольник продолжает свой путь от точки, где он остановился.
Фактический результат: в большинстве случаев прямоугольник появляется в конечном пункте назначения и останавливается. Иногда это работает как положено. Видео демонстрирует проблему.
Я играл с разными значениями для animation-fill-mode и animation-timing-function, но результат всегда был одинаковым. Как и rv7 указал, совместное использование примеров в CodePen, JSFiddle, JSBin и JS-инструменте stackoverflow влияет на результаты, поэтому лучше тестировать непосредственно на статической странице на локальном HTTP-сервере (или используя ссылки ниже).
Для удобства я развернул код выше для Heroku. Приложение представляет собой статический HTTP-сервер nodejs, который работает по бесплатной подписке, поэтому первая загрузка страницы может занять до 5 минут. Результаты тестирования на этой странице:
FAIL Chrome 64.0.3282.167 (Official Build) (64-bit) Linux Debian Stretch (PC)
FAIL Chrome 70.0.3538.67 (Official Build) (64-bit) Windows 10 (PC)
FAIL Chrome 70.0.3538.77 (Official Build) (32-bit) Windows 7 (PC)
FAIL Chrome 70.0.3538.77 (Official Build) (64-bit) OSX 10.13.5 (Mac mini)
FAIL FF Quantum 60.2.2esr (64-bit) Linux Debian Stretch (PC)
OK Safari 11.1.1 (13605.2.8) (Mac mini)
API видимости страницы может быть заменен оконными событиями focus и blur следующим образом:
window.addEventListener("focus", function() {
document.querySelector("#test").classList.remove("paused");
});
window.addEventListener("blur", function() {
document.querySelector("#test").classList.add("paused");
});
Однако эта замена не эквивалентна. Если страница содержит IFRAME, взаимодействие с их содержимым вызовет события focus и blur в главном окне. Выполнение любого кода переключения вкладок в этом случае некорректно. В некоторых случаях это все еще может быть вариантом, поэтому я развернул страницу для тестирования здесь. Результаты немного лучше:
FAIL Chrome 64.0.3282.167 (Official Build) (64-bit) Linux Debian Stretch (PC)
FAIL Chrome 70.0.3538.67 (Official Build) (64-bit) Windows 10 (PC)
FAIL Chrome 70.0.3538.77 (Official Build) (32-bit) Windows 7 (PC)
FAIL Chrome 70.0.3538.77 (Official Build) (64-bit) OSX 10.13.5 (Mac mini)
OK FF Quantum 60.2.2esr (64-bit) Linux Debian Stretch (PC)
OK Safari 11.1.1 (13605.2.8) (Mac mini)
This version fails more often in Chrome 64-bit than in Chrome 32-bit. In Chrome 32-bit I got just 1 failure after ~20 successful attempts. This might be related to the fact that Chrome 32-bit is installed on older hardware.
Вопрос: есть ли способ надежно приостанавливать / возобновлять анимацию CSS при переключении вкладок?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Для меня ваш пример работает большую часть времени.
Однако я бы использовал проценты, а также добавил бы начальную точку в ключевые кадры:
@keyframes move {
0% {
left: 0px;
}
100% {
left: 500px
}
}
Также обнаружение неактивной вкладки срабатывает не всегда. Неактивное окно работает лучше: (с jQuery)
$(window).on("blur focus", function(e) {
var prevType = $(this).data("prevType");
if (prevType != e.type) {
var element = document.getElementById("test");
switch (e.type) {
case "blur":
element.style["animation-play-state"] = "paused";
break;
case "focus":
element.style["animation-play-state"] = "running";
break;
}
}
$(this).data("prevType", e.type);
})
Я удалил отправную точку для простоты, в моем реальном сценарии она есть. Добавление / удаление ничего не меняет. Я попытался разместить console.info внутри обработчика переключения вкладок и могу подтвердить, что он всегда запускается.
В iframe это не работает, но когда это фактический документ, он работает: пример. jobse.be/animation.html
@Jobse ваше решение сложнее проверить, так как анимация зацикливается. В любом случае, похоже, он работает в FF, но не в Chrome.
Странно, я проверил в IE / FF / Chrome
Вы можете использовать focus и blur события вместо visibilitychange событие из-за лучшей поддержки браузером первого!
let box = document.querySelector('#test');
window.addEventListener('focus', function() {
box.style.animationPlayState = 'paused';
});
window.addEventListener('blur', function() {
box.style.animationPlayState = 'running';
});
В качестве альтернативы вы также можете сделать это с помощью классов CSS:
.paused {
animation-play-state: paused !important;
-webkit-animation-play-state: paused !important;
-moz-animation-play-state: paused !important;
}
let box = document.querySelector('#test');
window.addEventListener('focus', function() {
box.classList.remove('paused');
});
window.addEventListener('blur', function() {
box.classList.add('paused');
});
Указанные выше два способа не работают в iframe CodePen, JSFiddle, JSBin и т. Д .; возможная причина указана в конце сообщения. Но, вот ссылка на видео, показывающая, как код работает в режиме отладки CodePen.
Confirmed in:
Google Chrome v70.0.3538.67 (Official Build) (32-bit)
Firefox Quantum v62.0.3 (32-bit)
Internet Explorer v11+
iframe:Когда я попытался получить доступ к root window (он же parent объект, обратите внимание, что это не iframe window), в консоли появилась следующая ошибка:
А это означает, что я не могу получить доступ к root window CodePen и других. Поэтому, если я не могу получить к нему доступ, я не могу добавить к нему никаких прослушивателей событий.
Почему использование jQuery здесь что-то меняет?
@Kaiido, использование jQuery здесь мало что меняет. Вы также можете использовать эквивалент Javascript. Кстати, код работает за вас?
@ rv7 Мне все еще непонятно. Вы хотите сказать, что изначально опубликованный мной код работает только изредка, в то время как код с blur и focus работает у вас постоянно? Или обе версии всегда работают?
@Femel опубликованный вами код не работает для меня, в то время как код, который я опубликовал, работает для меня каждый раз.
@Femel Я добавил еще один способ с использованием классов CSS и Javascript. Проверьте, работает ли это для вас или нет. Кстати, у меня как обычно работало.
@ rv7 Нет, результат тот же. Я обновил свой вопрос новым кодом и снова все протестировал. Кстати, этот подход не то же самое, что использование API видимости страницы, когда в игру вступают IFRAME.
@Femel Приложение visibility-api не работает. Но приложение focus-blurсработало, как и ожидалось!
@ rv7, мне удалось протестировать оба приложения на моем ноутбуке 10-летней давности в последней 32-битной версии Chrome. Результаты очень похожи на ваши, за исключением того, что мне удалось вывести приложение focus-blur из строя всего один раз после почти 20 успешных попыток. Так что проблема возникает очень редко, но она определенно существует. Вы можете попробовать 64-битный Chrome?
@Femel Я согласен с тем, что код не работает один раз из 20 (или более) попыток. Но логически я не могу сказать, что это код, это могут быть какие-то ошибки во время выполнения или какие-то скрытые ошибки и т. д. Кроме того, я протестировал ваше приложение focus-blur на своем 64-битном ноутбуке, и результаты были такими же, как в 32-битном устройстве.
@Femel, твоя проблема все еще существует? Потому что я заинтересован в том, чтобы начать еще одну награду за это, если вы готовы к новым (возможно, лучшим) ответам.
@ rv7 проблема все еще существует. Я использую переходы CSS в качестве обходного пути, поэтому мне нет реальной необходимости решать эту проблему. Я могу провести небольшое тестирование, если появится какой-нибудь достойный ответ, но это все.
Вам не нужно использовать какие-либо классы или переключать их, чтобы получить ожидаемый результат. Здесь скачайте html-файл это суть и попробуйте со своей стороны, давайте посмотрим, работает ли это.
Вам необходимо установить animation-play-state при записи CSS для блока test в paused.
Как только окно загрузится, установите animation-play-state на running, затем снова установите их в соответствии с событиями blur и focus.
Также Я заметил, что использование префикса браузера (-webkit-animation-play-state) помогло мне получить ожидаемый результат, поэтому обязательно используйте их. Я тестировал firefox и chrome, у меня отлично работает.
Примечание: Это может не работать с этим фрагментом кода здесь (так как фрагмент не находится в главном окне этой текущей вкладки). Загрузите ссылку, указанную выше, и проверьте на своей стороне.
window.onload = function() {
// not mentioning the state at all does not provide the expected result, so you need to set
// the state to paused and set it to running on window load
document.getElementById('test').style.webkitAnimationPlayState = "running";
document.getElementById('test').style.animationPlayState = 'running';
}
window.onblur = function() {
document.title = 'paused now';
document.getElementById('test').style.webkitAnimationPlayState = "paused";
document.getElementById('test').style.animationPlayState = 'paused';
document.getElementById('test').style.background = 'pink'; // for testing
}
window.onfocus = function() {
document.title = 'running now';
document.getElementById('test').style.webkitAnimationPlayState = "running";
document.getElementById('test').style.animationPlayState = 'running';
document.getElementById('test').style.background = 'red'; // for testing
}div#test {
width: 50px;
height: 50px;
background-color: red;
position: absolute;
left: 10vw;
animation-name: move;
animation-duration: 5s;
animation-fill-mode: forwards;
animation-timing-function: linear;
/* add the state here and set it to running on window load */
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
@keyframes move {
to {
left: 90vw
}
}<div id = "test"></div>
Эта проблема сохраняется до сих пор, хотя поддержка css теперь поддерживается всеми основными браузерами. Пробовал с фокусом / размытием, работает немного лучше, но тоже не работает. Я отказался от попыток поставить его на паузу и просто решил начать с самого начала при размытии, добавив animation: none;