calc-mix() и progress() – что за функции и в чём разница?
• css
Вступление
Думаю, те, кому интересны всяческие новинки в CSS-мире от браузеров, могли слышать про progress(). Как-никак есть статья на MDN и 2 стабильных браузерных движка с реализацией. А вот что касается calc-mix(), то это более редкий зверь. Реализация пока есть только в Safari Technical Preview 247+, и пока только спека.
А ещё эти две функции могут… Смутить? И то и то про смешивание значений, и то и то принимает несколько значений, и то и то дружит с разными единицами.
Давайте разбираться, что зачем, а также попробуем прикинуть примеры использования.
progress()
Начнём с уже существующей и более понятной функции. progress() позволяет посчитать значение без единиц измерения, которое ответит на вопрос, “на какой стадии прогресса от А до Б мы находимся”:
.block {
--progress: progress(current, from, to);
...
--progress: progress(100vw, 100px, 200px);
}В качестве аргументов можно использовать значения без единиц измерения, значения “длинны” и проценты. Использовать другие единицы нельзя.
Можно подставлять функции внутрь, так и использовать progress() внутри других функций, например, в rgb().
Функция progress() возвращает значение от 0 до 1. По сути, такого же эффекта можно добиться, если просто посчитать
.block {
--progress: progress(current, from, to);
/* -> */
--progress: clamp(0, calc((current - from) / (to - from)), 1);
}Конечно, последняя строка работает только в браузерах, которые умеют в “типизированную арифметику”.
Для получения чисел без единиц измерения во всех браузерах сейчас можно использовать хак с tan(atan2(...)), а в браузерах из списка выше достаточно просто деления:
.block {
--unitless: calc(300px / 1px);
}progress() – ещё один способ получить числовое значение “без единиц измерения”.
calc-mix()
У calc-mix() история интереснее. В текущем Working Draft функция принимает 3 значения и похожа на обратную к progress() – просто смешивает два значения в пропорции.
А вот в Editor Draft функция резко меняется, и начинает принимать любое количество значений, каждое из которых можно снабдить процентами. Прямо как color-mix(), который недавно прокачали и теперь он может принимать больше двух цветов.
В Safari реализовали именно второй вариант:
.block {
/* 150px */
width: calc-mix(100px 50%, 200px 50%);
}В общем, вполне себе “пиксельная версия” color-mix(). В случае, если сумма больше 100% – проценты нормализуются. Проценты можно и опустить:
.block {
/* 150px */
width: calc-mix(100px, 200px);
}Если же одно значение записано без процентов, а второе – с процентами, то выражение будет задавать интервал от первого до второго при использовании 0-100%:
.block {
/* 100px */
width: calc-mix(100px, 200px 0%);
/* 150px */
width: calc-mix(100px, 200px 50%);
/* 200px */
width: calc-mix(100px, 200px 100%);
}Также в текущей версии нельзя использовать проценты за пределами интервала 0-100 (а это было бы полезно для анимации “пружины”).
По сути, calc-mix() – всего лишь синтаксический сахар, и того же самого можно написать через обычный calc().
Примеры
В доке есть пример с элементом, когда цвет высчитывается исходя из текущей ширины элемента:
.block {
--custom-minwidth: 320px;
--custom-maxwidth: 1200px;
--progress: progress(
var(--container-width),
var(--custom-minwidth),
var(--custom-maxwidth)
);
background-color: rgb(
calc(
255 *
var(--progress)
)
calc(
255 *
var(--progress)
)
255 / 0.5
)
}--container-width выставляется в примере скриптом, но ничего не мешает использовать вместо этого какой-нибудь 100vw. Как визуальный залипательный элемент на промо-страничке – почему нет.
Аналогичным способом можно сделать и без скрипта, используя container-query:
.container {
container-type: size;
--container-width: 100cqw;
...
}Благо, поддержка cqw выглядит всё лучше и лучше:
Другой способ использования progress() и calc-mix(). Есть классический пример с clamp():
p {
font-size: clamp(1rem, 2.5vw, 2rem);
}Если же хочется свободного поведения, то можно использовать новые функции:
p {
--progress: progress(100vw, 320px, 1200px);
font-size: calc-mix(14px, 18px var(--progress));
}Из плюсов мы получаем более тонкую настройку (выставили минимум и максимум ширины) и более явные значения (в примере с clamp() нужно ещё понять, когда будет достигаться 1rem, а когда нет).
Ещё одна возможность – использование sibling-index() в вычислениях. Это позволит задавать отступ лесенкой, поворот по спирали и подобные вещи.
Итого
В CSS добавляют всё больше функций. И пусть сегодняшние герои не очень сложно заменяются на обычный calc(), добавление подобного синтаксического сахара всё равно радует – порой просто наличие функции наталкивает на новые идеи и способы применения.
Ну а самый главный итог для меня – очень жду “типизированной арифметики” во всех браузерах. А до тех пор нужно очень внимательно тестировать сложные calc(), особенно в Файерфоксе.




