切换标签时暂停/恢复CSS动画

eug*_*sqr 18 javascript css animation tabs css-animations

我在页面上有一堆长时间运行的CSS动画.我想在用户切换到另一个选项卡时暂停它们,并在用户再次返回原始选项卡时恢复它们.为简单起见,我不打算在此时使用跨浏览器解决方案; 让它在Chrome中运行应该足够了.

document.addEventListener("visibilitychange", function() {
  if (document.hidden) {
    document.querySelector("#test").classList.add("paused");
  } else {
    document.querySelector("#test").classList.remove("paused");    
  }
});
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<div id="test"></div>
Run Code Online (Sandbox Code Playgroud)

上面的代码水平移动一个红色矩形.矩形完成动画需要5秒钟.

问题:动画启动后,切换到另一个浏览器选项卡; 经过一段时间(超过5秒)后切换回第一个标签.

预期结果:矩形从其停止的位置继续其路径.

实际结果:矩形出现在最终目的地并停止的大部分时间.有时它按预期工作.视频演示了这个问题.

我打了不同的价值观animation-fill-modeanimation-timing-function,但结果总是相同的.正如rv7 指出的那样,在CodePen,JSFiddle,JSBin和stackoverflow JS工具中共享示例会影响结果,因此最好直接针对本地HTTP服务器上的静态页面进行测试(或使用下面的链接).

为方便起见,我已将上面的代码部署到Heroku.该应用程序是一个静态nodejs HTTP服务器,它运行在免费订阅上,因此第一次加载页面最多可能需要5分钟.针对此页面的测试结果:

FAIL Chrome 64.0.3282.167(官方版)(64位)Linux Debian Stretch(PC)
FAIL Chrome 70.0.3538.67(官方版)(64位)Windows 10(PC)
FAIL Chrome 70.0.3538.77(官方版)(32 -bit)Windows 7(PC)
FAIL Chrome 70.0.3538.77(官方版)(64位)OSX 10.13.5(Mac mini)
FAIL FF Quantum 60.2.2esr(64位)Linux Debian Stretch(PC)
OK Safari 11.1 .1(13605.2.8)(Mac mini)

网页的曝光率API可以用窗口来代替focusblur事件是这样的:

window.addEventListener("focus", function() {
    document.querySelector("#test").classList.remove("paused");        
});

window.addEventListener("blur", function() {
    document.querySelector("#test").classList.add("paused");
});
Run Code Online (Sandbox Code Playgroud)

然而,这种替换并不等同.如果页面包含IFRAME,则与其内容进行交互将触发主窗口上的事件focusblur事件.在这种情况下执行任何制表符切换代码是不正确的.在某些情况下,这可能仍然是一个选项,所以我在这里部署了一个测试页面.结果略好一些:

FAIL Chrome 64.0.3282.167(官方版)(64位)Linux Debian Stretch(PC)
FAIL Chrome 70.0.3538.67(官方版)(64位)Windows 10(PC)
FAIL Chrome 70.0.3538.77(官方版)(32 -bit)Windows 7(PC)
FAIL Chrome 70.0.3538.77(官方版)(64位)OSX 10.13.5(Mac mini)
OK FF Quantum 60.2.2esr(64位)Linux Debian Stretch(PC)
OK Safari 11.1 .1(13605.2.8)(Mac mini)
此版本在Chrome 64位中比在Chrome 32位中更常失败.在Chrome 32位中,我在成功尝试20次后失败了1次.这可能与Chrome 32位安装在较旧的硬件上有关.

问题是:有没有办法在切换标签时可靠地暂停/恢复CSS动画?

rv7*_*rv7 9

您可以使用focusblur事件而不是visibilitychange事件,因为前者有更好的浏览器支持!

let box = document.querySelector('#test');

window.addEventListener('focus', function() {
  box.style.animationPlayState = 'paused';
});

window.addEventListener('blur', function() {
  box.style.animationPlayState = 'running';
});
Run Code Online (Sandbox Code Playgroud)

或者,您也可以使用CSS类来执行此操作:

.paused {
  animation-play-state: paused !important;
  -webkit-animation-play-state: paused !important;
  -moz-animation-play-state: paused !important;
}
Run Code Online (Sandbox Code Playgroud)
let box = document.querySelector('#test');

window.addEventListener('focus', function() {
  box.classList.remove('paused');
});

window.addEventListener('blur', function() {
  box.classList.add('paused');
});
Run Code Online (Sandbox Code Playgroud)

以上两种方式在CodePen,JSFiddle,JSBin等的iframe中不起作用; 在帖子结尾提供了一个可能的原因.但是,这是一个视频链接,显示代码如何在CodePen的调试模式下工作.

实例

确认:

  1. 谷歌浏览器v70.0.3538.67(官方版)(32位)

  2. Firefox Quantum v62.0.3(32位)

  3. Internet Explorer v11 +


代码无法在其中运行的可能原因iframe:

当我尝试访问root window(又名parent对象,请注意它不是iframe window)时,控制台中记录以下错误:

在此输入图像描述

这意味着我无法访问root windowCodePen和其他人.因此,如果我无法访问它,我无法向其添加任何事件侦听器.


Job*_*bse 0

对我来说,你的例子大部分时间都有效。

不过,我会使用百分比,并在关键帧中添加一个起点:

@keyframes move {
  0% { 
    left: 0px;
  }
  100% {
    left: 500px
  }
}
Run Code Online (Sandbox Code Playgroud)

另外,非活动选项卡检测并不总是触发。非活动窗口似乎效果更好:(使用 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);
})
Run Code Online (Sandbox Code Playgroud)