От автора: в этой статье я покажу, как создается плавная анимация 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.