То, что вы упомянули в последнем абзаце вашего вопроса, верно. После того, как анимация установлена для элемента и полностью выполнена, браузер/UA продолжает запоминать ее состояние. Эта анимация не может быть перезапущена (в том же или обратном направлении), если исходная анимация не будет удалена хотя бы на долю секунды.
Для этого нам нужно ввести задержку (тайм-аут) или принудительно перерисовать, потому что без этого, если мы удалим и добавим только классы в рамках одного и того же блокирующего вызова, то UA не увидит никакой разницы. Он будет продолжать думать, что анимация никогда не удалялась.
Для повторного запуска одной и той же анимации:
Чтобы снова запустить ту же анимацию, когда в любом месте документа сделан щелчок, просто удалите существующий класс анимации и снова добавьте его после задержки, как в приведенном ниже фрагменте.
var elem = document.querySelector(".box");
document.addEventListener("click", function(e) {
refireAnim("open");
});
function refireAnim(anim) {
elem.classList.remove(anim);
setTimeout(function() {
elem.classList.add(anim);
}, 0);
}
.box {
width: 200px;
height: 200px;
background: #099;
transform-origin: 0 0;
}
.open {
animation: grow 1s ease;
}
@keyframes grow {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
<div class="box open"></div>
Для повторного запуска одной и той же анимации в другом направлении:
Чтобы снова запустить ту же анимацию, но в другом направлении, удалите и добавьте класс анимации после задержки, как в приведенном выше фрагменте, а также измените направление.
var elem = document.querySelector(".box")
forward = true;
document.addEventListener("click", function() {
refireAnim("open");
if (forward) { /* change the direction based on state */
elem.style["animation-direction"] = "reverse";
}
else {
elem.style["animation-direction"] = null;
}
forward = !forward; /* change the state */
});
function refireAnim(anim) {
elem.classList.remove(anim);
setTimeout(function() {
elem.classList.add(anim);
}, 0);
}
.box {
width: 200px;
height: 200px;
background: #099;
transform-origin: 0 0;
}
.open {
animation: grow 1s ease forwards;
}
@keyframes grow {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="box open"></div>
Или, как вы указали, принудительно перерисуйте после удаления класса, но перед его повторным добавлением. (Кажется, это позволяет избежать включения вспышки)
var elem = document.querySelector(".box")
forward = true;
document.addEventListener("click", function() {
refireAnim("open");
if (forward) { /* change the direction based on state */
elem.style["animation-direction"] = "reverse";
}
else {
elem.style["animation-direction"] = null;
}
forward = !forward; /* change the state */
});
function refireAnim(anim) {
elem.classList.remove(anim);
elem.clientHeight;
elem.classList.add(anim);
}
.box {
width: 200px;
height: 200px;
background: #099;
transform-origin: 0 0;
}
.open {
animation: grow 1s ease forwards;
}
@keyframes grow {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="box open"></div>
Для переключения одной анимации на другую:
Создайте два класса, один из которых предназначен для анимации открытия, а другой — для анимации закрытия, и переключайте их по щелчку, чтобы была задержка между удалением одного класса и добавлением другого.
var trigger = document.querySelector("#toggle"),
elem = document.querySelector(".box");
trigger.addEventListener("click", function() {
currState = elem.className.split(" ")[1];
newState = (currState == "open") ? "close" : "open";
switchAnim(currState, newState);
});
function switchAnim(existingAnim, newAnim) {
elem.classList.remove(existingAnim);
setTimeout(function() {
elem.classList.add(newAnim);
}, 0);
}
.box {
width: 200px;
height: 200px;
background: #099;
transform-origin: 0 0;
}
.open {
animation: grow 1s ease;
}
.close {
animation: grow 1s ease reverse forwards;
}
@keyframes grow {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
<input type="checkbox" id="toggle" />
<div class="box open"></div>
Как вы указали в комментариях, вы можете избежать тайм-аута и вместо этого заставить браузер выполнить перерисовку, прежде чем добавлять также другой класс анимации. (Кажется, это позволяет избежать включения вспышки)
var trigger = document.querySelector("#toggle"),
elem = document.querySelector(".box");
trigger.addEventListener("click", function() {
currState = elem.className.split(" ")[1];
newState = (currState == "open") ? "close" : "open";
switchAnim(currState, newState);
});
function switchAnim(existingAnim, newAnim) {
elem.classList.remove(existingAnim);
elem.clientHeight;
elem.classList.add(newAnim);
}
.box {
width: 200px;
height: 200px;
background: #099;
transform-origin: 0 0;
}
.open {
animation: grow 1s ease;
}
.close {
animation: grow 1s ease reverse forwards;
}
@keyframes grow {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
<input type="checkbox" id="toggle" />
<div class="box open"></div>
Другой способ избежать этой вспышки (короткий период, когда элемент возвращается к своей реальной высоте и ширине перед повторной анимацией) — использовать переходы, как в приведенном ниже фрагменте.
var trigger = document.querySelector("#toggle"),
elem = document.querySelector(".box");
trigger.addEventListener("click", function() {
elem.classList.toggle("close");
elem.classList.toggle("open");
});
window.onload = function() {
elem.classList.toggle("close");
elem.classList.toggle("open");
}
.box {
width: 200px;
height: 200px;
background: #099;
transform-origin: 0 0;
transition: all 1s;
}
.open {
transform: scale(1);
}
.close {
transform: scale(0);
}
<input type="checkbox" id="toggle" />
<div class="box close"></div>
Повторный запуск любой анимации, существующей в элементе в заданной точке:
Флажок переключает анимацию, но всякий раз, когда документ щелкается, любая применимая анимация в зависимости от состояния флажка будет повторно запускаться.
var trigger = document.querySelector("#toggle"),
elem = document.querySelector(".box");
document.addEventListener("click", function(e) {
if (e.target != trigger) {
currState = elem.className.split(" ")[1];
switchAnim(currState, currState);
}
})
trigger.addEventListener("click", function() {
if (trigger.checked)
switchAnim("close", "open");
else
switchAnim("open", "close");
});
function switchAnim(existingAnim, newAnim) {
console.log(existingAnim);
elem.classList.remove(existingAnim);
setTimeout(function() {
elem.classList.add(newAnim);
}, 0);
}
.box {
width: 200px;
height: 200px;
background: #099;
transform-origin: 0 0;
}
.open {
animation: grow 1s ease;
}
.close {
animation: grow 1s ease reverse forwards;
}
@keyframes grow {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
<input type="checkbox" id="toggle" checked/>
<div class="box open"></div>
person
Harry
schedule
04.02.2016
animation-iteration-count:infinite;
- person Aaron   schedule 04.02.2016scale(0)
, а затем снова расти? - person Harry   schedule 04.02.2016