dr.Brain

doctor Brain

мир глазами веб-разработчика

Анимация элементов с display:none и callback функции

Учимся делать корректную анимацию появления элемента на странице

dr.Brain

время чтения 3 мин.

Photo by Sam Schooler on Unsplash

Когда появляется необходимость сделать анимацию для появляющегося элемента, обычно мы не задумываемся и прибегаем к помощи jQuery.

Например:

$('.foo').fadeIn(300);

или

$('.foo').fadeOut(300);

Почему мы так поступаем? Потому что это не требует никаких усилий. jQuery позаботился даже о том, чтобы у нас была возможность вызвать callback-функцию после анимации:

$('.foo').fadeIn(300, doSomthingAfterIt);

Это очень быстро и эффективно. Но, к сожалению, появляется проблема: вся анимация, которую предлагает jQuery, в конечном счете является чистым JavaScript кодом, без малейшей примеси CSS.

Хороший frontend-разработчик должен знать, что переходы и анимации должны находиться в зоне ответственности CSS. Только так можно добиться лучшей производительности. Функции jQuery: .hide(), .show(), .toggle(), .fadeIn(), .slideUp() не рекомендуются к использованию.


CSS переходы (transition)

Использование универсального CSS-свойства transition является верным подходом. Все что нужно сделать - это добавить класс с нужным свойством элементу:

.foo {
  opacity: 0;
  transition: opacity 300ms;
}

.fade-in {
  opacity: 1;
}
$('.foo').addClass('fade-in');

Тем не менее использование свойства transition связано с двумя проблемами:

Проблема 1

Как использовать callback-функции после transition?

Тут есть маленькая хитрость, а именно: событие transitionend (подробно спецификацию можно прочитать тут и тут:

$('.foo')
    .addClass('fade-in')
    .one(transitionend, doSomethingAfterIt);

Теперь callback-функции доступны после любой анимации.

Проблема 2

Как сделать анимацию для элемента с исходным свойством display:none

Анимация для скрытого объекта - насущная проблема. Например, если мы добавим класс с нужными свойствами к такому элементу и используем transition, ничего не произойдет.

Такой код не будет работать:

/* исходное состояние элемента */
.foo{
    display: none;
    opacity: 0;
    transition: opacity 300ms;
}

/* желаемый результат */
.foo.fade-in{
    display: block;
    opacity: 1;
}

Это происходит потому, что элемент не отрисован и не занимает места на экране, а это является обязательным условием для его анимации. То есть transition для элемента с display:none не работает.

Метод Reflow

Для начала, нужно разобраться с терминами repaint и reflow. Если коротко:

  • repaint вызывается при изменениях, связанных с внешним видом элементов. Эти изменения не затрагивают размеры объекта и его положение на экране. Например, это могут быть CSS-свойства opacity, outline, background-color.

  • reflow вызывается при изменении размеров элемента. В свою очередь это приводит к перерасчету макета страницы и дальнейшему обновлению экрана уже с учетом изменений.

По этой причине, для того, чтобы добавить анимацию элементу с исходным свойством display:none, нужно форсировать reflow. Как это сделать? Добавим элементу класс со свойством display:block и вызовем метод, возвращающий размеры элемента. Для определения размеров браузер будет вынужден обновить элемент и форсировать запуск reflow. После запуска reflow элемент уже занимает место на экране и выполнение анимации с помощью transition становится возможным.

Вот пример рабочего кода:

var $foo = $('.foo');

$foo
    .addClass('block')
    .outerWidth(); // Reflow

$foo
    .addClass('fade-in')
    .one(transitionEnd, function() {
        alert('Animated');
    });
.block {
    display: block;
}

.fade-in {
    opacity: 1;
}

Спасибо за внимание.

Новые публикации

Далее

Категории

О нас

Frontend & Backend. Статьи, обзоры, заметки, код, уроки.