От автора: CSS анимация и в частности свойство transition – отличный способ плавно передвинуть объект из точки А в точку В. Все это хорошо работает, если вы двигаете предмет по прямой. Не важно, сколько раз вы согнете кривые Безье, у вас не получится заставить двигаться объект по кривой линии при помощи свойств animation и transition. Можно ошибиться с временной функцией, что приведет к эффекту пружины, движения относительно осей Х и У всегда будут одинаковыми.
Тем не менее, есть более легкий способ для создания такой натуральной анимации без помощи JavaScript – слоистая анимация. Используя два или более объекта для анимации, можно контролировать любой участок пути. Одну тайминг функцию можно применить к движению по оси Х, другую – к движению по оси У.
Проблема
Перед тем как мы перейдем к решению, давайте поближе рассмотрим проблему. CSS свойства animation и transition ограничивают нас движением только по прямой. Как это происходит? Всегда выбирается кратчайший путь от точки А до точки В. Отлично же – в большинстве случаев этого достаточно – однако мы не можем сказать CSS пройтись «красивым путем», а не «самым коротким».
Самый простой способ в CSS анимации от одной точки к другой (с аппаратным ускорением) это свойство transform со значением translate. Таким образом можно получить движение вдоль прямой. В @keyframes блоке ниже мы движемся вверх и вниз из точки (0, 0) в точку (100, -100), как в примере выше:
1 2 3 4 5 6 7 8 9 |
@keyframes straightLine { 50% { transform: translate3D(100px, -100px, 0); } } .dot { animation: straightLine 2.5s infinite linear; } |
Это несложно, но давайте остановимся. Чтобы понять, как решить нашу задачу, необходимо хотя бы визуально разбить нашу анимацию на части.
В 0% объект находится в точке (0, 0), на 50% мы используем translate3D(100px, -100px, 0) для перемещения в точку (100, -100), затем перемещаемся обратно. Перефразируя, мы двигаем объект на 100px вправо и затем на 100px вверх. Два перехода вместе двигают объект в верхний угол.
Решение: одна функция на ось
Так как же создать движение по кривой, как показано выше? Чтобы объект двигался не по прямой линии необходимо, чтобы скорость передвижения по осям Х и У была разной.
В предыдущих примерах везде использовалась функция linear, однако если обернуть наш объект в контейнер, то одну функцию можно применить для передвижения по Х, а другую для передвижения по У. Ниже мы используем ease-in для оси Х и ease-out для У:
Реализация: один объект на ось
К сожалению, мы не можем задать несколько свойств transform, применено будет только последнее. Так как же на самом деле совместить две анимации? Мы поместим один объект внутрь другого и запустим одну анимацию для контейнера, а другую для дочернего элемента.
Во всех примерах выше с двигающейся точкой вдоль кривой вы видели, как анимации подвергались два элемента, а контейнер был полностью прозрачен. Чтобы понять, как два объекта взаимодействуют между собой, и как получается движение по кривой, мы сделали видимым контейнер при помощи значения border-box:
Точка располагается внутри бокса и двигается вместе с ним по оси Х, в то время как сама точка перемещается по оси У. Если убрать рамку контейнера, то мы получим передвижение по кривой. Вместо использования двух объектов в HTML можно добавить псевдокласс. Если HTML выглядит так:
1 |
<div class="dot"></div> |
Можно добавить псевдокласс:
1 2 3 4 5 6 7 |
.dot { /* Контейнер. Перемещается по оси X */ } .dot::after { /* Отрисовывает точку и перемещает ее вдоль оси У */ } |
Нам нужно два отдельных блока анимации: один для оси Х, другой для У. Обратите внимание, как в первом блоке используется ease-in, а во втором ease-out:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
.dot { /* тут идет код… */ animation: xAxis 2.5s infinite ease-in; } .dot::after { /* Точка */ animation: yAxis 2.5s infinite ease-out; } @keyframes xAxis { 50% { animation-timing-function: ease-in; transform: translateX(100px); } } @keyframes yAxis { 50% { animation-timing-function: ease-out; transform: translateY(-100px); } } |
Добавим вендорные префиксы для WebKit браузеров и парочку кривых Безье вместо ease-in и ease-out, и у нас получится пример, показанный в начале статьи:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
.demo-dot { -webkit-animation: xAxis 2.5s infinite cubic-bezier(0.02, 0.01, 0.21, 1); animation: xAxis 2.5s infinite cubic-bezier(0.02, 0.01, 0.21, 1); } .demo-dot::after { content: ''; display: block; width: 20px; height: 20px; border-radius: 20px; background-color: #fff; -webkit-animation: yAxis 2.5s infinite cubic-bezier(0.3, 0.27, 0.07, 1.64); animation: yAxis 2.5s infinite cubic-bezier(0.3, 0.27, 0.07, 1.64); } @-webkit-keyframes yAxis { 50% { -webkit-animation-timing-function: cubic-bezier(0.02, 0.01, 0.21, 1); animation-timing-function: cubic-bezier(0.02, 0.01, 0.21, 1); -webkit-transform: translateY(-100px); transform: translateY(-100px); } } @keyframes yAxis { 50% { -webkit-animation-timing-function: cubic-bezier(0.02, 0.01, 0.21, 1); animation-timing-function: cubic-bezier(0.02, 0.01, 0.21, 1); -webkit-transform: translateY(-100px); transform: translateY(-100px); } } @-webkit-keyframes xAxis { 50% { -webkit-animation-timing-function: cubic-bezier(0.3, 0.27, 0.07, 1.64); animation-timing-function: cubic-bezier(0.3, 0.27, 0.07, 1.64); -webkit-transform: translateX(100px); transform: translateX(100px); } } @keyframes xAxis { 50% { -webkit-animation-timing-function: cubic-bezier(0.3, 0.27, 0.07, 1.64); animation-timing-function: cubic-bezier(0.3, 0.27, 0.07, 1.64); -webkit-transform: translateX(100px); transform: translateX(100px); } } |
Код приведет нас к тому, с чего мы начали.
Вы, должно быть, уже заметили, что во всех примерах мы использовали @keyframes анимацию. Однако это только потому, что нам нужно было двигать точку назад и вперед. Если необходимо передвинуть объект из точки А в точку В, слоистая анимация хорошо работает со свойством transition.
Если объект спозиционирован абсолютно, анимировать по кривой линии его можно при помощи свойств left и bottom. В таком случае вам понадобится только один объект, контейнер добавлять не надо. Тем не менее, есть причина, по которой следует избегать такой анимации: производительность такой анимации очень низкая, и она отрисовывается на каждом кадре заново. Слоистая же анимация с псевдоклассами и аппаратным ускорением свойства translate делает передвижение очень плавным, не снижая производительность.
Автор: Tobias
Источник: //tobiasahlin.com/
Редакция: Команда webformyself.