Анимация без стереотипов


Хоть анимация и понимается прежде всего как «оживление», в CSS кроме обычной плавной анимации, есть ещё пара типов — дискретная и никакая (свойство не анимируется).

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

Свойства с плавной анимацией — это цвета, размеры, отступы и прочие свойства, к которым можно применить плавные переходы.

Свойства с дискретнной анимацией — (внезапно) почти все остальные свойства, за некоторым исключением.

Ок, как это можно использовать. В этом кейсе я воспринимаю анимацию как механизм переключения значения свойства из одного в другое (с опциональным таймером и повторением N-раз). То есть некий setInterval(() => fn(value)), только в мире CSS.

Я полистал список всех CSS-свойств и выбрал несколько, об анимации которых я раньше не задумывался. Например, свойство content. Можно сделать «мигающий курсор»:

div::after {
animation: blink 1s infinite;
}
@keyframes blink {
from {
content: "";
}
to {
content: "|";
}
}

А учитывая, что content в качестве значения поддерживает текст, картинки, кавычки, счётчики, то можно сделать хоть целое слайдшоу 😁

Окей, ещё с помощью дискретной анимации можно собрать свою gif-ку на коленке. Это буквально быстрая смена «кадров» с картинками, как в кино. Если в секунду сменять 10 «кадров», то скорость будет достаточная для создания иллюзии непрерывного движения:

.frame {
animation: movie 1s infinite;
}
@keyframes movie {
0% {
background-image: var(--image-1);
}
10% {
background-image: var(--image-2);
}
}

Демо собрано тут.

Дальше я решил развить тему кинематографа и следующим попробовать анимировать лейаут гридов. Для построения отдельных «кадров» я решил использовать свойство grid-template-areas. Это свойство описывает раскладку грида в декларативном виде. Вот так, к примеру, будет выглядеть начальный «кадр» будущего фильма:

grid-template-areas:
". . . . a a a a a . . . ."
". . . . . . . . b . . . ."
". . . . . . . . b . . . ."
". . . . . . . . b . . . ."
". . . . c c c c c . . . ."
". . . . . . . . d . . . ."
". . . . . . . . d . . . ."
". . . . . . . . d . . . ."
". . . . . . . . d . . . ."
". . . . e e e e e . . . .";

Точками в значении grid-template-areas обозначены те области грида, которые не будут закрашены белым, а буквами — те, которые закрашиваются. По ходу «фильма» значение grid-template-areas будет принимать разные значения. А вот подменять эти значения раскладки грида будет CSS-анимация. Для этого нужно прописать раскадровку «фильма» в кейфреймах:

@keyframes movie {
0% {
grid-template-areas: …;
}
10% {
grid-template-areas: …;
}
100% {
grid-template-areas: …;
}
}

И затем задать получившуюся анимацию для грид-контейнера .field:

.field {
animation-name: movie;
animation-duration: 10s;
animation-play-state: paused;
animation-timing-function: step-end;
animation-iteration-count: 1;
animation-fill-mode: both;
}

Получившийся триллер можно найти тут.

И ещё одна деталь про неанимируемые свойства. А что будет, если в кейфреймы положить display: none? Оно применится в конце, начале или середине анимации?

.fade-out {
animation: fade-out 1s forwards;
}
@keyframes fade-out {
100% {
opacity: 0;
display: none;
}
}

Спецификация говорит, что свойство display — not animatable. И для display: none с относительно недавних пор действует особое правило: если анимация «появляет» элемент, то display включается в самом начале, а если анимация элемент скрывает, то display: none применяется в самом конце, после всех остальных транзишнов (демо тут, работает везде)!