25.04.2014, 01:10
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
Как определить, что анимация закончилась?
Речь о CSS свойстве animation.
Допустим, мы не успели поймать событие animationend, как тогда определить, что анимация элемента закончилась?
Проверяем, что анимация не бесконечная computedStyle.animationIterationCount , но
computedStyle.animationPlayState так и продолжает возвращать значение running , даже если анимация уже прошла.
Мы можем вычислить время анимации animationDelay + animationDuration * animationIterationCount , но не знаем, когда анимация запускалась.
Есть идеи?
|
|
25.04.2014, 01:22
|
sinistral
|
|
Регистрация: 28.03.2011
Сообщений: 5,418
|
|
Сообщение от Octane
|
Допустим, мы не успели поймать событие animationend, как тогда определить, что анимация элемента закончилась?
|
хаки и велосипеды начинаются с этой строки А почему нельзя поймать это событие?
или это чисто тема на рассуждение ?
Сообщение от Octane
|
computedStyle.animationPlayState так и продолжает возвращать значение running, даже если анимация уже прошла.
|
свойство не для того сделано, чтобы показывать, проигрывается ли анимация или нет
Сообщение от Octane
|
Есть идеи?
|
Можно проверить по computedStyle одного из свойств, которое участвует в анимации. Определить, какое свойство участвует, можно по keyframesRule анимации.. а это keyframesRule можно найти из animationName.
Если режим заполнения (overflow) анимации стоит "forwards" или "both" - значением вычисленного стиля элемента будет значение последнего (!) ключевого кадра.
Если режим заполнения стоит "backwards" или "none" - значением вычисленного стиля будет значение, определённое НЕ из анимации - т.е. либо из каскадного стиля (вероятнее всего), либо из чего-нибудь другого.
Последний ключевой кадр - это последний ключевой кадр (спс К.О.) Не помню, влияет ли на то, каким будет последний ключевой кадр, свойство iterationCount с нецелым значением (напр. "1.5" проиграет один проход и ещё половину следующего прохода)
А вот animationDirection, установленное не в 'normal', обращает наоборот последний ключкадр через экстраполяцию.
... не помню всех деталей. всего лишь полгода прошло)
Последний раз редактировалось melky, 25.04.2014 в 01:28.
|
|
25.04.2014, 02:14
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
Сообщение от melky
|
чисто тема на рассуждение?
|
да, больше интересует возможность решения
Сообщение от melky
|
Можно проверить по computedStyle одного из свойств, которое участвует в анимации. Определить, какое свойство участвует, можно по keyframesRule анимации.. а это keyframesRule можно найти из animationName.
|
Проверять свойства, измененные в keyframes не прокатит, над элементом мог произойти еще и transition.
Вообще хотелось бы универсальное решение, например, задачи: добавить класс элементу, если над этим элементом не происходит анимация, при условии, что раньше об этом элементе мы ничего не знали. Или хотя бы элементарное: вызвать callback, когда все анимации закончились.
Я знаю, что из-за проблем с контролем CSS анимаций, например, в jQuery animate до сих пор никак не используются CSS transition и animation.
Например, для transition можно посчитать время и запустить callback через setTimeout, как это сделано в плагине https://github.com/ai/transition-events
Очень удобно, использовать метод addClass, возвращающий promise, которое fulfill'ется, когда все CSS переходы закончены:
addClass(element, "trans").then(doSomething)
если в качестве value будет передаваться элемент, можно строить такие очереди:
addClass(document.query(".some"), "trans1").then(function (element) {
doSomething1();
return addClass(element, "trans2");
}).then(function (element) {
doSomething2();
return addClass(element, "trans3");
}).then(function (element) {
doSomething3();
});
Для браузеров, не поддерживающих transition, сразу выполнять действия так, как будто
transition-duration + transition-delay = 0
А вот с animation уже такое не прокатит.
Последний раз редактировалось Octane, 25.04.2014 в 03:53.
|
|
25.04.2014, 10:28
|
sinistral
|
|
Регистрация: 28.03.2011
Сообщений: 5,418
|
|
Сообщение от Octane
|
Проверять свойства, измененные в keyframes не прокатит, над элементом мог произойти еще и transition.
|
это easy mode. для того, чтобы узнать, анимируется ли свойство ещё чем-нибудь, делаем requestAnimationFrame , снова получаем вычисленное значение стиля и сверяем с предыдущим.
Сообщение от Octane
|
Очень удобно, использовать метод addClass, возвращающий promise, которое fulfill'ется, когда все CSS переходы закончены:
|
это абстракция, и внутри promise может быть не transition, а animation. да и вообще всё, что угодно, хоть GSAP или Web Animations
если знать, когда анимация была запущена, вычислить её продолжительность не составляет труда.
тут и для анимаций метод с таймаутом
прокатит, если её не паузить через animation-play-state
а узнать, закончился ли transition, не зная, когда он начался - опять таки, проверять по изменению свойств.
API скудное у этих стандартов, да
Сообщение от Octane
|
А вот с animation уже такое не прокатит.
|
из-за паузы?
|
|
25.04.2014, 10:29
|
sinistral
|
|
Регистрация: 28.03.2011
Сообщений: 5,418
|
|
Сообщение от dmitriymar
|
События JavaScript
|
я так понял, тут принципиально обработчики не используются
|
|
25.04.2014, 10:30
|
х.з
|
|
Регистрация: 21.11.2010
Сообщений: 4,588
|
|
melky,
да не дочитал полностью сообщение Octane, и запостил..
|
|
25.04.2014, 13:20
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
Сообщение от melky
|
это easy mode. для того, чтобы узнать, анимируется ли свойство ещё чем-нибудь, делаем requestAnimationFrame , снова получаем вычисленное значение стиля и сверяем с предыдущим.
|
Да я понимаю, что можно так попытаться вычислить анимацию, но это неудобно, потому что асинхронно и придется проверять все свойства, описанные в keyframes, пока не найдем, которое все еще изменяется. Простого isAnimated похоже не получится написать.
Сообщение от melky
|
если знать, когда анимация была запущена
|
В этом вся и проблема. Расчет времени анимации я написал еще в первом посте, но это время бесполезно, если не знать момент запуска.
Сообщение от melky
|
из-за паузы?
|
transition само по себе не происходит. Если рассматривать в контексте функции addClass, то transition всегда будет начинаться при добавлении CSS класса, тоесть мы будем знать время начала (хотя может быть вариант, что какой-то переход еще не закончился).
В отличие от transition, animation может быть запущена хоть при загрузке старницы, действий никаких не требуется, время старта мы не знаем.
Пауза ладно, фиг с ней, ее хотя бы можно отследить по значению свойства, проблема начинается даже в простом случае:
.animated {
animation: anim1 1s 1, anim2 3s 1;
animation-delay: 0s, 5s;
}
В данном случае может сработать до 4х событий (2 раза animationstart и соответственно 2 animationend). Если анимации запускаются с разной задержкой, становится проблемно выполнить действия, когда все анимации над элементом закончатся. Даже распарсив в JavaScript свойство style.animationName и узнав количество анимаций, мы не уверены, что все анимации запустятся, браузер не известит об анимации свойства, которое и так уже находится в состоянии конечного кадра (хотя надо будет проверить, уверен что так работает для transition). Не понятно, когда и сколько событий нужно подождать, чтобы удостовериться, что анимация закончилась.
Если кому интересно, наработки здесь: getTransitionTime и getFiniteAnimationTime, add|remove|toggleClass
Последний раз редактировалось Octane, 25.04.2014 в 13:48.
|
|
25.04.2014, 20:04
|
sinistral
|
|
Регистрация: 28.03.2011
Сообщений: 5,418
|
|
Octane, у событий анимаций есть особые свойства:
Цитата:
|
dictionary AnimationEventInit : EventInit {
DOMString animationName;
float elapsedTime;
DOMString pseudoElement;
}
|
которые позволяют отличить "просто событие конца анимации" от "анимация SUPER закончилась"
или я не понял, в чём проблема событий анимации
|
|
25.04.2014, 23:49
|
|
|
Регистрация: 10.07.2008
Сообщений: 3,873
|
|
elapsedTime для каждой отдельной анимации приходит, не понимаю, как это может помочь.
Давай напишем метод addClass→promise для следующего случая:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>…</title>
<style>
.animated {
position: fixed;
top: 0;
left: 50%;
width: 20px;
height: 20px;
background: #f50;
transition: top 5s, left 1s;
}
.move {
top: 0;
left: 0;
}
</style>
</head>
<body>
<div class="animated"></div>
<script>
function parseFloats(string) {
return string.split(",").map(function (string) {
return parseFloat(string) || 0;
});
}
function calcTransitionTime(delay, duration) {
var length = Math.max(duration.length, delay.length),
i = 0, time, maxTime = 0;
while (i < length) {
time = (delay[i] || 0) + (duration[i] || 0);
if (time > maxTime) {
maxTime = time;
}
i++;
}
return Math.ceil(maxTime * 1000);
}
function getTransitionTime(style) {
return calcTransitionTime(
parseFloats(style.transitionDelay),
parseFloats(style.transitionDuration)
);
}
function addClass(element, className) {
return new Promise(function (resolve) {
element.classList.add(className);
var delay = getTransitionTime(getComputedStyle(element));
setTimeout(function () {
resolve(element);
}, delay);
});
}
window.onload = function () {
var el = document.querySelector(".animated");
addClass(el, "move").then(function () {
alert("done");
});
};
</script>
</body>
</html>
http://learn.javascript.ru/play/mdqxkb
Здесь сработает через 1 секунду только один transitionend , но максимальное время анимации 5 секунд. Браузер не будет ничего делать для свойства top , ни событий, ни reflow. Обещание будет ждать зря 4 секунды. Есть идеи, как определить, что анимация закончилась за 1 секунду?
Изобретать свой CSS парсер как-то не хочется, потому что просто вытащить анимируемое свойство из document.styleSheets[i].cssRules[j].style[n] не получится, класс может быть «размазан» по куче cssRules .
Тоже самое и с animation:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>…</title>
<style>
.animated {
position: fixed;
top: 0;
left: 50%;
width: 20px;
height: 20px;
background: #f50;
}
.move {
animation: move-top 5s, move-left 1s;
animation-fill-mode: both;
}
@keyframes move-top {
100% {
top: 0;
}
}
@keyframes move-left {
100% {
left: 0;
}
}
</style>
</head>
<body>
<div class="animated"></div>
<script>
function parseFloats(string) {
return string.split(",").map(function (string) {
return parseFloat(string) || 0;
});
}
function calcFiniteAnimationTime(delay, duration, count) {
var length = Math.max(duration.length, delay.length, count.length),
i = 0, time, maxTime = 0;
while (i < length) {
time = (delay[i] || 0) + (duration[i] || 0) * (count[i] || 0);
if (time > maxTime) {
maxTime = time;
}
i++;
}
return Math.ceil(maxTime * 1000);
}
function getFiniteAnimationTime(style) {
return calcFiniteAnimationTime(
parseFloats(style.animationDelay),
parseFloats(style.animationDuration),
parseFloats(style.animationIterationCount)
);
}
function addClass(element, className) {
return new Promise(function (resolve) {
element.classList.add(className);
var delay = getFiniteAnimationTime(getComputedStyle(element));
setTimeout(function () {
resolve(element);
}, delay);
});
}
window.onload = function () {
var el = document.querySelector(".animated");
addClass(el, "move").then(function () {
alert("done");
});
};
</script>
</body>
</html>
http://learn.javascript.ru/play/hOiFcb
Произойдет только одна пара событий animationstart и animationend , анимация закончится за 1 секунду, но delay будет равен 5 сек.
|
|
26.04.2014, 00:48
|
sinistral
|
|
Регистрация: 28.03.2011
Сообщений: 5,418
|
|
По анимациям глянь :
http://codepen.io/ColCh/full/jilgs
Поизменяй animation-delay или вообще анимации в классе и увидишь, что всё ок
а по переходам я сам не придумал, как эту фигню обойти. В моём недописанном движке transition вообще не используются за их ущербностью
Последний раз редактировалось melky, 26.04.2014 в 01:07.
|
|
|
|