Изучаем интересные приемы анимации с помощью SASS и SVG фильтров для реалистичных эффектов движения на примере мультипликационных животных.
Сегодня мы хотим показать вам, как грамотно используя HTML, последовательные CSS анимации и SVG фильтры можно воплотить в жизнь одну из самых неожиданных вещей на веб-странице - анимированных животных. Мы рассмотрим два метода для рисования животных: один с простым HTML и CSS, а другой - с инлайновым SVG в качестве фонового изображения.
Анимация - это не так просто, поэтому наш урок будет сосредоточен на различных методах, участвующих в создании каждого из питомцев и их реалистичного движения. Именно вы можете решать, каких уникальных забавных зверушек вы хотите создать.
Создаем животных
Демонстрационные примеры используют два различных метода для создания форм различных частей тела животных. Для хаски используется CSS свойство border-radius, а для лисицы - встроенные фоновые SVG-изображения, т.к. формы являются более сложными.
Разметка
Оба животных используют вложенные HTML-блоки, соответствующие частям тела. Группировка элементов имеет важное значение для создания реалистичного движения - когда движется голова, глаза и уши также должны двигаться.
<!-- Разметка головы лисы -->
<div class="fox-head">
<div class="fox-face">
<div class="fox-ears">
<div class="fox-ear"></div>
<div class="fox-ear"></div>
</div>
<div class="fox-skull"></div>
<div class="fox-front"></div>
<div class="fox-eyes"></div>
<div class="fox-nose"></div>
</div>
</div>
<!-- Разметка головы собаки -->
<div class="husky-head">
<div class="husky-ear"></div>
<div class="husky-ear"></div>
<div class="husky-face">
<div class="husky-eye"></div>
<div class="husky-eye"></div>
<div class="husky-nose"></div>
<div class="husky-mouth">
<div class="husky-lips"></div>
<div class="husky-tongue"></div>
</div>
</div>
</div>
Каждая часть может двигаться независимо друг от друга, и будет двигаться со своим родительским элементом, что создает более реалистичный эффект. Вы заметите, что хвост состоит из вложеных друг в друга компонентов хвоста. Когда каждая хвостовая часть позиционируется относительно ее родителя, она, затем, поворачивается настолько же, что создает иллюзию равномерной кривой.
Формируем CSS
CSS свойство border-radius
активно используется для формирования лайки с помощью CSS. Для многих элементов, необходим индивидуальный контроль каждого радиуса скругления. Например, вот как была сформирована задняя нога собаки:
.husky-hind-leg {
// ...
border-top-left-radius: 35% 100%;
border-top-right-radius: 40% 100%;
}
Первое число указывает на то, насколько глубоко кривая начинается на верхней/нижней кромке, а второе - насколько глубоко кривая начинается на левом/правом крае.
Другие части туловища, такие как передние ноги, не могут быть сформированы с помощью border-radius
, для них мы воспользуемся CSS свойством transform:
.husky-front-legs > .husky-leg:before {
transform: skewY(-30deg) skewX(10deg);
transform-origin: top right;
}
Как только формы размещены в нужных местах, каждому элементу присваивается позиция внутри «родителя», рассчитанная на основе процентных соотношений. Это обеспечивает точное размещение каждой частей тела, а также отзывчивость.
Формируем SVG
Для лисицы, Sass-SVG могут быть использованы для создания сложных SVG форм для каждой части тела. SVG изображения могут использоваться в качестве фона и закодированы в base64 или UTF-8 (для максимальной поддержки браузеров).
Вручную их писать сложно, хотя можно воспользоваться Adobe Illustrator для создания начальных форм:
А потом я сохранил каждую часть тела в качестве SVG изображения. Код SVG был переведен в SCSS таблицу стилей через Sass-SVG. Например, это нос и рот лисы:
.fox-nose:before {
@include svg((viewBox: (0 0 168 168))) {
// Нос
@include svg('path', (
fill: $color-nose,
d: 'M83.7,86.7c3.3,0,11.6-3.9,11.6-7.1c0-3.2-9.4-3.2-11.6-3.2c-2.2,0-11.6,0-11.6,3.2 C72.1,82.8,80.4,86.7,83.7,86.7z'
));
// линия, соединяющая нос и рот
@include svg('path', (
stroke: $color-nose,
fill: none,
d: 'M83.7,102.3V86.7'
));
// Рот
@include svg('path', (
stroke: $color-nose,
fill: none,
d: 'M94.5,104.9c0,0-5.2-2.7-10.8-2.7c-5.6,0-10.8,2.7-10.8,2.7'
));
}
}
Так создается SVG-строка внутри ссылки `url()`, которая выглядит примерно так:
.fox-nose:before {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg...");
}
Поскольку SVG является фоновым изображением, оно может быть преобразовано и анимировано, как если бы это был HTML элемент. С Sass-SVG, Sass $variables
может быть использовано, чтобы обрести полный контроль над SVG fill
и stroke
цветами.
При использовании SVG, заставить лису «отзываться» было нетрудно. Значения атрибутов viewbox ((viewBox: (0 0 168 168))
) берутся прямиком из файла SVG, но пока сохраняется соотношение высоты с шириной, элемент, содержащий фоновое изображение SVG может быть любого размера. Все части головы лисы расположены абсолютно, с такой же высотой и шириной, как и у .fox-head
.
“Squigglevision” с SVG фильтрами
Squigglevision - это метод анимации, которая имитирует рисованную анимацию шевеля контуры фигуры. Это делает сцены с лисицой и собакой более динамичными и прорисованными, даже когда животные не двигаются.
SVG имеет фильтр под названием <feTurbulence>
, который дает "шум" к применяемому объекту. Добавим к нему фильтр <feDisplacementMap>
, чтобы определять, как далеко пиксели должны двигаться в каждом фильтре.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="squiggly-0">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="0"/>
<feDisplacementMap id="displacement" in="SourceGraphic" in2="noise" scale="2" />
</filter>
<filter id="squiggly-1">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="1"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="3" />
</filter>
<filter id="squiggly-2">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="2"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" />
</filter>
<filter id="squiggly-3">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="3"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="3" />
</filter>
<filter id="squiggly-4">
<feTurbulence id="turbulence" baseFrequency="0.02" numOctaves="3" result="noise" seed="4"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="1" />
</filter>
</defs>
</svg>
Каждый фильтр имеет несколько различных атрибутов. Эти фильтры могут быть применены к любому элементу с помощью CSS свойства filter: url(...);
. Для того, чтобы создать эффект "squigglevision", анимация ключевого кадра устанавливает фильтры.
@keyframes squigglevision {
0% {
-webkit-filter: url('#squiggly-0');
filter: url('#squiggly-0');
}
25% {
-webkit-filter: url('#squiggly-1');
filter: url('#squiggly-1');
}
50% {
-webkit-filter: url('#squiggly-2');
filter: url('#squiggly-2');
}
75% {
-webkit-filter: url('#squiggly-3');
filter: url('#squiggly-3');
}
100% {
-webkit-filter: url('#squiggly-4');
filter: url('#squiggly-4');
}
}
Эти SVG фильтры в настоящее время не работают в Firefox, поэтому относитесь к ним просто как к прогрессивному улучшению.
Анимация животных
CSS ключевые кадры не дают нам удобный способ последовательности действий и создания анимаций. Лучший способ решения этой проблемы заключается в планировании (раскадровки) анимации как график и использовать препроцессор, такой как Sass, для создания ключевых кадров.
Для лисицы преобразования и абсолютные коррекции времени (в секундах) использовались для анимирования каждой отдельной части тела после отметок, когда какая анимация должна произойти. Вот пример того, как нос лисы был размечен в SCSS:
$animations: (
// ...
'nose': (
// сидячая поза
(4s, 5s, 7s): rotateY(-4deg),
// нос опущен
4.5s: rotateY(-4deg) rotateX(-3deg),
// лиса смотрит влево
(7.5s, 9s): rotateX(-3deg) rotateY(-28deg) rotateZ(-11deg),
// лиса смотрит вправо
(9.5s, 12s): rotateY(7deg),
// лиса смотрит вперед
13s: rotateY(0),
),
// ...
);
Здесь, $animations
это Sass-карта, в которой ключом является название анимации (например, 'nose'
). Значение каждого названия анимации – это другая карта, в которой ключ – это офсет или список офсетов в секундах (например, (7.5s, 9s)
), а значение – это свойство transform для каждого ключа.
И так, как сделать такую карту анимацией @keyframe
? Сперва задана глобальная переменная $duration: 17s
— это будет общей продолжительностью каждой анимации. Затем используя встроенные циклы Sass @each ... in ...
, можно генерировать CSS-декларации @keyframe
для каждой анимации путем зацикливания через карту $animations
:
@each $animation-name, $animation in $animations {
// декларация ключевых кадров
@keyframes #{$animation-name} {
@each $offsets, $transform in $animation {
@each $offset in $offsets {
// блок объявления офсета
#{percentage($offset / $duration)} {
// свойство transform
transform: #{$transform};
}
}
}
}
}
Этот код будет генерировать ключевые кадры, которые выглядят следующим образом:
@keyframes nose {
14.70588% {
transform: rotateY(-4deg); }
23.52941% {
transform: rotateY(-4deg); }
29.41176% {
transform: rotateY(-4deg); }
41.17647% {
transform: rotateY(-4deg); }
26.47059% {
transform: rotateY(-4deg) rotateX(-3deg); }
44.11765% {
transform: rotateX(-3deg) rotateY(-28deg) rotateZ(-11deg); }
52.94118% {
transform: rotateX(-3deg) rotateY(-28deg) rotateZ(-11deg); }
55.88235% {
transform: rotateY(7deg); }
70.58824% {
transform: rotateY(7deg); }
76.47059% {
transform: rotateY(0); } }
Расчет этих процентов могут быть очень утомительным без использования SCSS. Они представляют собой процентную долю офсета для каждого значения времени на каждом шаге нашей анимации относительно общей продолжительности $duration
.
Анимации могут быть применены к соответствующим частям тела, например animation: nose $duration none infinite;
. Очень важно, все анимации имели одинаковыю длительность, чтобы их можно было легко зациклить.
Реалистичные сглаживания кривых линий
Другая важная часть придания анимации реалистичности – это тщательный выбор (или создание) сглаживаний для каждой части анимации. Лучший способ для этого – это использовать «синусоидальные» кривые, другими словами, те кривые, у которых мягкие изгибы. Натуральные движения не будут слишком быстро начинаться или останавливаться, поэтому это будет отражено с помощью animation-timing-function
.
Для лисы и собаки я использую cubic-bezier(0.645, 0.045, 0.355, 1)
(см. превью). Кривая, нарисованная ниже начинается довольно быстро, но плавно останавливается. Как и со многими другими вещами, лучший способ найти то, что вам нужно – это поэкспериментировать самому.
И в заключение: в Chrome, вы можете визуально проверить все ваши последовательные анимации, чтобы гарантировать, что они происходят в правильное время. Просто откройте консоль, нажмите вкладку Style, а затем на кнопку воспроизведения:
Надеюсь, этот урок вдохновил вас на создание собственных CSS анимаций!