Новые функции CSS (mod, round) или как сделать анимированные Sprite Sheet без JS

от автора

Введение. Что это такое?

Sprite Sheet — это техника в веб-разработке, позволяющая использовать множество различных кадров анимации, хранящихся в одном изображении. Это эффективный способ уменьшить количество HTTP-запросов к серверу и ускорить загрузку веб-страницы, так как все кадры анимации загружаются одновременно. (базовое определение которое дает чатгпт)

Что из себя представляет

Sprite Sheet — это композиция из множества различных изображений (обычно кадров анимации), объединенных в один файл. Это позволяет управлять анимацией персонажей или элементов интерфейса, изменяя позицию изображения в CSS.

Sprite Sheet сундуков в разных состояниях и направлениях

Sprite Sheet сундуков в разных состояниях и направлениях

В чем была сложность до появления round и mod в CSS?

Основная сложность заключалась в том чтобы автоматизировать процесс перехода по строкам. Например:

@keyframes sprite {   from { background-position: 0px; }   to { background-position: -8640px; } }

Начальное Положение (from): Анимация начинается с background-position: 0px;. Это означает, что фоновое изображение начнет отображаться с начальной позиции, обычно это левый верхний угол изображения.

Конечное Положение (to): Анимация заканчивается при background-position: -8640px;. Это означает, что фоновое изображение будет сдвигаться по горизонтали на -8640 пикселей от начальной позиции к концу анимации. Такой сдвиг обычно используется для прохождения через различные кадры Sprite Sheet, где каждый кадр представляет различные этапы анимации.

Далее при помощи animation-timing-function: steps(x); где X это количество кадров мы могли производить смещение по кадрам. В зависимости от времени выполнения и частоты кадров в Sprite Sheet можно было наблюдать анимацию изображения. Например:

Animated mario by frames

Animated mario by frames

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

Inline sprite sheet

Inline sprite sheet

Что изменилось с выходом Chrome 125?

Были добавлены новые функции:

  • CSS-функция round() возвращает округленное число на основе выбранной стратегии округления.

  • CSS-функция mod() возвращает модуль, оставшийся при делении первого параметра на второй параметр, аналогично оператору остатка в JavaScript (%). Модуль — это значение, оставшееся после деления одного операнда, делимого, на второй операнд, делитель. Оно всегда принимает знак делителя.

  • CSS-функция rem() возвращает остаток, оставшийся при делении первого параметра на второй параметр, аналогично оператору остатка JavaScript (%). Остаток — это значение, оставшееся после деления одного операнда (делимого) на второй операнд (делитель). Оно всегда принимает знак дивиденда.

Что это нам дает?

Теперь мы можем написать анимированные Sprite Sheet с переходами по X и Y координатам на чистом CSS! Давайте начнем с примера который выложен на Codepen. (Если кто то хочет, может сразу перейти по ссылке и сам разобраться что к чему)

Начнем!

Для начала нам нужно определить:

@property --sprite-fs {       syntax: "<integer>"; /* Описывает допустимый синтаксис свойства. */      initial-value: 0;  /* Устанавливает начальное значение свойства. */   inherits: false;  /* Определяет наследования свойства */ }

@property — является частью API-интерфейсов CSS Houdini . Оно позволяет разработчикам явно определять свои свойства, позволяя проверять тип свойства, устанавливать значения по умолчанию и определять @property , может ли свойство наследовать значения или нет. В Chrome со 119 версии.

--sprite-fs — в данном случае является начальной точкой отсчета кадров (сейчас выставлен 0, но можно изменять и начинать анимацию с любого кадра)

Далее, необходимые обязательные переменные:

/* Количество колонок */ --sprite-c: 5; /* Высота Sprite Sheet */ --sprite-h: 345; /* Ширина Sprite Sheet */ --sprite-w: 640; /* Количество кадров */ --sprite-f: 15;

Как вы уже догадались, далее идет несложная математика:

/* Считаем количество кадров (от 0 поэтому, 15 - 1) */ --sprite-fe: calc(var(--sprite-f) - 1); /* Считаем количество строк с округлением до большего целого числа (15/5 = 3) */ --sprite-r: round(up, calc(var(--sprite-f) / var(--sprite-c)), 1); /* Считаем высоту отдельного фрейма (345/3 = 115) */ --sprite-sh: calc(var(--sprite-h) / var(--sprite-r)); /* Данное свойство изменяемое, можно вводить любое значение чтобы изменять высоту спрайта при  отрисовке */ --sprite-th: 100; /* default: var(--sprite-sh) */ /* Aspect ratio для пропорционального изменения ширины (~0.869) */ --sprite-ar: calc(var(--sprite-th) / var(--sprite-sh)); /* Обновленный размер ширины и высоты Sprite Sheet (w: ~556.52, ~h:300) */ --sprite-uh: calc(var(--sprite-h) * var(--sprite-ar)); --sprite-uw: calc(var(--sprite-w) * var(--sprite-ar)); /* Считаем ширину отдельного фрейма (556.52/5 = 111.30) */ --sprite-tw: calc(var(--sprite-uw) / var(--sprite-c));

Далее задаем свойства нашего элемента:

/* Высота блока куда будет выводится анимация */ height: calc(1px * var(--sprite-th));  /* Ширина блока куда будет выводится анимация */ width: calc(1px * var(--sprite-tw)); /* Sprite Sheet */ background-image: var(--sprite-image); /* Итоговый размер background с учетом aspect ratio */ background-size: calc(1px * var(--sprite-uw)) calc(1px * var(--sprite-uh));

А теперь то что стало возможным с введением round и mod:

/* Да, это всё еще CSS) */ /* Расчет текущей строки, определяем выходит ли наше изображение на пределы ширины Sprite Sheet */ /* Определяем шаг по X ((ширина спрайта * текущий кадр) / ширину спрайт листа) */ /* Округляем в меньшую сторону, находим позицию строки Y (0,1,2) умножаем на высоту спрайта для координат */ --row: calc(round(down, calc(calc(var(--sprite-tw) * var(--sprite-fs)) / var(--sprite-uw)), 1) * var(--sprite-th)); /* Расчет текущей колонки */ /* Остаток от деления по X (ширина спрайта * текущий кадр) / ширину спрайт листа */ /* Позволяет определить позицию по X. В нашем примере (0,1,2,3,4)) */ --col: mod(calc(var(--sprite-tw) * var(--sprite-fs)), var(--sprite-uw));

Переводим в px, выставляем background-size:

background-position: calc(-1px * var(--col)) calc(-1px * var(--row));
Первый кадр, выставлен (подложка для наглядности)

Первый кадр, выставлен (подложка для наглядности)

Анимируем

Тут все очень просто

@keyframes frame {     to {         --sprite-fs: var(--sprite-fe);     } }

Ранее мы задавали @property --sprite-fs, которое обозначало кадр начала анимации, теперь мы задаем to --sprite-fe (индекс последнего кадра) т.е. анимация будет выполняться от 0 до 14 (15 кадров).

Анимируем:

/* animation duration */ --sprite-as: 4s; /* animation direction */ --sprite-ad: normal; /* animation fill mode */ --sprite-af: none; /* animation play state */ --sprite-ap: running; /* animation iteration count */ --sprite-ai: infinite; /* animation timing function */ --sprite-at: linear;  /* одинокий delay в 0s :) */ animation: frame var(--sprite-as) var(--sprite-at) 0s var(--sprite-ai) var(--sprite-ad) var(--sprite-af) var(--sprite-ap);

Результат

Пример анимации Sprite Sheet

Пример анимации Sprite Sheet

P.S.: Первый пост, на платформе. (Пока разбираюсь).

Демо с настройками анимации (правый верхний угол): https://codepen.io/Maseone/pen/oNRxxWX


ссылка на оригинал статьи https://habr.com/ru/articles/816587/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *