CSS-анимации
На собеседованиях я часто спрашивал, какие CSS-свойства можно анимировать. Почему-то многих он ставил в тупик, кто-то вспоминал отдельные свойства, но сформулировать общее правило не получалось. А ведь всё довольно просто. Что вообще такое «анимация»? Это некое изменение внешнего вида, имеющее промежуточные положения, благодаря чему изменение происходит плавно, а не дискретно. У чего бывают промежуточные состояния? У всего, что можно измерить числами. Это может быть ширина объекта, его отступ, да даже цвет: будь то hex
, rgba
и пр., это всё числа. И нельзя анимировать display
, имеющий определённый набор свойств.
Следующим был вопрос о стоимости анимации, какие же свойства можно использовать со спокойной душой, а с какими надо быть осторожными. Здесь достаточно запомнить три свойства: transform
, opacity
и filter
(c которым не всё так просто).
Если у нас есть некий элемент, который мы хотим подвигать туда-сюда, то почему же его двигать лучше при помощи transform
, а не left
, margin
или чего-то ещё?
Дело в том, что изменение большинства свойств вызывают рендеринг, в то время как при transform
элемент не меняет своего положения в сетке, рендеринг не запускается. Причём элемент может двигаться сам по себе в своём контейнере, никак фактически не влияя на другие узлы в дереве, браузеру всё равно это надо пересчитать. Но насколько это важно и критично, можно ли это пощупать и измерить? Сделал пару простых экспериментов.
Возьмём сто элементов. Хотя нет, возьмём тысячу. И напишем странных вещей на SCSS:
.item {
width: 50px;
height: 40px;
background-color: rebeccapurple;
}
@for $i from 1 through 20 {
.item:nth-child(20n + #{$i}) {
animation: left_#{$i} #{5 - $i/8}s ease-in-out infinite;
}
@keyframes left_#{$i} {
0%, 100% {
margin-left: calc(50px * #{$i - 1});
}
50% {
margin-left: 1000px;
}
}
}
Используя вкладку Performance в DevTools, получаем такую картинку:
Из 3067 мс рендеринг занял 1469 мс, около 48%.
Теперь включим троттлинг процессора, а на вкладке Rendering выберем Paint flashing (дабы подсветить области на странице, которые необходимо перерисовать). Постоянный рендер вызывает постоянную перерисовку, анимация откровенно подтормаживает.
Меняем margin-left
на transform: translateX
, замеряем:
Всего лишь 66,5 мс, посколько рендеринг был только в самом начале, а далее в основном холостой ход, не пожирающий ресурсы.
Оба видео по 15 кадров в секунду, поэтому второй случай не столь гладкий, как в жизни, но здесь суть в сравнении.
Аналогично дело обстоит с другими трансформами. Если нужно анимировать width
, height
, то посмотрите, нельзя ли это сделать при помощи scaleX
, scaleY
. Более того, использование scale
для текста вместо font-size
тоже имеет смысл и может быть оправданно с точки зрения производительности, а разница в плавности становится очевидной и без троттлинга.
Может казаться, что одно дело, когда мы меняем какие-то физические размеры и местоположение элемента, но совсем другое дело, если работаем с цветом, например, с фоном блока, неужели это может вызвать проблемы? Можем ли мы воспользоваться альфа-каналом? Сравним.
@keyframes color_#{$i} {
0%, 100% {
background-color: rgba(102,51,153,1);
}
50% {
background-color: rgba(102,51,153,0);
}
}
@keyframes opacity_#{$i} {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
Слева — rgba
, справа opacity
, комментарии излишни.
А с filter
немного сложнее, поскольку там всё зависит от конкретного фильтра и браузера. И если, скажем, grayscale
отрабатывает без проблем, то анимация нескольких свойств в drop-shadow
уже тяжелее, и не только начинает вызывать рендеринги, но и знатно подвешивает браузер в целом. Так что надо тестировать и разбирать конкретные случаи.
{{ 'Comments (%count%)' | trans {count:count} }}
{{ 'Comments are closed.' | trans }}