Как определить, что анимация закончилась?
Речь о CSS свойстве animation.
Допустим, мы не успели поймать событие animationend, как тогда определить, что анимация элемента закончилась? Проверяем, что анимация не бесконечная computedStyle.animationIterationCount , ноcomputedStyle.animationPlayState так и продолжает возвращать значение running , даже если анимация уже прошла.Мы можем вычислить время анимации animationDelay + animationDuration * animationIterationCount , но не знаем, когда анимация запускалась.Есть идеи? |
Цитата:
или это чисто тема на рассуждение ? Цитата:
Цитата:
Если режим заполнения (overflow) анимации стоит "forwards" или "both" - значением вычисленного стиля элемента будет значение последнего (!) ключевого кадра. Если режим заполнения стоит "backwards" или "none" - значением вычисленного стиля будет значение, определённое НЕ из анимации - т.е. либо из каскадного стиля (вероятнее всего), либо из чего-нибудь другого. Последний ключевой кадр - это последний ключевой кадр (спс К.О.) Не помню, влияет ли на то, каким будет последний ключевой кадр, свойство iterationCount с нецелым значением (напр. "1.5" проиграет один проход и ещё половину следующего прохода) А вот animationDirection, установленное не в 'normal', обращает наоборот последний ключкадр через экстраполяцию. ... не помню всех деталей. всего лишь полгода прошло) |
Цитата:
Цитата:
Вообще хотелось бы универсальное решение, например, задачи: добавить класс элементу, если над этим элементом не происходит анимация, при условии, что раньше об этом элементе мы ничего не знали. Или хотя бы элементарное: вызвать 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 уже такое не прокатит. |
Цитата:
Цитата:
Цитата:
тут и для анимаций метод с таймаутом прокатит, если её не паузить через animation-play-state а узнать, закончился ли transition, не зная, когда он начался - опять таки, проверять по изменению свойств. API скудное у этих стандартов, да Цитата:
|
Цитата:
|
melky,
да не дочитал полностью сообщение Octane, и запостил.. |
Цитата:
Цитата:
Цитата:
В отличие от 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, у событий анимаций есть особые свойства:
Цитата:
или я не понял, в чём проблема событий анимации :) |
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 сек. |
По анимациям глянь :
http://codepen.io/ColCh/full/jilgs Поизменяй animation-delay или вообще анимации в классе и увидишь, что всё ок а по переходам я сам не придумал, как эту фигню обойти. В моём недописанном движке transition вообще не используются за их ущербностью |
А если поставим начальные значения
top и left отличные от 0, callback выполнится через 1 секунду, а не как положено через 5. В твоем примере resolve выполняется по первому сработавшему animationend , а я пытаюсь сделать событие allNewAnimationsEnd.Ладно узнали мы имена новых анимаций, можно попробовать дождаться окончания их всех. НО мы не знаем какие из этих анимаций будут выполняться. Наверное, буду пока что считать getFiniteAnimationName только для новых анимаций, а delay будет: var delay = Math.max( getTransitionTime(style), getFiniteAnimationTime(style, newAnimationNames) );Интересно, значит все таки 1 animationend гарантированно срабатывает (надеюсь во всех браузерах). Я почему-то был уверен, что если еще и для left поставлю начальное значение 0, то вообще ни одно событие не сработает и callback никогда не выполнится. |
я там код обновил, глянь его :)
Цитата:
Цитата:
Цитата:
Цитата:
|
Теперь наоборот ждем 5 сек, вместо 1 сек, если top=0 :)
Цитата:
---------------------- Цитата:
|
Часовой пояс GMT +3, время: 19:40. |