Научите вас, как реализовать эффект анимации CSS «Фабрика Цзинси».

CSS

Автор этой статьи @ Wu Guanxi.

0 возможность и фон

В этом году Q1 (2020 первого квартала) участвовал в развитии в Пекинском радостном развитии «Пекинской радости», бизнес «Пекинская радость завода». Пользователи могут «Beijing Joy Factory» маски участия из бумаги, риса и других товаров, «производственная линия», как интересные существа, но и свободно принимать продукцию награды. В настоящее время через "Кёнсу” на главной странице апплета, чтобы получить доступ к событию.

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

Проект «Фабрика Jingxi» включает в себя две платформы, нативную страницу апплета WeChat и страницу H5.В проекте используется большое количество анимаций CSS, которые могут отлично работать на обеих платформах, и явных проблем с совместимостью обнаружено не было. .

В этой статье рассматриваются и обобщаются некоторые задействованные анимационные эффекты. В реальном бою проекта я научу вас подробно изучать принципы и детали реализации CSS-анимации.

Скриншот домашней страницы мероприятия «Kingxi Factory» выглядит следующим образом:

0.0 Принципы компьютерной анимации

Анимация относится к неподвижному изображению, состоящему из множества кадров, когда оно непрерывно воспроизводится с определенной скоростью (например, 16 кадров в секунду), невооруженным глазом视觉暂留создать иллюзию. Для достижения самого простого визуального эффекта постоянства требуется не менее 10 кадров в секунду, обычные фильмы — 24 кадра в секунду, а частота обновления обычных мониторов — 60 кадров в секунду.

Animexample

Следующие две гифки состоят из одних и тех же 6 кадров, но с разной скоростью воспроизведения: 10 кадров в секунду будут иметь небольшой эффект анимации, а 2 кадра в секунду — ощущение зависания.

10 кадров/сек: 2 кадра/сек:
Animexample
Animexample2

1 Насколько сложную анимацию может делать CSS?

1.1 Анимационный дисплей

Анимация ходьбы злодея на Фабрике Цзинси (ускорение в 4 раза):

京喜工厂小人走路动画(4倍速播放)

1.2 Описание и анализ анимации

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

京喜工厂小人路径

Во время ходьбы будут шагательные движения:

走路的动作

Обратите внимание, что при движении справа налево злодей смотрит вправо, при движении слева направо злодей смотрит влево:

朝向

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

工作中的动作

2 Зачем использовать CSS для сложных анимаций? (Конкуренты сравнивают SVG, Javascript, CSS)

Давайте сначала сравним.

2.1 SVG

SVG изначально поддерживает SMIL (синхронизированный язык интеграции мультимедиа), SMIL позволяет:

(1) Изменить цифровые атрибуты элемента (x, y...)<animate>

(2) Изменить атрибут деформации (перемещение или вращение)<animateTransform>

(3) Изменить атрибут цвета<animate> || <animateColor>(已废弃)

(4) Направление объекта синхронизировано с направлением пути движения (анимация пути)<animateMotion>

На самом деле, все они являются обычными возможностями анимации, но с некоторыми специфическими функциями SVG будут создавать некоторые замечательные эффекты, такие как анимация штрихов для использования.stroke-dasharrayа такжеstroke-dashoffsetосуществленный. Кроме того, то же самое для анимации пути SMIL<animateMotion>чем CSSoffset-pathСовместимость намного лучше.

caniuse-animateMotion

Апплет WeChat: апплет WeChat не поддерживает SVG и SMIL.

2.2 Javascript

Теоретически Javascript может делать любую анимацию. Вообще говоря, анимацию Javascript можно разделить на操纵 DOM 属性的动画а также操纵 canvas api 的动画, оба из которых основаны наwindow.requestAnimationFrame()илиwindow.setTimeout()Этот тип функции контроля времени отображает кадр каждые 16,7 мс, что обеспечивает скорость анимации 60 кадров в секунду. а такжеWeb Animations API, который открывает разработчику механизм анимации браузера и управляется с помощью JavaScript. Это один из самых эффективных способов поддержки анимации в Интернете в будущем, позволяющий браузерам выполнять собственную внутреннюю оптимизацию. Но совместимость плохая.

caniuse-web-animation-api

Апплет WeChat: апплет WeChat реализует собственный наборWX Ainmation APIНе совместимо с веб-стандартами.

2.3 CSS

CSS-анимации являются декларативными, используя@keyframeСоздайте ключевые кадры, и браузер будет автоматически рассчитывать изменения экрана каждые 16,7 мс. Эти вычисления не выполняются с помощью JS, что позволяет избежать GC. Еще одним преимуществом CSS-анимации является то, что вы можете использоватьtranslateZВключите аппаратное ускорение графического процессора, и в 2020 году можно сказать, что совместимость анимации CSS будет очень хорошей.

Немного жаль, что CSS используется в анимации пути.offset-pathСовместимость по-прежнему относительно плохая.

caniuse-offset-path

Апплет WeChat: апплет WeChat поддерживает анимацию CSS.

выберите

Учитывая, что проект в основном работает на платформах апплетов H5 и WeChat, полной совместимости и собственного мастерства, мы, наконец, решили использовать анимацию CSS.

3 Классификация анимации CSS

согласно сanimation-timing-function(функция времени), анимация CSS делится на «анимацию линейного изменения» и «анимацию нелинейного изменения»:

  • «Линейная анимация изменения» означаетanimation-timing-function = linear(直线) || cubic-bezier-timing-function(贝塞尔曲线).

  • «Нелинейная анимация изменения» означаетanimation-timing-function = step-timing-function(分段).

3.1 Анимация линейного изменения

Рекомендуется URL визуализации кривой Безье:✿ www.cube-bezier.com

3.2 Нелинейная анимация изменений

Нелинейные изменения в движении, обычно используются для «рамы анимации». Дизайнеры обычно выходят последовательность кадров, установленных в виде фона изображения, контроль анимацииbackground-positionсвойства и черезstep-timing-functionдобиться эффекта перехода.

В чем смысл? Позвольте мне привести пример маленького человека, идущего:

double

На самом деле есть только две картинки, а именно поднять левую ногу и поднять правую ногу (120 X 160), и с помощью инструментов объединить их в одну картинку (120 X 320).

spirit

код показывает, как показано ниже:

<div class="anim linear"></div>
<div class="anim steps"></div>
.anim{
  width:120px;
  height:160px;
  background-image:url(./spirit.png); /* 合成图 */
  background-position:0 0;
  background-size:100% auto;
}

.liear{
  animation:anim-walk 0.4s linear 0s infinite;
}

.steps{
  animation:anim-walk 0.4s steps(1) 0s infinite;
}

@keyframes anim-walk{
  0% {background-position:0px 0px;}
  50% {background-position:0px -160px;}
  100% {background-position:0px 0px;}
}

Эффект:

linear: steps:
anim-linear
anim-steps

В коде CSS мы определяемanim-walkНабор ключевых кадров, ключевой кадр 0%background-position-y0, 50% -160, 100% от времени и обратно в 0. Как видно на рисном эффекте, различныеanimation-timing-functionУстанавливает эффект на эффект анимации.

  • linearПоскольку это линейное изменение, данные между 0 ~ -160 ~ 0 рассчитываются как 0 ~ -40 ~ -80 ~ -120 ~ -160 ~ -120 ~ -80 ~ -40 ~ 0.

  • stepsПоскольку это нелинейное изменение, данные между 0 ~ -160 ~ 0 рассчитываются как 0 ~ 0 ~ 0 ~ 0 ~ -160 ~ -160 ~ -160 ~ -160 ~ 0.

3.3 Анимация пути (Как CSS делает кривую анимацию пути?)

В проекте фабрики Цзинси злодей должен перемещаться в пределах нескольких точек фабрики.

3.3.1 CSS offset-path

Самый простой способ - использоватьoffset-path, использование очень подробно описано в статье Чжан Синьсюй:offset-path-css-animation.

Недостатком является то, что совместимость плохая, поэтому здесь мы не будем подробно объяснять это.

3.3.2 Побочные эффекты использования функций времени для кривых Безье

В проекте «Фабрика Цзинси» путь движения злодея можно увидеть на схеме настроек ниже, а все отмеченные точки — оставаться и работать.

path-all

Несомненно то, что положение этих отмеченных точек должно быть ключевым кадром в анимации CSS, и с прямым путем между точками легко справиться, но как насчет кривой?

Здесь я использую «многоуровневую анимацию CSS» и «функцию времени как побочный эффект кривых Безье». Проще говоря, используя два или более элементов для достижения эффектов анимации (наслоение), мы можем более детально управлять траекторией движения элемента, используя функцию синхронизации по оси X, вдоль оси Y. Ось движения использует другую временную функцию.

Предположим, естьA[0,0]、B[100,100]два часа. Двигаясь от A к B, мы можем разделить его на количество изменений по оси X и количество изменений по оси Y. При движении по прямой совокупное изменение оси X и оси Y одинаково:

<div class="anim-x">
  <div class="anim-y">
  </div>
</div>
.anim-x{
  animation: anim-x 1000ms 0s linear infinite;
}
.anim-y{
  animation: anim-y 1000ms 0s linear infinite;
}
@keyframes anim-x {
  0%{ transform:translate3d(0 , 0 , 0) }
  100%{ transform:translate3d(100px , 0 , 0) }
}
@keyframes anim-y {
  0%{ transform:translate3d(0 , 0 , 0) }
  100%{ transform:translate3d(0 , 100px , 0) }
}

exp2

И наоборот, если кумулятивное изменение оси X и оси Y не совпадает, оно выйдет за пределы кривой:

<div class="anim-x">
  <div class="anim-y">
  </div>
</div>
.anim-x{
  animation: anim-x 1000ms 0s ease-in infinite;
}
.anim-y{
  animation: anim-y 1000ms 0s ease-out infinite;
}
@keyframes anim-x {
  0%{ transform:translate3d(0 , 0 , 0) }
  100%{ transform:translate3d(100px , 0 , 0) }
}
@keyframes anim-y {
  0%{ transform:translate3d(0 , 0 , 0) }
  100%{ transform:translate3d(0 , 100px , 0) }
}

exp3

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

3.4 Объединение

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

(1) Кадровая анимация ходьбы и работы злодея не может появляться одновременно.

(2) Ориентация злодея, когда анимация пути идет слева направо, должна быть противоположна ориентации, когда она идет справа налево.

Решением здесь также является «многоуровневая анимация CSS» и «нелинейная анимация».

Еще один слой анимации рулевого управления, один слой для управления анимацией «Анимация шагающего кадра человечка» и один слой для управления анимацией «Анимация рабочего кадра человечка». Все эти три управляющие анимации являются «нелинейными». анимации».

Примерный код можно записать так:

<div class="anim-turn">
  <div class="anim-walk"></div>
  <div class="anim-work"></div>
</div>
.anim-turn{
  animation: anim-turn ${allTime}ms 0s steps(1) infinite;
}
.anim-walk{
  animation: anim-walk-opacity ${allTime}ms 0s steps(1) infinite,anim-walk 0.4s steps(1) 0s infinite;
}
.anim-work{
  animation: anim-work-opacity ${allTime}ms 0s steps(1) infinite,anim-working 0.4s steps(1) 0s infinite;
}

@keyframes anim-turn { // 转向动画
  0% {transform:scale(1,1)} // 正向
  50% {transform:scale(-1,1)} // 反向
  100% {transform:scale(1,1)} // 正向
}

@keyframes anim-walk-opacity { // 控制「小人走路帧动画」的动画
  0% {opacity:1}
  50% {opacity:0}
  100% {opacity:1}
}

@keyframes anim-work-opacity { // 控制「小人工作帧动画」的动画
  0% {opacity:0}
  50% {opacity:1}
  100% {opacity:0}
}

Плюс многоуровневый CSS для осей X и Y:

<div class="anim-x">
  <div class="anim-y">
    <div class="anim-turn">
      <div class="anim-walk"></div>
      <div class="anim-work"></div>
    </div>
  </div>
</div>
.anim-x{
  animation: anim-x ${allTime}ms 0s linear infinite;
}
.anim-y{
  animation: anim-y ${allTime}ms 0s linear infinite;
}
.anim-turn{
  animation: anim-turn ${allTime}ms 0s steps(1) infinite;
}
.anim-walk{
  animation: anim-walk-opacity ${allTime}ms 0s steps(1) infinite,anim-walk 0.4s steps(1) 0s infinite;
}
.anim-work{
  animation: anim-work-opacity ${allTime}ms 0s steps(1) infinite,anim-working 0.4s steps(1) 0s infinite;
}

@keyframes ...

3.5 Написать инструмент визуализации для повышения эффективности

Вышеупомянутое просто написало простую архитектуру этой анимации и конкретные данные анимации.@keyframesВ том-то и дело, что эти ключевые кадры точно не написаны от руки.

Если рабочий хочет хорошо работать, он должен сначала заточить свои инструменты.

Итак, давайте создадим инструмент визуализации [doge] с помощью Vue.

Это может выглядеть так:

Основными операциями являются «добавление ключевых кадров», «настройка свойств каждого ключевого кадра», «генерация тестовых анимаций» и «вывод анимации CSS».

«Добавить ключевой кадр»:

添加关键帧

«Настроить свойства каждого ключевого кадра»:

调整每个关键帧的属性

"Создать тестовую анимацию - вывести анимацию в CSS":

生成测试动画-输出动画CSS

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

(1) Как нарисовать путь анимации?

(2) Как рассчитать время анимации?

3.6 Нарисуйте путь анимации

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

point-line

Например:

<div class="anim-x">
  <div class="anim-y">
  </div>
</div>
@keyframes anim-x {
  0%{ transform:translate3d(0 , 0 , 0); animation-timing-function:linear}
  100%{ transform:translate3d(300px , 0 , 0) }
}
@keyframes anim-y {
  0%{ transform:translate3d(0 , 0 , 0); animation-timing-function:cubic-bezier(0,.26,.74,1) }
  100%{ transform:translate3d(0 , 300px , 0) }
}
.anim-x{
  animation: anim-x 1000ms 0s;
}
.anim-y{
  animation: anim-y 1000ms 0s;
}

В этом примере ось x элемента от 0 до 300,animation-timing-functionдаlinear, ось Y от 0 до 300,animation-timing-functionдаcubic-bezier(0,.26,.74,1), затем определите продолжительность времени как 1, разделите ее в среднем на 100 сегментов и используйте цикл for, чтобы найти X и Y в разное время:

  const moveTo = [0,0];
  const step = 100;
  const dX = 300;
  const dY = 300;
  const timeFunX = "linear";
  const timeFunY = "cubic-bezier(0,.26,.74,1)";
  if (Math.abs(dX) > 0 || Math.abs(dY) > 0) {
    ctx.moveTo(moveTo[0],moveTo[1]);
    for(let i = 0;i <= step;i ++) {
      const x = getTimeFunctionValue(timeFunX,i/step) * dX + moveTo[0]; // 求出 timeFunX(linear) 对应时间进度下的 x
      const y = getTimeFunctionValue(timeFunY,i/step) * dY + moveTo[1]; // 求出 timeFunY(cubic-bezier(0,.26,.74,1)) 对应时间进度下的 y
      ctx.fillRect(x, y, 1, 1);
      if (i % 10 === 0) {
        ctx.font = "16px serif";
        ctx.fillText(`(${x},${(y).toFixed(2)})`, x + 20, y + 20);
      }
    }
  }

Эффект следующий:

point-text

Остальное этоgetTimeFunctionValue(时间函数,时间进度[0,1])Как это должно быть написано?

прежде всегоlinearОтдельно от других кривых Безье,linearНа самом деле это прямая линия Любое значение, введенное в прогресс времени, вернет одно и то же значение.

function getTimeFunctionValue(timeFunctionName = "linear",x = 0){
  ...
  if (timeFunctionName === "linear") return x;
  ...
}

Что можно сказать о кривых Безье? Давайте начнем с учебника по функции времени кривой Безье в CSS-анимации.

3.7 Функция времени кривой Бесселя в анимации CSS

«Кривая Безье» — это параметрическая функция. Наиболее широко используемым компьютером является «кубическая кривая Безье».

Четыре точки P0, P1, P2 и P3 определяют кубическую кривую Безье на плоскости или в трехмерном пространстве. Кривая начинается от P0 до P1 и идет от P2 до P3. Обычно он не проходит через P1 или P2; эти две точки нужны только для предоставления информации о направлении. Расстояние между P0 и P1 определяет «длину» кривой, идущей к P1 перед поворотом к P2.

Параметрическая форма кривой:

math-x

math-y

Функция времени кривой Безье в анимации CSS представляет собой упрощенную версию «кубической кривой Безье», P0 фиксируется на [0,0], P3 фиксируется на [1,1].

Более того, его прямоугольная система координат отличается от геометрических координат (x, y), но имеет другие значения.Горизонтальная ось представляет «прогресс во времени (время)», а значение [0% ~ 100%]. Вертикальная ось представляет «прогресс» атрибута. Это значение обычно находится в диапазоне [0% ~ 100%], что может быть меньше 0% или больше 100%.

Таким образом, эта упрощенная кривая Безье CSS может быть представлена ​​следующими двумя уравнениями (подставляя P0[0,0] P3[1,1]):

T(временной прогресс) = ...

math-2

P(% изменения) = ...

math-3

cubic-bezier(0,.26,.74,1)Параметры внутри на самом деле (P1_time, P1_progression, P2_time, P2_progression). Как показано ниже:

exp

Выбиратьcubic-bezier(0,.26,.74,1)Параметры внутри (P1[0,0,26],P2[0,74,1]) подставляются в две приведенные выше формулы, и получаются следующие результаты (уравнения):

T(временной прогресс) = ...

math-4

P(% изменения) = ...

math-5

T в первом уравнении - это временной прогресс, который является входным параметром Решите корень этой одномерной кубической функции относительно t и подставьте его во второе уравнение, чтобы получить P. P - «степень изменения» под T «прогресс во времени».

Примечание. Кубические функции имеют 3 корня, но положительными решениями являются только действительные числа и числа между [0 ~ 1].

3.8 Как считать время в анимации?

Выше мы используем интегральный метод для аппроксимации пути анимации, который эквивалентен приблизительному значению длины пути анимации. длина/скорость = время анимации. Скорость может быть настроена.

4 других

4.1 Решить проблему неправильного уровня (translateZ)

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

before

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

Причина тоже очень проста, потому что эти товары являются соседними элементами, а уровень элементов сзади всегда выше, чем спереди. Как следующее:

<div>
  <div>1</div>  <!-- 显示层级最低 -->
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>  <!-- 显示层级最高 -->
</div>

Но уровень, который хочет показать эта анимация, высокий в середине и низкий с обеих сторон.

Некоторые студенты могут подумать об использовании z-index, но, к сожалению, z-index не работает в CSS-анимации.

Правильное решение — использовать translateZ для преобразования его в 3D-отображение, таким образом достигнув высокого уровня посередине и низкого уровня по бокам:

@keyframes anim-z{
  0% {transform: perspective(500px) translateZ(0);}
  50% {transform: perspective(500px) translateZ(50px);}
  100% {transform: perspective(500px) translateZ(0);}
}

Дополнительный эффект:

after

4.2 Решение проблемы дрожания покадровой анимации

dou

В покадровой анимации тоже есть проблема с дрожанием.Глядя на гифку выше, можно заметить, что злодей немного дергается. Эта гифка отображается на iPhone 6 Plus (экран телефона 414px).

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

Для исследования джиттера покадровой анимации достаточно прочитать эту статью от "Bump Lab":«Навыки CSS: решение покадровой анимации с дрожанием»

В этой статье предлагаются три решения A, B и C, из которых решение C является «окончательным решением». К сожалению, это решение использует SVG, а апплет не поддерживает SVG.

В качестве второго варианта я выбрал решение A, которое заключается в использовании медиа-запросов CSS для записи точек останова, а все точки останова указаны в пикселях.

/* 300 ~ 349 之间用 iphone5(320)的数据 */
@media screen and (min-width: 300px) and (max-width: 349px) {
    .m_worker_employee {
        width:51px;
        height: 68px
    }
    @keyframes anim-working {
        0% {
            background-position: 0px -204px
        }
        50% {
            background-position: 0px -272px
        }
        100% {
            background-position: 0px -204px
        }
    }
}
/* 350 ~ 399 之间用 iphone6(375)的数据 */
@media screen and (min-width: 350px) and (max-width: 399px) {
    .m_worker_employee {
        width:60px;
        height: 80px
    }
    @keyframes anim-working {
        0% {
            background-position: 0px -240px
        }
        50% {
            background-position: 0px -320px
        }
        100% {
            background-position: 0px -240px
        }
    }
}
/* 400 ~ 449 之间用 iphone6P(414)的数据 */
@media screen and (min-width: 400px) and (max-width: 449px) {
    .m_worker_employee {
        width:66px;
        height: 88px
    }
    @keyframes anim-working {
        0% {
            background-position: 0px -264px
        }
        50% {
            background-position: 0px -352px
        }
        100% {
            background-position: 0px -264px
        }
    }
}

budou

После применения точки останова анимация кадра не дрожит.

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

5 Формула, связанная

Формула в этой статье состоит в том, чтобы использовать программное обеспечение «TeX», а затем использовать «MathJax» для вывода в формате SVG, вот рекомендация:www.mathjax.org/#demo


Если вы считаете, что этот контент ценен для вас, ставьте лайк и подписывайтесь на наш официальный веб-сайт и нашу общедоступную учетную запись WeChat (WecTeam). Каждую неделю публикуются высококачественные статьи: