Анимация — несколько рисунков, показанных последовательно, чтобы создать иллюзию движения. Анимации привлекают внимание пользователей и помогают сделать интерфейсы понятными. Мы подготовили перевод статьи, чтобы разобраться, как контролировать движения и переходы в CSS.
Есть два способа анимировать элементы в CSS: свойства animationи transition.
Свойство animationпозволяет изменять свойства элемента в течение определённого периода, а transitionопределяет, как элемент меняется за определённый период.
Возникает вопрос, в чём же разница. Для animation нужны @keyframes, то есть требуется определить точки начала и конца изменений. Ключевые кадры используют для пошаговых анимаций. Более простые анимации создают при помощи transition. В этом случае движение запускается по определённому сигналу, например, по клику или наведению курсора. Свойства animation и transition управляют продолжительностью, задержкой, итерациями движения.
Что такое временные функции
Это функции, которые определяют, как веб-элементы работают в каждом кадре анимации: animation-timing-function и transition-timing-function действуют и как автономные свойства CSS, и как значения свойств animation и transition .
Поэтому свойство animation может выглядеть так:
animation: <name> <duration> <timing-function>;
Или так:
animation-name: name; animation-duration: 500ms; animation-timing-function: ease;
То же самое со свойством transition:
transition: <property> <duration> <timing-function>;
Или:
transition-property: transform; transition-duration: 500ms; transition-timing-function: ease;
Transition-timing-function
Transition-timing-function определяет кривую скорости эффекта перехода. Кривая — соединение множества точек. Так что каждый период transition разделён на несколько точек.
Всего в CSS шесть transition-timing-functions:
lineareaseease-inease-outease-in-outcubic-bezier()
Они служат для создания плавных переходов, поэтому называются функциями плавности.
Функция linear
Анимации с linear двигаются с постоянной скоростью. При этом с начала до конца никаких изменений внутри функции нет, так что кривая скорости тут, скорее, прямая.

Функция ease
Анимации с этой функцией начинаются медленно, ускоряются, потом снова замедляются до стартовых значений. Эта функция используется по умолчанию.

Для понимания устроим гонки, участвуют ease и linear:

Используем простую разметку HTML:
<body> <div class="container"> <div class="rockets rocket-1"><img src="Rocket1.png"></div> <div class="rockets rocket-2"><img src="Rocket2.png"></div> </div> </body>
Добавляем CSS:
body { margin: 0px; padding: 0px; } *{ box-sizing: border-box; } .container{ width: 100%; height: 300px; background: rgba(224,214,233, 0.5) } .rockets{ width: 500px; height: 100px; transition-duration: 2s; transition-property: transform; display: flex; align-items: center; } .rockets img{ height: 100px; border-right: 1px solid red;/*To track the speed easily*/ }
Добавляем анимацию. Триггером делаем :hover.
.container:hover .rockets{ transform: translateX(500px); } .rocket-1{ transition-timing-function: linear; } .rocket-2{ transition-timing-function: ease; }
Итог:

На первый взгляд, кажется, что ease быстрее, чем linear, но в обоих случаях animation-duration одинаково. Если присмотреться, обе анимации заканчиваются в одной точке. В гонке ничья.
Функции ease-in, ease-out и ease-in-out
Анимации с функцией ease-in начинаются медленно, ускоряются к концу. Функция ease-out вызывает обратный эффект: быстрый старт и медленный финал.

Ease-in-out анимания начинается медленно, ускоряется в середине, завершается медленно. Похоже на ease, но только с симметричной кривой скорости.

Снова гоночки:

.rocket-1{ transition-timing-function: ease-in; } .rocket-2{ transition-timing-function: ease-out; } .rocket-3{ transition-timing-function: ease-in-out }

Функция cubic-bezier()
Все функции плавности основаны на кубической кривой Безье, которая определяется контрольными точками. Даже linear — кривая Безье с двумя контрольными точками.
cubic-bezier(P1,P2,P3,P4)
Точки P1 и P3 должны быть в пределах от 0 до 1. Точки P2 и P4 могут быть с любыми значениями, в том числе отрицательными. Удобнее создавать все точки в пределах от -1 до 1, чтобы анимации не дёргались.
Зададим рандомные значения для точек.
.rocket-1{ transition-timing-function: cubic-bezier(.66,.39,.21,.67); } .rocket-2{ transition-timing-function: cubic-bezier(1,-0.42,.42,-0.39); } .rocket-3{ transition-timing-function: cubic-bezier(.57,1.34,.21,0); }
Наводим курсор и смотрим, что вышло.

Значения для контрольных точек можно задавать вручную, но это довольно долго. Есть два способа выбрать идеальную скорость cubic-bezier(): инструменты разработчика и генератор.
Используем инструменты проверки
Задаём анимированному элементу любую временную функцию. Открываем инструменты разработчика. У нас скриншоты из Chrome.
Если вы используете сокращённые свойства animation или transition, рядом с названием свойства найдёте значок раскрывающегося списка. Кликаем, раскрываем список значений — среди них будет время. В другом случае это свойство отображается отдельно.

Рядом с названием временно функции есть значок кривой, который открывает редактор Безье.

Кликаем и регулируем cubic-bezier(). В этом помогает визуализатор.

Когда найдёте подходящую кривую скорости, скопируйте cubic-bezier() и вставьте в проект.

Используем генератор
Заходим на cubic-bezier.com — это инструмент, которые создаёт кривые скорости. Поиграйте с настройками, пока не найдёте подходящий вариант. В это помогут предварительный просмотр и сравнение с функциями по умолчанию. Скопируйте и вставьте в проект.

Animation-timing-function
Функция animation-timing-function определяет кривую скорости. Да, тоже. Свойство animation делится на @keyframes, которые работают как FPS у камер. Animation-timing-function работает с любой функцией плавности, а также с другими функциями: step-end, step-start и steps.
Когда со свойством animation используют функция плавности, нужно добавлять @keyframes с начальной и конечной точками. Посмотрим, как это работает. Сделаем анимации скролла, элементы будут появляться при прокрутке вниз.

Начинаем:
<section class="container"> <h2>Ease-in, Ease-out, and Ease-in-out</h2> <div class="text-container"> <div class="text-box reveal box-3"> <h3>Ease-in</h3> <p>Random text</p> </div> <div class="text-box reveal box-4"> <h3>Ease-out</h3> <p>Random text</p> </div> <div class="text-box reveal box-5"> <h3>Ease-in-out</h3> <p>Random text</p> </div> </div> </section>
Стилизуем и присваиваем каждому элементу animation:
.active.box-1 { animation: box-1 1s ease; } .active.box-2 { animation: box-2 1s linear; } .active.box-3 { animation: box-3 1s ease-in; } .active.box-4 { animation: box-4 1s ease-out; } .active.box-5 { animation: box-5 1s ease-in-out; } .active.box-6 { animation: box-6 1s cubic-bezier(.66,.39,.21,.67); } .active.box-7 { animation: box-7 1s cubic-bezier(1,-0.42,.42,-0.39); } .active.box-8 { animation: box-8 1s cubic-bezier(.57,1.34,.21,0); }
Все анимированные элементы сопровождаем классом reveal, который спрячет их до запуска анимации.
.reveal { position: relative; opacity: 0; } .reveal.active { opacity: 1; }
Анимации прокрутки контролируем функцией JavaScript:
function reveal() { var reveals = document.querySelectorAll(".reveal"); for (var i = 0; i < reveals.length; i++) { var windowHeight = window.innerHeight; var elementTop = reveals[i].getBoundingClientRect().top; var elementVisible = 150; if (elementTop < windowHeight - elementVisible) { reveals[i].classList.add("active"); } else { reveals[i].classList.remove("active"); } } } window.addEventListener("scroll", reveal);
Условие запуска анимации — появление элемента при скролле страницы. Эта функция отслеживает дистанцию прокрутки элемента, elementTop, пока скролл не достигнет точки, в которой элемент должен быть видимым, elementVisible.
getBoundingClientRect().top — расстояние от верхней точки области просмотра, window.innerHeight — высота области просмотра.
Далее@keyframes:
@keyframes box-1 { 0% { transform: translateY(100px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes box-2 { 0% { transform: translateY(100px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes box-3 { 0% { transform: translateY(100px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes box-4 { 0% { transform: translateY(100px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes box-5 { 0% { transform: translateY(100px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes box-6 { 0% { transform: translateY(100px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes box-7 { 0% { transform: translateY(100px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes box-8 { 0% { transform: translateY(100px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } }
Функции step-end, step-start и steps()
Эти функции разбивают анимацию на равные части или шаги: step-end запускает анимацию после первого @keyframe и пропускает шаг в конце, step-start действует наоборот. Вот как это работает:
.rockets{ width: 500px; height: 100px; animation-duration: 2s; animation-name: flight; animation-iteration-count: infinite; animation-direction: alternate-reverse; display: flex; align-items: center; } .rocket-1{ animation-timing-function: step-end; } .rocket-2{ animation-timing-function: step-start; } @keyframes flight{ 0%{transform: none;} 25%{transform: translateX(125px);} 50%{transform: translateX(250px);} 75%{transform: translateX(375px);} 100%{transform: translateX(500px);} }
Итог:

Ракета с step-start переходит к первому @keyframe, как только начинается анимация.
Для усложнения используем steps(), чтобы указать количество шагов в анимации:
.rocket-1{ animation-timing-function: steps(5); } .rocket-2{ animation-timing-function: steps(10); } .rocket-3{ animation-timing-function: steps(20); } @keyframes flight{ 0%{transform: none;} 100%{transform: translateX(500px);} }
Итог:

Ключевые слова, которые можно использовать со steps() в дополнение к количеству шагов:
jump-startjump-endjump-bothjump-none
Jump-start и jump-end работают так же, как step-start и step-end; jump-both — анимация пропускает шаг с обоих концов; jump-none— анимация не пропускает ни одного шага, каждый шаг равномерно распределён по продолжительности.
.rocket-1{ animation-timing-function: steps(5,jump-end); } .rocket-2{ animation-timing-function: steps(5,jump-start); } .rocket-3{ animation-timing-function: steps(5,jump-both); } .rocket-4{ animation-timing-function: steps(5,jump-none); } @keyframes flight{ 0%{transform: none;} 100%{transform: translateX(500px);} }
Результат:

Глобальные временные функции
Работают для всех свойств CSS, в том числе:
inherit: даёт дочернему элементу наследуемые свойства родительского. Если свойства не наследуются, то возвращаются кinitial.initial: кажется, чтоinitial— способ использовать дефолтные значения свойств, но это не всегда так. У временных функцийinitialсовпадает с дефолтным значением ease.revert: устанавливает свойства элемента как дефолтные CSS свойства браузера.unset: похоже наrevert, но с дополнениями. Влияет на наследуемые и ненаследуемые свойства.
Свойства выше — наследуемые. К сожалению, animation-timing-function и transition-timing-function не наследуются.
Итак, если свойство наследуется, unset присваивает ему значение inherit. Если нет — свойству присваивается значение initial.
Бонус: animation-delay и transition-delay
Свойство -delay можно использовать с временными функциями: animation-delay и transition-delay откладывают старт анимации. Добавить свойство можно при помощи сокращения:
animation: <name> <duration> <timing-function> <delay>; transition: <property> <duration> <timing-function> <delay>;
Порядок свойств неважен, вторым значением времени в объявлении всегда будет свойство delay. Если у нас такое объявление:
transition: transform 2s 1s ease;
Тогда transition-delay равняется 1s. Это же относится animation.
Покажем на примере:
.rocket-1{ transition-timing-function: cubic-bezier(.66,.39,.21,.67); transition-delay: 500ms; } .rocket-2{ transition-timing-function: cubic-bezier(.66,.39,.21,.67); transition-delay: 700ms; } .rocket-3{ transition-timing-function: cubic-bezier(.66,.39,.21,.67); transition-delay: 1s; }

Такое свойство можно использовать для загрузки разных разделов веб-страницы без необходимости определять разные animation или transition для каждого раздела.

Курсы по программированию для получения новой специальности или повышения:
Топ бесплатных курсов и занятий:
