Главная » Статьи » Создание плавной последовательной анимации CSS с Sass

Создание плавной последовательной анимации CSS с Sass

Создание плавной последовательной анимации CSS с Sass

От автора: в этой статье я покажу, как создается плавная анимация CSS с помощью Sass цикла for. Эту анимацию можно использовать в вашем приложении, неважно используете вы React, Vue или Angular.

Зачем вообще что-то анимировать?

Анимация – жизненно важный ингредиент при добавлении жизни и характера сайту или приложению. Она может сделать переходы быстрее, даже если без анимации переходы требуют меньше времени. В частности, анимация полезна во время запроса динамических данных с API. На этом я сегодня и остановлюсь.

Пример типов анимации, о которых я сегодня буду вести речь:

Здесь 2 отдельные анимации:

Прелоадер – когда контент запрошен, но еще не загружен.

Загрузка – контент загружен и последовательно появляется.

CSS анимация

Лично я почти всегда использовал CSS свойство transition для анимации. Переходы – отличная и простая вещь, но они ограничены. CSS свойство animation намного мощнее, но и значительно сложнее.

В CSS анимации есть 2 основных части:

Свойство animation

Правило @keyframes

Свойство animation

Animation – это сокращение для ряда свойств анимации (animation-name, animation-duration и т.д.). Обычное правило анимации может выглядеть следующим образом:

.tile { // duration | timing-function | iteration-count | name animation: 1s ease infinite pulse;
}

Свойство animation сверху применяет анимацию pulse к элементу .tile длительностью 1 секунду с бесконечным повторением и функцией смягчения ease. Здесь мало что происходит, но это лучше видно на практике (подробнее ниже).

Правило @keyframes

Правило @keyframes работает по принципу правила @media: внутрь него вложен CSS. В правиле keyframes содержатся правила стилей, которые применяются к элементу по мере прогресса анимации от 0% до 100%.

Создадим пример с правилом @keyframes:

@keyframes pulse { 0% { background: $tile-bg; transform: scale(1); } 25% { background: darken($tile-bg, 10%); transform: scale(1.015); } 50% { background: $tile-bg; transform: scale(1); }
}

В нашем примере .tile в первой половине анимации будет затемняться и увеличиваться, после чего вернется в исходное состояние. Анимация будет длиться 1 секунду, как указано в свойстве animation для .tile. Между 0% и 100% можно добавить любое количество процентов.

Префиксы браузеров

К сожалению, в продакшене до сих пор нужно использовать вендорные префиксы –moz и –webkit:

@-webkit-keyframes pulse { // Animations.
} @-moz-keyframes pulse { // Animations.
}

Обслуживать такой код быстро надоедает. К счастью, autoprefixer автоматически добавляет вендорные префиксы, и вы можете сосредоточиться на написании CSS. Этот инструмент меняет жизнь, его стоит интегрировать в свой рабочий процесс.

Задержка анимации и nth-child

Я буду много использовать свойство animation-delay вместе с селектором nth-child для анимирования группы элементов во временной последовательности. Задержка анимации позволяет применять ее к разным элементам на разных стартовых точках. Так анимация на группе элементов будет похожа на круги на воде.

Наш конечный CSS:

.tile:nth-child(1) { animation-delay: .1s;
} .tile:nth-child(2) { animation-delay: .2s;
} .tile:nth-child(3) { animation-delay: .3s;
}

И т.д…

Создание прелоадер анимации с помощью циклов Sass

Код сверху дает желаемый эффект, но так писать очень неудобно. Придется очень долго вносить любые изменения. Вместо этого можно использовать Sass цикл @for, чтобы сделать код более управляемым.

Sass циклы @for

Пример простого Sass цикла for:

@for $i from 1 through 3 { .tile-#{$i} { margin-left: 100px * #{$i} }
}

Код сверху компилируется в CSS:

.tile-1 { margin-left: 100px;
} .tile-2 { margin-left: 200px;
} .tile-3 { margin-left: 300px;
}

Циклы Sass, nth-child и animation-delay

Мы будем использовать цикл for для обращения к nth-child элементу и добавлять задержку к каждой анимации. По мере прохождения цикла будем увеличивать задержку.

// Loop from 1-9.
@for $i from 1 through 9 { .tile { // :nth-child(1-9) &:nth-child(#{$i}) { // Delay the animation. Delay increases as items loop. animation-delay: $i * (1s / 18); } }
}

В этом примере я использую 9 плейсхолдер элементов, поэтому я установил цикл на 9, а animation-delay основана на 9. Из-за этого у анимации будет постоянный ритм, а девятый nth-child будет иметь задержку 0.5s (половину анимации pulse).

CSS код цикла:

.tile:nth-child(1) { animation-delay: .05555s;
} .tile:nth-child(2) { animation-delay: .1111s;
} .tile:nth-child(3) { animation-delay: .01666s;
}

И т.д. Посмотрите демо:

Анимация хорошая и резкая. Удобно показывать на экране такую анимацию, пока пользователь ждет ответа от API. Что делать, когда контент загрузился? Перейдем к этому.

Анимация элементов после загрузки

После загрузки элементов хорошо бы показать их один за другим, как в последовательности. Воспользуемся тем, что уже знаем, и добавим пару трюков.

Проигрывание анимации только один раз

Эта анимация будет отличаться от прелоадера, так как она должна запуститься один раз (например, когда элемент появляется в DOM). Будем плавно показывать элемент .tile. Нужно убедиться, что он использует стили из первого keyframe нашей анимации сразу после появления (например, он должен начинаться с opacity: 0).

Также нужно, чтобы после анимации (например, opacity: 1) .tile сохранил стили, объявленные в последнем keyframe анимации.

Самое очевидно, что хочется сделать – задать animation-iteration-count в значение 1. Но к сожалению, все сложнее.

Если число итераций равно 1, элемент начинает со стилей по умолчанию, а затем внезапно принимает стили, объявленные в начале анимации (0%). Далее запускается анимация, но после достижения 100% элемент резко возвращается к первичным стилям.

Из-за этого элемент на экране начинает мигать, резко пропадая и возвращаясь к opacity 100%. Вместе с animation-delay проблема становится еще очевиднее.

Режим заполнения анимации

Для решения описанной выше проблемы разработано свойство animation-fill-mode. animation-fill-mode: both заставляет элемент использовать стили из первого keyframe (0%) анимации сразу, как только применяется анимация (даже если есть animation-delay).

После завершения анимации элемент будет использовать стили из последнего keyframe анимации.

Если ничего непонятно, не переживайте. Если хотите подробнее изучить animation-fill-mode, у Codrops есть полезный разбор.

Новый CSS для нашего элемента .tile:

.tile { // duration | timing-function | fill-mode | name animation: .3s ease-in-out both fade-in;
}

Заметка: нам не нужно подключать iteration-count 1, так как значение по умолчанию равно 1

Keyframes

Наш fade-in @keyframes:

@keyframes fade-in { 0% { opacity: 0; transform: scale(0); } 100% { opacity: 1; transform: scale(1); }
}

Цикл Sass

Наш обновленный Sass цикл:

@for $i from 1 through 12 { .tile { // :nth-child(1-12) &:nth-child(#{$i}) { // Delay the animation. Delay increases as items loop. animation-delay: $i * (.03s); } }
}

Конечное демо:

В отличие от прелоадер анимации, здесь тайминг анимации не совсем математический. Важнее добиться плавного эффекта.

Добавление вращения и эффекта spring-back

Наша анимация выглядит хорошо, но пара CSS трюков сделает ее еще лучше. Добавим к @keyframes правилам несколько трансформаций.

@keyframes fade-in { 0% { opacity: 0; transform: translateX(50%) scale(0) rotateZ(-60deg); } 100% { opacity: 1; transform: translateX(0) scale(1) rotateZ(0deg); }
}

Так наш элемент .tile будет анимироваться справа с небольшим поворотом.

Далее добавим кастомную animation-timing-function с cubic-bezier. Так наша анимация дойдет до 100% и затем отпрыгнет назад.

.tile { animation: .4s cubic-bezier(.25, .25, .25, 1.25) forwards fade-in;
}

Я запомнил свою формулу cubic-bezier (.25, .25, .25, 1.25) и часто использую для анимации и переходов. Можете поиграться с окончательными цифрами, чтобы увеличить или уменьшить то, как далеко после 100% анимация пойдет.

Обновленное демо:

Поворот элементов с помощью 3D трансформаций

Вот тут становится немного смешно. Если что-то можно сделать, не значит, что нужно. Но все же, посмотрите этот вариант с переворотом:

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

Поддержка в браузерах и прогрессивное улучшение

Использованные нами CSS свойства (animation, nth-child и т.д.) хорошо поддерживаются современными браузерами и изящно деградируют. Если анимация не поддерживается, элементы появятся мгновенно.

Поддержка в браузерах по данным can I use:

Анимация отлично работает до IE9. Там анимации нет, а элементы появляются мгновенно (что нормально).

Другой виновник – это старые мобильные браузеры.

Mobile Safari требует вендорный префикс –webkit в относительно свежей версии (например, v8 2014), а в старых версиях (v5.1 2012) поддержка частичная.

С устройствами Android история схожая. В Android до v4 (вышла в 2011) поддержка частичная. После v4 анимация поддерживается с вендорным префиксом –webkit.

Важно помнить об этом на перспективу. Если анимация не поддерживается, страница все еще работает. Отсутствует всего лишь анимация. Все деградирует красиво и изящно.

Автор: Glenn McComb

Источник: https://glennmccomb.com/

Редакция: Команда webformyself.