Skip to content

Latest commit

 

History

History
450 lines (300 loc) · 24.1 KB

File metadata and controls

450 lines (300 loc) · 24.1 KB

CSS-анимации

CSS позволяет создавать простые анимации без использования JavaScript.

JavaScript может быть использован для управления такими CSS-анимациями. Это позволяет делать более сложные анимации, используя небольшие кусочки кода.

CSS-переходы [#css-transition]

Идея CSS-переходов проста: мы указываем, что некоторое свойство должно быть анимировано, и как оно должно быть анимировано. А когда свойство меняется, браузер сам обработает это изменение и отрисует анимацию.

Всё что нам нужно, чтобы начать анимацию - это изменить свойство, а дальше браузер сделает плавный переход сам.

Например, CSS-код ниже анимирует трёхсекундное изменение background-color:

.animated {
  transition-property: background-color;
  transition-duration: 3s;
}

Теперь, если элементу присвоен класс .animated, любое изменение свойства background-color будет анимироваться в течение трёх секунд.

Нажмите кнопку ниже, чтобы анимировать фон:

<button id="color">Нажми меня</button>

<style>
  #color {
    transition-property: background-color;
    transition-duration: 3s;
  }
</style>

<script>
  color.onclick = function() {
    this.style.backgroundColor = 'red';
  };
</script>

Существует 4 свойства для описания CSS-переходов:

  • transition-property -- свойство перехода
  • transition-duration -- продолжительность перехода
  • transition-timing-function -- временная функция перехода
  • transition-delay -- задержка начала перехода

Далее мы рассмотрим их все, а сейчас ещё заметим, что есть также общее свойство transition, которое позволяет задать их одновременно в последовательности: property duration timing-function delay, а также анимировать несколько свойств одновременно.

Например, у этой кнопки одновременно анимируются два свойства -- color и font-size:

<button id="growing">Нажми меня</button>

<style>
#growing {
*!*
  transition: font-size 3s, color 2s;
*/!*
}
</style>

<script>
growing.onclick = function() {
  this.style.fontSize = '36px';
  this.style.color = 'red';
};
</script>

Теперь рассмотрим каждое свойство анимации по отдельности.

transition-property

В transition-property записывается список свойств, изменения которых необходимо анимировать, например: left, margin-left, height, color.

Анимировать можно не все свойства, но многие из них. Значение свойства all означает "анимируй все свойства".

transition-duration

В transition-duration можно определить, сколько времени займёт анимация. Время должно быть задано в формате времени CSS: в секундах s или миллисекундах ms.

transition-delay

В transition-delay можно определить задержку перед началом анимации. Например, если transition-delay: 1s, тогда анимация начнётся через 1 секунду после изменения свойства.

Отрицательные значения также допустимы. В таком случае анимация начнётся с середины. Например, если transition-duration равно 2s, а transition-delay -- -1s, тогда анимация займёт одну секунду и начнётся с середины.

Здесь приведён пример анимации, сдвигающей цифры от 0 до 9 с использованием CSS-свойства transform со значением translate:

[codetabs src="digits"]

Свойство transform анимируется следующим образом:

#stripe.animate {
  transform: translate(-90%);
  transition-property: transform;
  transition-duration: 9s;
}

В примере выше JavaScript-код добавляет класс .animate к элементу, после чего начинается анимация:

stripe.classList.add('animate');

Можно начать анимацию "с середины", с определённого числа, например, используя отрицательное значение transition-delay, соответствующее необходимому числу.

Если вы нажмёте на цифру ниже, то анимация начнётся с текущей секунды:

[codetabs src="digits-negative-delay"]

JavaScript делает это с помощью нескольких строк кода:

stripe.onclick = function() {
  let sec = new Date().getSeconds() % 10;
*!*
  // например, значение -3s здесь начнёт анимацию с третьей секунды
  stripe.style.transitionDelay = '-' + sec + 's';
*/!*
  stripe.classList.add('animate');
};

transition-timing-function

Временная функция описывает, как процесс анимации будет распределён во времени. Будет ли она начата медленно и затем ускорится или наоборот.

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

Это свойство может принимать два вида значений: кривую Безье или количество шагов. Давайте начнём с кривой Безье, как с наиболее часто используемой.

Кривая Безье

Временная функция может быть задана, как кривая Безье с 4 контрольными точками, удовлетворяющими условиям:

  1. Первая контрольная точка: (0,0).
  2. Последняя контрольная точка: (1,1).
  3. Для промежуточных точек значение x должно быть 0..1, значение y может принимать любое значение.

Синтаксис для кривых Безье в CSS: cubic-bezier(x2, y2, x3, y3). Нам необходимо задать только вторую и третью контрольные точки, потому что первая зафиксирована со значением (0,0) и четвёртая - (1,1).

Временная функция описывает то, насколько быстро происходит анимации во времени.

  • Ось x - это время: 0 - начальный момент, 1 - последний момент transition-duration.
  • Ось y указывает на завершение процесса: 0 - начальное значение свойства, 1 - конечное значение.

Самым простым примером анимации является равномерная анимация с линейной скоростью. Она может быть задана с помощью кривой cubic-bezier(0, 0, 1, 1).

Вот как выглядит эта "кривая":

...Как мы видим, это прямая линия. Значению времени (x) соответствует значение завершённости анимации (y), которое равномерно изменяется от 0 к 1.

В примере ниже поезд "едет" слева направо с одинаковой скоростью (нажмите на поезд):

[codetabs src="train-linear"]

В свойстве transition указана следующая кривая Безье:

.train {
  left: 0;
  transition: left 5s cubic-bezier(0, 0, 1, 1);
  /* JavaScript устанавливает свойство left равным 450px */
}

...А как показать замедляющийся поезд?

Мы можем использовать другую кривую Безье: cubic-bezier(0.0, 0.5, 0.5, 1.0).

Её график:

Как видим, анимация начинается быстро: кривая быстро поднимается вверх, и затем все медленнее и медленнее.

Ниже временная функция в действии (нажмите на поезд):

[codetabs src="train"]

CSS:

.train {
  left: 0;
  transition: left 5s cubic-bezier(0, 0.5, 0.5, 1);
  /* JavaScript устанавливает свойство left равным 450px */
}

Есть несколько встроенных обозначений кривых Безье: linear, ease, ease-in, ease-out и ease-in-out.

linear это короткая запись для cubic-bezier(0, 0, 1, 1) -- прямой линии, которую мы видели раньше.

Другие названия -- это также сокращения для других cubic-bezier:

ease* ease-in ease-out ease-in-out
(0.25, 0.1, 0.25, 1.0) (0.42, 0, 1.0, 1.0) (0, 0, 0.58, 1.0) (0.42, 0, 0.58, 1.0)
ease, figure ease-in, figure ease-out, figure ease-in-out, figure

* -- используется по умолчанию, если не задана другая временная функция.

Для того, чтобы замедлить поезд, мы можем использовать ease-out:

.train {
  left: 0;
  transition: left 5s ease-out;
  /* transition: left 5s cubic-bezier(0, 0.5, 0.5, 1); */
}

Но получившийся результат немного отличается.

Кривая Безье может заставить анимацию "выпрыгивать" за пределы диапазона.

Контрольные точки могут иметь любые значения по оси y: отрицательные или сколь угодно большие. В таком случае кривая Безье будет скакать очень высоко или очень низко, заставляя анимацию выходить за её нормальные пределы.

В приведённом ниже примере код анимации:

.train {
  left: 100px;
  transition: left 5s cubic-bezier(0.5, -1, 0.5, 2);
  /* JavaScript sets left to 400px */
}

Свойство left будет анимироваться от 100px до 400px.

Но когда вы нажмёте на поезд, вы увидите следующее:

  • Сначала, поезд поедет назад: left станет меньше, чем 100px.
  • Затем он поедет вперёд, немного дальше, чем 400px.
  • И затем вернётся назад в значение 400px.

[codetabs src="train-over"]

Если мы взглянем на кривую Безье из примера, становится понятно поведение поезда.

Мы вынесли координату y для первой опорной точки ниже нуля и выше единицы для третьей опорной точки, поэтому кривая вышла за пределы "обычного" квадрата. Значения y вышли из "стандартного" диапазона 0..1.

Как мы знаем, ось y измеряет "завершённость процесса анимации". Значение y = 0 соответствует начальному значению анимируемого свойства и y = 1 -- конечному значению. Таким образом, y<0 делает значение свойства left меньше начального значения и y>1 -- больше конечного.

Это, конечно, "мягкий" вариант. Если значение y будут -99 и 99, то поезд будет гораздо сильнее "выпрыгивать" за пределы.

Как сделать кривую Безье необходимую для конкретной задачи? Существует множество инструментов.

  • К примеру, мы можем сделать это на сайте https://cubic-bezier.com.
  • Браузернные инструменты разработчика также имеют специальную поддержку для создания кривых Безье в CSS:
    1. Откройте инструменты разработчика при помощи key:F12 (Mac: key:Cmd+Opt+I).
    2. Выберете вкладку Elements, затем обратите внимание на под-панель Styles в правой стороне.
    3. Свойства CSS со словом cubic-bezier будут иметь иконку перед этим словом.
    4. Кликните по иконке, чтобы отредактировать кривую.

Шаги

Временная функция steps(количество шагов[, start/end]) позволяет разделить анимацию на шаги.

Давайте рассмотрим это на уже знакомом нам примере с цифрами.

Ниже представлен список цифр, без какой-либо анимации, который мы будем использовать в качестве основы:

[codetabs src="step-list"]

В HTML, вереница цифр заключена в <div id="digits"> фиксированной длины:

<div id="digit">
  <div id="stripe">0123456789</div>
</div>

Div-элемент #digit имеет фиксированную ширину и границу, поэтому он выглядит как красное окно.

Мы сделаем таймер: цифры будут появляться одна за другой, дискретно.

Чтобы добиться этого, мы скроем #stripe за пределами #digit, используя overflow: hidden, а затем, шаг за шагом будем сдвигать #stripe влево.

Всего будет 9 шагов, один шаг для каждой цифры:

#stripe.animate  {
  transform: translate(-90%);
  transition: transform 9s *!*steps(9, start)*/!*;
}

Первый аргумент временной функции steps(9, start) -- количество шагов. Трансформация будет разделена на 9 частей (10% каждая). Временной интервал также будет разделён на 9 частей, таким образом свойство transition: 9s обеспечивает нам 9 секунд анимации, что даёт по одной секунде на цифру.

Вторым аргументом является одно из ключевых слов: start или end.

start -- означает, что в начале анимации нам необходимо перейти на первый шаг немедленно.

В действии:

[codetabs src="step"]

Щелчок по цифре немедленно изменяет её на 1 (первый шаг), а затем изменяется в начале следующей секунды.

Анимация будет происходить так:

  • 0s -- -10% (первое изменение в начале первой секунды, сразу после нажатия)
  • 1s -- -20%
  • ...
  • 8s -- -90%
  • (на протяжении последней секунды отображается последнее значение).

Здесь первое изменение было немедленным из-за start в steps.

Альтернативное значение end означало бы, что изменения нужно применять не в начале, а в конце каждой секунды.

Анимация будет происходить так:

  • 0s -- 0
  • 1s -- -10% (первое изменение произойдёт в конце первой секунды)
  • 2s -- -20%
  • ...
  • 9s -- -90%

Пример step(9, end) в действии (обратите внимание на паузу перед первым изменением цифры):

[codetabs src="step-end"]

Существуют также некоторые заранее определённые сокращения для steps(...):

  • step-start -- то же самое, что steps(1, start). Оно означает, что анимация начнётся сразу и произойдёт в один шаг. Таким образом она начнётся и завершится сразу, как будто и нет никакой анимации.
  • step-end -- то же самое, что steps(1, end): выполнит анимацию за один шаг в конце transition-duration.

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

Событие: "transitionend"

Когда завершается анимация, срабатывает событие transitionend.

Оно широко используется для выполнения действий после завершения анимации, а также для создания последовательности анимаций.

Например, корабль в приведённом ниже примере начинает плавать туда и обратно по клику, каждый раз всё дальше и дальше вправо:

[iframe src="boat" height=300 edit link]

Анимация начинается с помощью функции go, которая вызывается заново каждый раз, когда переход заканчивается и меняется направление:

boat.onclick = function() {
  //...
  let times = 1;

  function go() {
    if (times % 2) {
      // плыть вправо
      boat.classList.remove('back');
      boat.style.marginLeft = 100 * times + 200 + 'px';
    } else {
      // плыть влево
      boat.classList.add('back');
      boat.style.marginLeft = 100 * times - 200 + 'px';
    }

  }

  go();

  boat.addEventListener('transitionend', function() {
    times++;
    go();
  });
};

Объект события transitionend содержит ряд полезных свойств:

event.propertyName : Имя свойства, анимация которого завершилась. Может быть полезным, если мы анимируем несколько свойств.

event.elapsedTime : Время (в секундах), которое заняла анимация, без учёта transition-delay.

Ключевые кадры

Мы можем объединить несколько простых анимаций вместе, используя CSS-правило @keyframes.

Оно определяет "имя" анимации и правила: что, когда и где анимировать. После этого можно использовать свойство animation, чтобы назначить анимацию на элемент и определить её дополнительные параметры.

Ниже приведён пример с пояснениями:

<div class="progress"></div>

<style>
*!*
  @keyframes go-left-right {        /* объявляем имя анимации: "go-left-right" */
    from { left: 0px; }             /* от: left: 0px */
    to { left: calc(100% - 50px); } /* до: left: 100%-50px */
  }
*/!*

  .progress {
*!*
    animation: go-left-right 3s infinite alternate;
    /* применить анимацию "go-left-right" на элементе
       продолжительностью 3 секунды
       количество раз: бесконечно (infinite)
       менять направление анимации каждый раз (alternate)
    */
*/!*

    position: relative;
    border: 2px solid green;
    width: 50px;
    height: 20px;
    background: lime;
  }
</style>

Существует множество статей про @keyframes, а также детальная спецификация.

Скорее всего, вам нечасто понадобится @keyframes, разве что на вашем сайте всё постоянно в движении.

Итого

CSS-анимации позволяют плавно, или не очень, менять одно или несколько свойств.

Они хорошо решают большинство задач по анимации. Также мы можем реализовать анимации через JavaScript, более подробно об этом - в следующей главе.

Ограничения CSS-анимаций в сравнении с JavaScript-анимациями:

+ Простые анимации делаются просто.
+ Быстрые и не создают нагрузку на CPU.
- JavaScript-анимации более гибкие. В них может присутствовать любая анимационная логика, как например "взорвать" элемент.
- Можно изменять не только свойства. Мы можем создавать новые элементы с помощью JavaScript для анимации.

Большинство анимаций может быть реализовано с использованием CSS, как описано в этой главе. А событие transitionend позволяет запускать JavaScript после анимации, поэтому CSS-анимации прекрасно интегрируются с кодом.

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