Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 25.04.2014, 01:10
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 10.07.2008
Сообщений: 3,873

Как определить, что анимация закончилась?
Речь о CSS свойстве animation.

Допустим, мы не успели поймать событие animationend, как тогда определить, что анимация элемента закончилась?

Проверяем, что анимация не бесконечная computedStyle.animationIterationCount, но
computedStyle.animationPlayState так и продолжает возвращать значение running, даже если анимация уже прошла.

Мы можем вычислить время анимации animationDelay + animationDuration * animationIterationCount, но не знаем, когда анимация запускалась.

Есть идеи?
Ответить с цитированием
  #2 (permalink)  
Старый 25.04.2014, 01:22
sinistral
Посмотреть профиль Найти все сообщения от melky
 
Регистрация: 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.
Ответить с цитированием
  #3 (permalink)  
Старый 25.04.2014, 02:14
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 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.
Ответить с цитированием
  #4 (permalink)  
Старый 25.04.2014, 10:28
sinistral
Посмотреть профиль Найти все сообщения от melky
 
Регистрация: 28.03.2011
Сообщений: 5,418

Сообщение от Octane
Проверять свойства, измененные в keyframes не прокатит, над элементом мог произойти еще и transition.
это easy mode. для того, чтобы узнать, анимируется ли свойство ещё чем-нибудь, делаем requestAnimationFrame , снова получаем вычисленное значение стиля и сверяем с предыдущим.

Сообщение от Octane
Очень удобно, использовать метод addClass, возвращающий promise, которое fulfill'ется, когда все CSS переходы закончены:
это абстракция, и внутри promise может быть не transition, а animation. да и вообще всё, что угодно, хоть GSAP или Web Animations

Сообщение от Octane
Например, для transition можно посчитать время и запустить callback через setTimeout, как это сделано в плагине https://github.com/ai/transition-events
если знать, когда анимация была запущена, вычислить её продолжительность не составляет труда.
тут и для анимаций метод с таймаутом
прокатит, если её не паузить через animation-play-state

а узнать, закончился ли transition, не зная, когда он начался - опять таки, проверять по изменению свойств.

API скудное у этих стандартов, да

Сообщение от Octane
А вот с animation уже такое не прокатит.
из-за паузы?
Ответить с цитированием
  #5 (permalink)  
Старый 25.04.2014, 10:29
sinistral
Посмотреть профиль Найти все сообщения от melky
 
Регистрация: 28.03.2011
Сообщений: 5,418

Сообщение от dmitriymar
События JavaScript
я так понял, тут принципиально обработчики не используются
Ответить с цитированием
  #6 (permalink)  
Старый 25.04.2014, 10:30
х.з
Посмотреть профиль Найти все сообщения от dmitriymar
 
Регистрация: 21.11.2010
Сообщений: 4,588

melky,
да не дочитал полностью сообщение Octane, и запостил..
Ответить с цитированием
  #7 (permalink)  
Старый 25.04.2014, 13:20
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 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.
Ответить с цитированием
  #8 (permalink)  
Старый 25.04.2014, 20:04
sinistral
Посмотреть профиль Найти все сообщения от melky
 
Регистрация: 28.03.2011
Сообщений: 5,418

Octane, у событий анимаций есть особые свойства:

Цитата:
dictionary AnimationEventInit : EventInit {
DOMString animationName;
float elapsedTime;
DOMString pseudoElement;
}
которые позволяют отличить "просто событие конца анимации" от "анимация SUPER закончилась"

или я не понял, в чём проблема событий анимации
Ответить с цитированием
  #9 (permalink)  
Старый 25.04.2014, 23:49
Отправить личное сообщение для Octane Посмотреть профиль Найти все сообщения от Octane  
Регистрация: 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 сек.
Ответить с цитированием
  #10 (permalink)  
Старый 26.04.2014, 00:48
sinistral
Посмотреть профиль Найти все сообщения от melky
 
Регистрация: 28.03.2011
Сообщений: 5,418

По анимациям глянь :

http://codepen.io/ColCh/full/jilgs

Поизменяй animation-delay или вообще анимации в классе и увидишь, что всё ок

а по переходам я сам не придумал, как эту фигню обойти. В моём недописанном движке transition вообще не используются за их ущербностью

Последний раз редактировалось melky, 26.04.2014 в 01:07.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Баги Opera пишем сюда devote Оффтопик 101 08.08.2013 05:56
Как определить была ли ссылка посещена Почемучкин Элементы интерфейса 12 10.09.2012 10:54
Как сделать что бы при регистрации человека на моем сайте у него не появлялось... drunkwolfs Общие вопросы Javascript 2 07.08.2012 10:58
способы организации кода melky Общие вопросы Javascript 17 01.10.2011 22:57
О фрилансе (Личный опыт) free Оффтопик 105 18.08.2011 17:02