webentwicklung-frage-antwort-db.com.de

CSS-Animationen beim Wechseln zwischen Registerkarten anhalten/fortsetzen

Ich habe eine Reihe von langlebigen CSS-Animationen auf einer Seite. Ich möchte sie anhalten, wenn ein Benutzer zu einer anderen Registerkarte wechselt, und ihn wieder aufnehmen, wenn der Benutzer sich wieder auf der ursprünglichen Registerkarte befindet. Der Einfachheit halber strebe ich an dieser Stelle keine Cross-Browser-Lösung an. Damit es in Chrome funktioniert, sollte es genug sein.

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>

Der obige Code verschiebt ein rotes Rechteck horizontal. Es dauert 5 Sekunden, bis das Rechteck die Animation abgeschlossen hat.

Das Problem: Wechseln Sie nach dem Start der Animation zu einer anderen Browser-Registerkarte. Wechseln Sie nach einiger Zeit (länger als 5 Sekunden) wieder zur ersten Registerkarte.

Erwartetes Ergebnis: Das Rechteck setzt seinen Pfad von dem Punkt aus fort, an dem es unterbrochen wurde.

Tatsächliches Ergebnis: Meistens erscheint das Rechteck am endgültigen Ziel und stoppt. Manchmal funktioniert es wie erwartet. Das Video demonstriert das Problem.

Ich habe mit verschiedenen Werten für animation-fill-mode und animation-timing-function gespielt, aber das Ergebnis war immer dasselbe. Wie rv7 wies darauf hin , wirkt sich das Teilen der Beispiele in CodePen, JSFiddle, JSBin und JSBin Stackoverflow auf die Ergebnisse aus. Daher sollten Sie direkt mit einer statischen Seite auf einem lokalen HTTP-Server (oder mit Links unten) testen.

Der Einfachheit halber habe ich den obigen Code bei Heroku eingesetzt. Die App ist ein statischer Node-HTTP-Server, der mit einem kostenlosen Abonnement ausgeführt wird. Es kann also bis zu 5 Minuten dauern, bis die Seite zum ersten Mal geladen wird. Testergebnisse gegen diese Seite: 

FAIL Chrome 64.0.3282.167 (Offizielle Version) (64-Bit) Linux Debian Stretch (PC)
FAIL Chrome 70.0.3538.67 (Offizielle Version) (64-Bit) Windows 10 (PC)
FAIL Chrome 70.0.3538.77 (Offizielle Version) (32-Bit) Windows 7 (PC)
FAIL Chrome 70.0.3538.77 (Offizielle Version) (64-Bit) OSX 10.13.5 (Mac mini)
FEHLER FF Quantum 60.2.2esr (64-Bit) Linux Debian Stretch (PC)
OK Safari 11.1.1 (13605.2.8) (Mac mini)

Die page-Sichtbarkeits-API kann durch die Fenster focus und blur -Ereignisse wie folgt ersetzt werden:

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

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

Dieser Ersatz ist jedoch nicht gleichwertig. Wenn eine Seite IFRAMEs enthält, werden durch Interaktion mit ihrem Inhalt focus- und blur-Ereignisse im Hauptfenster ausgelöst. In diesem Fall ist es nicht korrekt, einen Tabwechsel-Code auszuführen. In einigen Fällen ist dies möglicherweise immer noch eine Option, daher habe ich eine Seite zum Testen von hier bereitgestellt. Die Ergebnisse sind etwas besser: 

FAIL Chrome 64.0.3282.167 (Offizielle Version) (64-Bit) Linux Debian Stretch (PC)
FAIL Chrome 70.0.3538.67 (Offizielle Version) (64-Bit) Windows 10 (PC)
FAIL Chrome 70.0.3538.77 (Offizielle Version) (32-Bit) Windows 7 (PC)
FAIL Chrome 70.0.3538.77 (Offizielle Version) (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)
Diese Version schlägt in Chrome 64-Bit häufiger als in Chrome 32-Bit fehl. In Chrome 32-Bit habe ich nach ca. 20 erfolgreichen Versuchen nur einen Fehler erhalten. Dies kann mit der Tatsache zusammenhängen, dass Chrome 32-Bit auf älterer Hardware installiert ist.

Die Frage: gibt es eine Möglichkeit, CSS-Animationen beim Wechseln zwischen Registerkarten zuverlässig anzuhalten/wieder aufzunehmen?

18
Femel

Sie können mit den Ereignissen focus UND blur anstelle von visibilitychange [] gehen, da der Browser besser unterstützt wird!

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

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

window.addEventListener('blur', function() {
  box.style.animationPlayState = 'running';
});

Alternativ können Sie dies auch mit CSS-Klassen tun:

.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');
});

Die beiden oben genannten Methoden funktionieren nicht in iframe von CodePen, JSFiddle, JSBin usw. Ein möglicher Grund wird am Postende angegeben. Aber Hier ist ein Videolink, der anzeigt, wie der Code im Debug-Modus von CodePen funktioniert.

Live-Beispiel

Bestätigt in:

  1. Google Chrome v70.0.3538.67 (Offizielle Version) (32-Bit)

  2. Firefox Quantum v62.0.3 (32-Bit)

  3. Internet Explorer v11 +


Möglicher Grund, warum der Code in iframe nicht funktioniert:

Beim Versuch, auf das root window zuzugreifen (aka parent object , beachten Sie, dass es sich nicht um das iframe window handelt), wurde der folgende Fehler in der Konsole protokolliert:

enter image description here

Dies bedeutet, dass ich nicht auf den root window von CodePen und anderen zugreifen kann. Wenn ich also nicht darauf zugreifen kann, kann ich keine Ereignis-Listener hinzufügen.

9
rv7

Für mich funktioniert dein Beispiel meistens.

Ich würde jedoch Prozentsätze verwenden und auch einen Startpunkt in den Keyframes hinzufügen:

@keyframes move {
  0% { 
    left: 0px;
  }
  100% {
    left: 500px
  }
}

Die Erkennung der inaktiven Registerkarte wird nicht immer ausgelöst. Ein inaktives Fenster scheint besser zu funktionieren: (mit 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);
})
0
Jobse

Sie müssen keine Klassen verwenden oder sie umschalten, um das erwartete Ergebnis zu erhalten. Hier können Sie diese Gist html-Datei herunterladen und an Ihrem Ende versuchen, ob dies funktioniert.

Sie müssen den animation-play-state beim Schreiben von CSS für test div auf paused setzen.

Wenn das Fenster geladen ist, setzen Sie den animation-play-state auf running und stellen Sie sie dann erneut entsprechend dem Ereignis blur und focus ein.

Auch das Verwenden des Browser-Präfixes (-webkit-animation-play-state) hat mir dabei geholfen, das erwartete Ergebnis zu erhalten, stellen Sie also sicher, dass Sie diese verwenden. Ich habe Firefox und Chrom getestet, funktioniert gut für mich.

Note: Dies funktioniert möglicherweise nicht mit diesem Code-Snippet (da sich das Snippet nicht im Hauptfenster dieser aktuellen Registerkarte befindet). Laden Sie den oben angegebenen Gist-Link herunter und überprüfen Sie das Ende.

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>

0
Towkir Ahmed