Функции за CSS променливи (част 2)
В предния урок разгледахме някои математически функции в CSS, които работят добре с променливи. На https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#math_functions обаче има и други, за които ще поговорим сега.
За съжаление, никоя от останалите функции (като sin()
, cos()
, exp()
, abs()
, sign()
, mod()
, pow()
, log()
, sqrt()
и т.н.) все още не се поддържа. Това е защото все още са на ниво чернова, която предстои да бъде обсъждана и приета, преди да бъде въведена като краен стандарт.
Все пак има начин поне част от тези функции (или техни алтернативи) да се ползват и сега.
Преди да задълбаем трябва да си призная, че тази статия е базирана на една много малка част от https://css-tricks.com/using-absolute-value-sign-rounding-and-modulo-in-css-today/, която просто съм превел на български. Така че, който няма проблеми с английския – нека направо види оригинала в CSS Tticks, тъй като е доста по-пълен.
pow()
Първата функция, която ще разгледаме е степенуване. Ако смятате да използвате променлива за степенния показател вероятно няма решение. Но ако с променлива искате да означите само основата, тогава pow(var(--some-var), 3)
би могло да се замени просто с:
--pow-3: calc(var(--some-var) * var(--some-var) * var(--some-var))
abs()
Да продължим с abs()
. Би трябвало тази функция да върне абсолютната стойност на числото, което сме ѝ подали. Така че abs(100)
трябва да е същото като abs(-100)
. Още тук идва въпросът: „а abs(100px)
ще бъде ли същото като abs(-100px)
или мерни единици не са позволени?“. Според текущата версия на спецификацията, предложението в превод гласи нещо като:
Функцията
CSS Стойности и Мерни единици от ниво 4abs(A)
съдържа едно изчисление „A“ и връща абсолютната стойност на „A“ от същия тип като входния параметър: ако числовата стойност на „A“ е положителна, резултатът би бил просто „A“; в противен случай би бил-1 * A
.
Работна чернова на W3C от 16 декември 2021
Тълкуване на дефиницията
Ако я разгледаме внимателно можем да анализираме следните части:
- „стойността на А от същия тип като входния параметър“ означава, че ако имаме мерна единица тя трябва да се запази
- „ако числовата стойност на А е положителна“ означава, че ако игнорираме мерната единица трябва А да е полочително число
- „би бил
-1 * A
“ е препратка към възможността наcalc()
да приема стойности с различни мерни единици
По последната точка – възможността на calc()
да приема стойности с различни мерни единици е една от най-важните функционалности. В крайна сметка и сами можем да сметнем calc(5px + 10px)
и употребата на calc()
в такъв случай се обезсмисля. Резултатът от функцията, обаче не можем да опишем по друг начин, ако имаме calc(5% + 10px)
, тъй като едната част зависи от размера на родителския елемент (или от viewport-а), а другата част винаги е 10px
.
От там следва, че можем да направим и calc(15 * 1deg)
. На пръв поглед това също е малко безсмислено, тъй като можем да напишем просто 15deg
, но на този тип употреба на calc()
ще се спрем в бъдещ урок от поредицата, когато числовата стойност в изчислението идва от JavaScript (и няма мерна единица) и трябва да умножим по 1deg
или 1px
или 1
следвано от каквато и да е друга мерна единица, тъй като това е начинът, по който можем да придадем измерение на иначе числовата стойност.
И как да пресметнем абсолютната стойност на накоя променлива в CSS?
Отговорът е:
--abs: max(var(--some-var), calc(-1 * var(--some-var)));
Това, което правим, е доста близко до спецификацията: взимаме или стойността на променливата такава, каквато е, или стойността ѝ умножена по -1
. Тъй като не можем да имаме if
в CSS, можем с помощта на max()
да вземем което от двете е по-голямото число.
sign()
Продължаваме със sign()
, защото е доста близка като концепция. Идеята е тази функция да ни каже дали числото е положително (връща 1
) или отрицателно (връща -1
).
Някои хора предлагат първо да си дефинираме --abs
както е дадено по-горе, а след това да добавим:
--sign: calc(var(--some-var) / abs(var(--some-var)))
Идеята е, че -5 / 5
дава -1
, а 10 / 10
дава 1
. Доста лесно решение, нали?
Проблемът идва, когато стойността на --some-var
е 0. Тогава имаме делене на 0, което не е позволено.
По-доброто решение за sign()
Затова по-добро решение е:
--sign: clamp(-1, var(--some-var), 1)
Така ограничаваме стойността да варира между -1
и 1
. Ако променливата е по-малка (примерно -5
) ще получим резултат от долната граница (-1
). Ако пък е по-голяма (примерно 10
): ще получим горната граница.
Тук обаче имаме проблем с променливи, чиято абсолютна стойност е между 0 и 1, като например 0.5 или даже 0.00005.
Още по-доброто решение за sign()
За да оправим това някои хора просто умножават променливата по някакво доста голямо число (примерно 99999999), което ни гарантира, че версията на --sign
ще работи, стига абсолютната стойност на променливата да е по-голяма от 0.00000001
. И ако това не ни е достатъчно – винаги може да ползваме по-голямо число при умножението.
--sign: clamp(-1, calc(99999999 * var(--some-var)), 1)
Да, не е технически издържано и някой винаги може да вмъкне още една нула след десетичната запетая и преди първата знакова цифра, но затова и ползваме някакво временно решение, докато в CSS официално не вкарат sign()
функция.
round()
, floor()
, ceil()
, mod()
На тези функции също е обърнато внимание в статията от CSS Tricks, но аз лично няма да ги разгледам. Това е защото решението е базирано на @property
синтаксиса за деклариране на променливи, който позволява да ограничим стойността им до цяло число. Ако се опитаме да назначим дробна стойност на такава променлива, то дробната част просто ще бъде изрязана.
@property
синтаксисът обаче все още не се поддържа от Firefox и Safari. Така че пак няма да имаме поддръжка на всички браузъри и тогава: защо да имитираме тези функции? Ако имате добър отговор на въпроса – тогава се чувствайте свободни да прилагате тези допълнителни решения.