Главная » Статьи » Расширенная анимация CSS с использованием cubic-bezier()

Расширенная анимация CSS с использованием cubic-bezier()

Расширенная анимация CSS с использованием cubic-bezier()

От автора: имея дело со сложной анимацией CSS, есть тенденция создавать экспансивные @keyframes с большим количеством объявлений. Однако есть пара приемов, о которых я хочу поговорить, они могут помочь упростить задачу, оставаясь при этом ванильным CSS.

Несколько анимаций

Функции времени

Первый более широко используется и знаком многим, а второй менее распространен. Для этого могут быть веские причины — объединять анимации с помощью запятых относительно проще, чем разбираться в различных доступных нам функциях синхронизации и том, что они делают. Есть одна особенно удобная функция синхронизации, которая дает нам полный контроль над созданием пользовательской функций времени. Это cubic-bezier() и в этом посте я покажу вам ее силу и то, как ее можно использовать для создания чудесной анимации без особой сложности.

Начнем с базового примера, показывающего, как мы можем перемещать мяч в определённых направлениях, например, в форме бесконечности (∞):

Как видите, здесь нет сложного кода — только два ключевых кадра и функция cubic-bezier(). И все же мы получаем довольно сложную финальную анимацию бесконечной формы. Круто, правда? Давайте вникнем в это!

Функция cubic-bezier()

Начнем с официального определения: Кубическая функция ослабления Безье — это тип функции ослабления, определяемый четырьмя действительными числами, которые определяют две контрольные точки, P1 и P2, кубической кривой Безье, конечные точки P0 и P3 которой фиксированы в (0, 0) и (1, 1) соответственно. Координаты x P1 и P2 ограничены диапазоном [0, 1].

Расширенная анимация CSS с использованием cubic-bezier()

Вышеупомянутая кривая определяет, как выходные данные (ось y) будут вести себя в зависимости от времени (ось x). Каждая ось имеет диапазон [0, 1](или [0% 100%]). Если у нас есть анимация, которая длится две секунды (2s), то:

0 (0%) = 0s 1 (100%) = 2s

Если мы хотим анимировать left от 5px до 20px, то:

0 (0%) = 5px 1 (100%) = 20px

X, время, всегда ограничено [0 1]; однако Y может выходить за рамки [0 1]. Моя цель — отрегулировать P1 и P2, чтобы создать следующие кривые:

Расширенная анимация CSS с использованием cubic-bezier()

Вы можете подумать, что этого невозможно достичь, потому что, как указано в определении, P0 и P3 фиксированы (0,0) и (1,1) и это означает, что они не могут находиться на одной оси. Это правда, и мы воспользуемся некоторыми математическими приемами, чтобы «приблизительно» их «аппроксимировать».

Параболическая кривая

Давайте начнем со следующим определением: cubic-bezier(0,1.5,1,1.5). Это дает нам следующую кривую:

Расширенная анимация CSS с использованием cubic-bezier()

Наша цель — двигаться от (1,1) до (0,1) и делать то, что технически невозможно. Так что попробуем построить фейковою кривую.

Ранее мы говорили, что наш диапазон равен [0 1] (или [0% 100%]), поэтому давайте представим случай, когда 0% он очень близок к 100%. Если, например, мы хотим анимировать top от 20px (0%) до, 20.1px (100%) то мы можем сказать, что начальное и конечное состояния равны.

Хм, наш элемент вообще не двинется? Ну, он немного сдвинется, потому что значение Y превышает 20.1px( 100%). Но этого недостаточно, чтобы дать нам ощутимое движение:

Давайте обновим кривую и будем использовать cubic-bezier(0,4,1,4) вместо нее. Обратите внимание на то, что наша кривая стала намного выше, чем раньше:

Расширенная анимация CSS с использованием cubic-bezier()

Но все равно движения нет — даже если верхнее значение пересекает 3 (или 300%). Попробуем cubic-bezier(0,20,1,20):

Расширенная анимация CSS с использованием cubic-bezier()

Да! Он начал немного двигаться. Вы заметили изменение кривой каждый раз, когда мы увеличиваем значение? Это делает нашу точку (1,1) «визуально» ближе к тому моменту (0,1) когда мы уменьшаем масштаб, чтобы увидеть полную кривую, и это уловка.

Используя cubic-bezier(0,V,1,V) где V- какое-то очень большое значение, а начальное и конечное состояния очень близки (или почти равны), мы можем моделировать параболическую кривую. Пример стоит тысячи слов:

Я применил «волшебную» кубическую функцию Безье к анимации top, а также применил к ней линейную left. Это дает нам желаемую кривую.

Копаемся в математике

Для тех из вас, кто разбирается в математике, мы можем разбить это объяснение дальше. Кубический Безье можно определить по следующей формуле:

P = (1−t)³P0 + 3(1−t)²tP1 + 3(1−t)t²P2 + t³P3

Каждая точка определяется следующим образом : P0 = (0,0), P1 = (0,V), P2 = (1,V), и P3 = (1,1). Это дает нам две функции для координат x и y:

X(t) = 3(1−t)t² + t³ = 3t² - 2t³
Y(t) = 3(1−t)²tV +3(1−t)t²V + t³ = t³ - 3Vt² + 3Vt

V это наше большое значение и t находится в пределах допустимого диапазона [0 1]. Если мы рассмотрим наш предыдущий пример, Y(t) даст нам значение top в то время как X(t) — это время прогресса. Затем точки (X(t),Y(t)) будут определять нашу кривую.

Найдем максимальное значение Y(t). Для этого нам нужно найти значение t, которое нам даст Y’(t) = 0 (когда производная равна 0):

Y'(t) = 3t² - 6Vt + 3V

Y’(t) = 0 является квадратным уравнением. Я пропущу скучную часть и дам результат, который равен t = V — sqrt(V² — V).

Когда V имеет наибольшее значение, t будет равно 0.5. Значит, Y(0.5) = Max и X(0.5) будет равно 0.5. Это означает, что мы достигаем максимального значения в средней точке анимации, которая соответствует желаемой параболической кривой.

Кроме того, Y(0.5) даст нам, (1 + 6V)/8 и это позволит нам найти максимальное значение на основе V. И поскольку мы всегда будем использовать большое значение для V, мы можем упростить до 6V/8 = 0.75V.

Мы использовали V = 500 в последнем примере, поэтому максимальное значение будет равно 375(или 37500%), и мы получим следующее:

Исходное состояние (0): top: 200px

Конечное состояние (1): top: 199.5px

Разница -0.5px между 0 и 1. Назовем это увеличением . Для 375(или 37500%) у нас есть уравнение 375*-0.5px = -187.5px. Наш анимированный элемент достигает top: 12.5px( 200px — 187.5px) и дает нам следующую анимацию:

top: 200px (at 0% of the time ) → top: 12.5px (at 50% of the time) → top: 199.5px (at 100% of the time)

Или, выражаясь иначе:

top: 200px (at 0%) → top: 12.5px (at 50%) → top: 200px (at 100%)

Сделаем обратную логику. Какое значение Vмы должны использовать, чтобы наш элемент стал доступным top: 0px? Анимация будет 200px → 0px → 199.5px, значит надо от -200px перейти к 0px. Наш прирост всегда равен -0.5px. Максимальное значение будет равно 200/0.5 = 400, а это 0.75V = 400 значит V = 533.33.

Наш элемент касается вершины! Вот цифра, которая подводит итог только что проделанной нами математике:

Расширенная анимация CSS с использованием cubic-bezier()

Синусоидальная кривая

Мы воспользуемся почти тем же трюком, чтобы создать синусоидальную кривую, но с другой формулой. На этот раз мы будем использовать cubic-bezier(0.5,V,0.5,-V). Как и раньше, давайте посмотрим, как будет развиваться кривая, когда мы увеличим значение:

Расширенная анимация CSS с использованием cubic-bezier()

Я думаю, вы, наверное, уже поняли идею. Использование большого значения для V приближает нас к синусоидальной кривой.

Вот еще один пример с непрерывной анимацией — настоящая синусоидальная анимация!

Математика

Давайте займемся математикой! Следуя той же формуле, что и раньше, мы получим следующие функции:

X(t) = 3/2(1−t)²t + 3/2(1−t)t² + t³ = (3/2)t - (3/2)t² + t³
Y(t) = 3(1−t)²tV - 3(1−t)t²V + t³ = (6V + 1)t³ - 9Vt² + 3Vt

На этот раз нам нужно найти минимальное и максимальное значения для Y(t). Y’(t) = 0 даст нам два решения. После решения для этого:

Y'(t) = 3(6V + 1)t² - 18Vt + 3V = 0

…мы получили:

t' = (3V + sqrt(3V² - V))/(6V + 1)
t''= (3V - sqrt(3V² - V))/(6V + 1)

Для большого значения у V нас есть t’=0.211 и t»=0.789. Это означает, что Y(0.211) = Max и Y(0.789) = Min. Это также означает, что X(0.211)= 0.26 и X(0.789) = 0.74. Другими словами, мы достигаем максимума в 26% случаев и минимума в 74% случаев.

Y(0.211) равно 0.289V а Y(0.789) равно -0.289V. Мы получили эти значения с некоторым округлением, учитывая, что Vочень большие.

Наша синусоидальная кривая также должна пересекать ось x (или Y(t) = 0) в половине случаев (или X(t) = 0.5). Чтобы доказать это, мы используем вторую производную от Y(t)- которая должна быть равна 0 и Y»(t) = 0.

Y''(t) = 6(6V + 1)t - 18V = 0

Решением является 3V/(6V + 1), и для большого значения V результатом будет 0.5. Это дает нам Y(0.5) = 0 и X(0.5) = 0.5 что подтверждает, что наша кривая пересекает точку (0.5,0).

Теперь давайте рассмотрим предыдущий пример и попытаемся найти значение V, которое вернет top: 0%. У нас есть:

Исходное состояние (0): top: 50%

Конечное состояние (1): top: 49.9%

Увеличение : -0.1%

Нам нужно от -50% достичь значения top: 0%, при 0.289V*-0.1% = -50% дает нам V = 1730.10.

Как видите, наш элемент касается верхней части и исчезает внизу, потому что у нас есть следующая анимация:

top: 50% → top: 0% → top: 50% → top: 100% → top: 50% → and so on ...

Цифра для подведения итогов расчета:

Расширенная анимация CSS с использованием cubic-bezier()

И пример, иллюстрирующий все кривые вместе:

Да вы видите четыре кривых! Если вы присмотритесь, вы заметите, что я использую две разные анимации: одна идет 49.9% (с шагом -0.01%), а другая 50.1% (с шагом +0.01%). Изменяя знак увеличения, мы контролируем направление кривой. Мы также можем контролировать другие параметры кубической кривой Безье (но V, должно оставаться большим значением), чтобы создавать больше вариаций одних и тех же кривых.

И ниже интерактивная демонстрация:

Возвращаясь к нашему примеру

Вернемся к нашему первоначальному примеру шара, движущегося в форме символа бесконечности. Я просто объединил две синусоидальные анимации, чтобы заставить их работать.

Если мы объединим то, что мы делали ранее, с концепцией множественной анимации, мы можем получить потрясающие результаты. Это снова начальный пример, на этот раз в виде интерактивной демонстрации. Измените значения и увидите волшебство:

Давайте пойдем дальше и добавим немного CSS Houdini. Мы можем анимировать сложное преобразования благодаря @property (но на данный момент CSS Houdini ограничен поддержкой Chrome и Edge).

Какие рисунки с его помощью можно делать? Вот несколько, что мне удалось сделать:

Расширенная анимация CSS с использованием cubic-bezier()

А вот анимация:

И версия без CSS Houdini:

Из этих примеров можно сделать несколько выводов:

Каждый ключевой кадр определяется с использованием только одного объявления, содержащего увеличение.

Положение элемента и анимация независимы. Мы можем легко разместить элемент в любом месте без необходимости настраивать анимацию.

Мы не производили расчетов. Нет множества углов или значений пикселей. Нам нужно только малое значение в ключевом кадре и большое значение функции cubic-bezier().

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

А как насчет перехода?

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

Вот что я сделал без ключевых кадров:

Марио прыгает благодаря параболической кривой. Для создания анимации прыжка при наведении курсора нам не нужны были ключевые кадры. Синусоидальная кривая отлично справится со всей работой.

Вот еще одна версия Марио, на этот раз с использованием CSS Houdini. И да, он все еще прыгает благодаря параболической кривой:

Для удобства вот более интересные эффекты наведения без ключевых кадров (опять же, только для Chrome и Edge):

Вот и всё!

Теперь у вас есть несколько волшебных кривых cubic-bezier() и математика, стоящая за ними. Преимущество, конечно же, заключается в том, что такие пользовательские функции синхронизации позволяют нам создавать фантастические анимации без сложных ключевых кадров, к которым мы обычно стремимся.

Я понимаю, что не все разбираются в математике, и это нормально. Есть инструменты, которые могут помочь, например Ceaser Мэтью Лейна, который позволяет вам перетаскивать точки кривой, чтобы получить то, что вам нужно. И, если вы еще не добавили его в закладки, cubic-bezier.com — еще один полезный ресурс. Если вы хотите поиграть с кубиком Безье за пределами мира CSS, я рекомендую desmos, где вы можете увидеть некоторые математические формулы.

Независимо от того, как вы получаете свои cubic-bezier(), надеюсь, теперь вы имеете представление об их силе и о том, как они могут помочь в создании более приятного кода.

Автор: Temani Afif

Источник: css-tricks.com

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

Читайте нас в Telegram, VK, Яндекс.Дзен