Улучшения API веб-анимации в Chromium 84

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

Кевин Эллис
Kevin Ellis

Опубликовано: 27 мая 2020 г.

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

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

В то время как Firefox и Safari уже реализовали полный набор функций спецификации, Chromium 84 добавляет в Chrome и Edge множество ранее неподдерживаемых функций, обеспечивая кроссбраузерную совместимость.

API веб-анимации впервые появился в Chromium в версии 36 в июле 2014 года. Теперь спецификация будет завершена в версии 84, запуск которой запланирован на июль 2020 года.
Долгая история API веб-анимации в Chromium.

Начиная

Создание анимации с помощью API веб-анимации должно показаться вам очень знакомым, если вы использовали правила @keyframe . Сначала вам нужно создать объект Keyframe. Вот как это может выглядеть в CSS:

@keyframes openAnimation {
  0% {
    transform: scale(0);
  }
  100% {
    transform: scale(1);
  }
}

В JavaScript это будет выглядеть так :

const openAnimation = [
  { transform: 'scale(0)' },
  { transform: 'scale(1)' },
];

Где задаются параметры анимации в CSS:

.modal {
  animation: openAnimation 1s 1 ease-in;
}

вы бы установили в JS:

document.querySelector('.modal').animate(
    openAnimation, {
      duration: 1000, // 1s
      iterations: 1, // single iteration
      easing: 'ease-in' // easing function
    }
);

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

Помимо element.animate()

Однако с обновлением API веб-анимации больше не ограничивается анимациями, созданными с помощью element.animate() . Мы также можем управлять анимациями и переходами CSS.

getAnimations() — это метод, который возвращает все анимации элемента независимо от того, был ли он создан с помощью element.animate() или с помощью правил CSS (анимация или переход CSS). Вот пример того, как это выглядит:

Сначала вы "get" ключевые кадры для перехода, чтобы определить, откуда мы переходим. Затем вы создаете две новые анимации непрозрачности, включающие эффект перекрестного затухания. После завершения перекрестного затухания вы удаляете копию.

Как организовать анимацию с помощью обещаний

В Chromium 84 теперь есть два метода, которые можно использовать с обещаниями: animation.ready и animation.finished .

  • animation.ready позволяет дождаться вступления в силу отложенных изменений (то есть переключения между методами управления воспроизведением, такими как воспроизведение и пауза).
  • animation.finished предоставляет средства для выполнения пользовательского кода JavaScript после завершения анимации.

Продолжая наш пример, создадим организованную цепочку анимации с помощью animation.finished . Здесь у вас есть вертикальное преобразование ( scaleY ), за которым следует горизонтальное преобразование ( scaleX ), за которым следует изменение непрозрачности дочернего элемента:

Применение преобразований и непрозрачности к открывающемуся модальному элементу. Смотрите демо на Codepen
const transformAnimation = modal.animate(openModal, openModalSettings);
transformAnimation.finished.then(() => { text.animate(fadeIn, fadeInSettings)});

Мы связали эти анимации с помощью animation.finished.then() перед выполнением следующего набора анимаций в цепочке. Таким образом, анимации появляются по порядку, и вы даже применяете эффекты к разным целевым элементам с разными наборами параметров (такими как скорость и простота).

В CSS это было бы обременительно для воссоздания, особенно при применении уникальных, но последовательных анимаций к нескольким элементам. Вам пришлось бы использовать @keyframe , отсортировать правильные проценты времени для размещения анимаций и использовать animation-delay перед запуском анимаций в последовательности.

Пример: воспроизведение, пауза и перемотка назад

Что может открываться, должно закрываться! К счастью, начиная с Chromium 39 , API веб-анимации предоставил нам возможность воспроизводить, приостанавливать и отменять анимацию.

Вы можете взять ранее показанную анимацию и придать ей плавную, обратную анимацию при повторном нажатии кнопки с помощью .reverse() . Таким образом, вы можете создать более плавное и контекстное взаимодействие для нашего модального окна.

Пример модального открытия и закрытия при нажатии кнопки. Смотрите демо на CodePen

Что вы можете сделать, так это создать две анимации с ожиданием воспроизведения ( openModal и встроенное преобразование непрозрачности), а затем приостановить одну из анимаций, отложив ее до завершения другой. Затем вы можете использовать обещания, чтобы дождаться завершения каждой из них перед воспроизведением. Наконец, вы можете проверить, установлен ли флаг, а затем отменить каждую анимацию.

Пример: Динамическое взаимодействие с частичными ключевыми кадрами

Пример ретаргетинга, где щелчок мыши настраивает анимацию на новое место. Смотрите демо на CodePen
selector.animate([{transform: `translate(${x}px, ${y}px)`}],
    {duration: 1000, fill: 'forwards'});

В этом примере есть только один ключевой кадр и не указано начальное положение. Это пример использования частичных ключевых кадров . Обработчик мыши делает здесь несколько вещей: он устанавливает новое конечное положение и запускает новую анимацию. Новое начальное положение выводится из текущего базового положения.

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

Улучшения производительности с заменяемыми анимациями

При создании анимаций на основе событий, таких как 'mousemove' , каждый раз создается новая анимация, что может быстро потреблять память и снижать производительность. Для решения этой проблемы в Chromium 83 были введены заменяемые анимации, позволяющие автоматическую очистку, когда готовые анимации помечаются как заменяемые и автоматически удаляются, если заменяются другой готовой анимацией. Рассмотрим следующий пример:

След кометы анимируется при движении мыши. Смотрите демо на CodePen
elem.addEventListener('mousemove', evt => {
  rectangle.animate(
    { transform: translate(${evt.clientX}px, ${evt.clientY}px) },
    { duration: 500, fill: 'forwards' }
  );
});

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

  1. Анимация завершена.
  2. Выше в композитном порядке есть одна или несколько анимаций, которые также завершены.
  3. Новые анимации анимируют те же свойства.

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

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

  • animation.replaceState предоставляет средства отслеживания того, является ли анимация активной, сохраненной или удаленной.
  • animation.commitStyles() обновляет стиль элемента на основе базового стиля вместе со всеми анимациями элемента в составном порядке.
  • animation.persist() помечает анимацию как незаменяемую.

Более плавная анимация с композитными режимами

С API веб-анимации теперь можно задать составной режим анимаций, то есть они могут быть аддитивными или накопительными, в дополнение к режиму по умолчанию «replace». Составные режимы позволяют разработчикам писать отдельные анимации и контролировать, как комбинируются эффекты. Теперь поддерживаются три составных режима: 'replace' (режим по умолчанию), 'add' и 'accumulate' .

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

Демонстрация, демонстрирующая режимы составного сложения, добавления и накопления. Смотреть демонстрацию на CodePen

В стандартном композитном режиме 'replace' финальная анимация заменяет свойство transform и заканчивается на rotate(360deg) scale(1.4) . Для 'add' composite добавляется вращение и умножается масштаб, что приводит к конечному состоянию rotate(720deg) scale(1.96) . 'accumulate' объединяет преобразования, что приводит к rotate(720deg) scale(1.8) . Для получения дополнительной информации о тонкостях этих композитных режимов ознакомьтесь с перечислениями CompositeOperation и CompositeOperationOrAuto из спецификации Web Animations.

Взгляните на следующий пример элемента пользовательского интерфейса:

Выпадающее меню с пружинистым меню, к которому применены две составные анимации. Смотрите демо на CodePen

Здесь две top анимации объединены. Первая — макроанимация, которая перемещает раскрывающийся список на всю высоту самого меню как эффект слайда сверху страницы, а вторая — микроанимация, которая применяет небольшой отскок при ударе о дно. Использование композитного режима 'add' обеспечивает более плавный переход.

const dropDown = menu.animate(
    [
      { top: `${-menuHeight}px`, easing: 'ease-in' },
      { top: 0 }
    ], { duration: 300, fill: 'forwards' });

  dropDown.finished.then(() => {
    const bounce = menu.animate(
      [
        { top: '0px', easing: 'ease-in' },
        { top: '10px', easing: 'ease-out' },
        { ... }
      ], { duration: 300, composite: 'add' });
  });

Что ждет API веб-анимации дальше?

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