javascript - Приостановка/возобновление CSS анимации при переключении вкладок




animation tabs (2)

У меня есть куча длительных 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 и stackoverflow JS влияет на результаты, поэтому лучше проводить тестирование непосредственно на статической странице на локальном HTTP-сервере (или используя ссылки ниже).

Для удобства я развернул приведенный выше код в Heroku . Приложение представляет собой статический HTTP-сервер nodejs, который работает по бесплатной подписке, поэтому загрузка страницы может занять до 5 минут в первый раз. Результаты тестирования на этой странице:

FAIL Chrome 64.0.3282.167 (официальная сборка) (64-разрядная версия) Linux Debian Stretch (ПК)
FAIL Chrome 70.0.3538.67 (официальная сборка) (64-разрядная версия) Windows 10 (ПК)
FAIL Chrome 70.0.3538.77 (официальная сборка) (32-разрядная версия) Windows 7 (ПК)
FAIL Chrome 70.0.3538.77 (официальная сборка) (64-разрядная версия) OSX 10.13.5 (Mac mini)
FAIL FF Quantum 60.2.2esr (64-разрядная версия) Linux Debian Stretch (ПК)
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 в главном окне. Выполнение любого кода переключения вкладок в этом случае не является правильным. В некоторых случаях это все еще может быть вариантом, поэтому я развернул страницу для тестирования here . Результаты немного лучше:

FAIL Chrome 64.0.3282.167 (официальная сборка) (64-разрядная версия) Linux Debian Stretch (ПК)
FAIL Chrome 70.0.3538.67 (официальная сборка) (64-разрядная версия) Windows 10 (ПК)
FAIL Chrome 70.0.3538.77 (официальная сборка) (32-разрядная версия) Windows 7 (ПК)
FAIL Chrome 70.0.3538.77 (официальная сборка) (64-разрядная версия) OSX 10.13.5 (Mac mini)
OK FF Quantum 60.2.2esr (64-разрядная версия) Linux Debian Stretch (ПК)
OK Safari 11.1.1 (13605.2.8) (Mac mini)
Эта версия чаще всего дает сбой в 64-разрядной версии Chrome, чем в 32-разрядной версии Chrome. В 32-битном Chrome я получил всего 1 ошибку после ~ 20 успешных попыток. Это может быть связано с тем, что 32-разрядная версия Chrome установлена ​​на устаревшем оборудовании.

Вопрос: есть ли способ надежно приостанавливать / возобновлять CSS анимацию при переключении вкладок?


Вам не нужно использовать какие-либо классы или переключать их, чтобы получить ожидаемый результат. Здесь загрузите этот hist- файл gist и попробуйте на своем конце, посмотрим, работает ли это.

Вам нужно установить animation-play-state при написании CSS для paused test div.

Как только окно загрузится, установите для 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>


Вы можете использовать события 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 .

Живой пример

Подтверждено в :

  1. Google Chrome v70.0.3538.67 (официальная сборка) (32-разрядная версия)

  2. Firefox Quantum v62.0.3 (32-разрядная версия)

  3. Internet Explorer v11 +

Возможная причина того, почему код не работает внутри iframe :

Когда я попытался получить доступ к root window (он же parent объект , обратите внимание, что это не iframe window ), в консоли была зарегистрирована следующая ошибка:

А это значит, что я не могу получить доступ к root window CodePen и других. Поэтому, если я не могу получить к нему доступ, я не могу добавить к нему никаких слушателей событий.





css-animations