В CSS для выполнения основных математических операций есть специальная функция calc(). В этой статье мы рассмотрим практически все, что нужно знать об этой очень полезной функции.
Вот пример:
.main-content {
/* Subtract 80px from 100vh */
height: calc(100vh - 80px);
}
Функция calc() для значений
Единственный вариант, где вы можете использовать функцию calc() — это значения. Посмотрите эти примеры, где мы устанавливаем значение для ряда различных свойств.
.el {
font-size: calc(3vw + 2px);
width: calc(100% - 20px);
height: calc(100vh - 20px);
padding: calc(1vw + 5px);
}
Его также можно использовать только для части ресурса, например:
.el {
margin: 10px calc(2vw + 5px);
border-radius: 15px calc(15px / 3) 4px 2px;
transition: transform calc(1s - 120ms);
}
Это может быть даже часть другой функции, которая является частью свойства! Например, вот функция calc(), используемая в цветовых точках градиента.
.el {
background: #1E88E5 linear-gradient(
to bottom,
#1E88E5,
#1E88E5 calc(50% - 10px),
#3949AB calc(50% + 10px),
#3949AB
);
}
Функция calc()предназначена для различных числовых значений
Обратите внимание, что все приведенные выше примеры основаны на числах. Мы рассмотрим ряд предостережений относительно того, как можно использовать числа (потому что иногда вам не нужны единицы измерения), но это не касается строк или чего-то подобного.
.el {
/* Nope! */
counter-reset: calc("My " + "counter");
}
.el::before {
/* Nope! */
content: calc("Candyman " * 3);
}
Однако существует много величин CSS, и все они могут использоваться с функцией calc():
px
%
em
rem
in
mm
cm
pt
pc
ex
ch
vh
vw
vmin
vmax
Безразмерные величины также допустимы. Например, line-height: calc(1.2 * 1.2);
а также такие свойства углов, как transform:
.
rotate(calc(10deg * 5));
Вы также можете не выполнять какие-либо расчеты, и она по-прежнему валидна:
.el {
/* Little weird but OK */
width: calc(20px);
}
Нет медиа-запросам
Когда calc() используется правильно (единицы длины используются в качестве значения свойства), к сожалению, calc() не будет работать для медиа-запросов.
@media (max-width: 40rem) {
/* Narrower or exactly 40rem */
}
/* Nope! */
@media (min-width: calc(40rem + 1px)) {
/* Wider than 40rem */
}
Когда-нибудь вы сможете делать взаимоисключающие медиа-запросы довольно логичным способом (как показано выше).
Смешанные единицы 🎉
Это, пожалуй, самая ценная функция calc()! Почти в каждом приведенном выше примере уже есть смешанные единицы, но для наглядности, взгляните на этот пример:
/* Percentage units being mixed with pixel units */
width: calc(100% - 20px);
Это говорит, что ширина такая же, как у элемента, минус 20 пикселей.
С плавающей шириной невозможно предварительно вычислить это значение только в пикселях. Другими словами, вы не можете предварительно обрабатывать calc() с помощью Sass, чтобы сделать полифилл. Не то чтобы вам это было нужно, поскольку нет проблем с поддержкой браузера. Но дело в том, что это должно быть сделано на стороне браузера (“runtime”), когда вы смешиваете единицы измерений.
Вот еще несколько примеров смешанных единиц:
transform: rotate(calc(1turn + 45deg));
animation-delay: calc(1s + 15ms);
Вероятно, их можно было бы предварительно обработать, поскольку смешанные единицы не связаны с параметрами, определяемыми runtime.
Сравнение с математическими расчетами препроцессора
Только что мы узнали, что вы не можете использовать calc() для препроцессинга. Но есть выход. В Sass встроены математические расчеты, поэтому вы можете делать подобные вещи:
$padding: 1rem;
.el[data-padding="extra"] {
padding: $padding + 2rem; // processes to 3rem;
margin-bottom: $padding * 2; // processes to 2rem;
}
Даже возможны математические расчеты с единицами измерения: сложение значений с одинаковыми единицами измерения или умножение на безразмерные величины. Но вы не можете смешивать единицы измерения, и существуют ограничения, аналогичные calc() (например, умножение и деление должны выполняться с числами без единиц измерения).
Показать расчеты
calc() можно использовать, чтобы «показать расчеты» внутри CSS. Например, вам нужно рассчитать ровно 1⁄7 ширины элемента…
.el {
/* This is easier to understand */
width: calc(100% / 7);
/* Than this is */
width: 14.2857142857%;
}
Это может сработать в каком-то самостоятельно созданном CSS API, например:
[data-columns="7"] .col { width: calc(100% / 7); }
[data-columns="6"] .col { width: calc(100% / 6); }
[data-columns="5"] .col { width: calc(100% / 5); }
[data-columns="4"] .col { width: calc(100% / 4); }
[data-columns="3"] .col { width: calc(100% / 3); }
[data-columns="2"] .col { width: calc(100% / 2); }
Математические операторы calc()
У вас есть +, -, * и /. Но они отличаются тем, как вы должны их использовать.
Сложение (+) и вычитание (-) требуют, чтобы оба числа были длинами
.el {
/* Valid 👍 */
margin: calc(10px + 10px);
/* Invalid 👎 */
margin: calc(10px + 5);
}
Недопустимые значения делают недействительным декларирование.
Деление (/) требует, чтобы второе число было безразмерным
.el {
/* Valid 👍 */
margin: calc(30px / 3);
/* Invalid 👎 */
margin: calc(30px / 10px);
/* Invalid 👎 (can't divide by 0) */
margin: calc(30px / 0);
}
Умножение (*) требует, чтобы одно из чисел было безразмерным
.el {
/* Valid 👍 */
margin: calc(10px * 3);
/* Valid 👍 */
margin: calc(3 * 10px);
/* Invalid 👎 */
margin: calc(30px * 3px);
}
Пробелы имеют значение
Когда речь идет о сложении и вычитании.
.el {
/* Valid 👍 */
font-size: calc(3vw + 2px);
/* Invalid 👎 */
font-size: calc(3vw+2px);
/* Valid 👍 */
font-size: calc(3vw - 2px);
/* Invalid 👎 */
font-size: calc(3vw-2px);
}
Отрицательные числа допустимы (например, calc (5vw – -5px)), но это пример того, когда пробелы не только необходимы, но полезны.
Таб Аткинс говорит, что причина, по которой необходим интервал между + и - на самом деле связана с проблемами синтаксического анализа. Я не могу сказать, что полностью это понимаю, но, например, 2px-3px обрабатывается как число «2» и единица измерения «px-3px». А у + есть другие проблемы. Я думал, что пробелы связаны с синтаксисом пользовательских свойств, но нет!
Умножение и деление не требуют пробелов вокруг операторов. Но, думаю, что нужно добавить пробел ради удобочитаемости и мышечной памяти для других операторов.
Пустое пространство снаружи не имеет значения. Вы даже можете делать разрывы строк, если хотите:
.el {
/* Valid 👍 */
width: calc(
100% / 3
);
}
Однако будьте осторожны: между calc() и открывающей скобкой не должно быть пробелов.
.el {
/* Invalid 👎 */
width: calc (100% / 3);
}
Вложение calc(calc());
Так делать можно, но не нужно. Это то же самое, что использовать дополнительный набор скобок без calc() . Например:
.el {
width: calc(
calc(100% / 3)
-
calc(1rem * 2)
);
}
Они не нужны вам внутри calc(), потому что скобки работают сами по себе:
.el {
width: calc(
(100% / 3)
-
(1rem * 2)
);
}
И в этом случае нам помогает «порядок действий» даже без скобок. То есть сначала происходят деление и умножение (перед сложением и вычитанием), поэтому скобки вообще не нужны. Можно было бы написать так:
.el {
width: calc(100% / 3 - 1rem * 2);
}
Но не стесняйтесь использовать круглые скобки, если считаете, что это добавляет ясности. Если порядок операций не работает в вашу пользу (например, вам действительно нужно сначала выполнить сложение или вычитание), вам понадобятся скобки.
.el {
/* This */
width: calc(100% + 2rem / 2);
/* Is very different from this */
width: calc((100% + 2rem) / 2);
}
Пользовательские свойства CSS и calc() 🎉
Помимо удивительной способности calc() смешивать единицы измерения, следующая замечательная особенность calc() – его использование с пользовательскими свойствами. Пользовательские свойства могут иметь значения, которые затем используются в расчетах:
html {
--spacing: 10px;
}
.module {
padding: calc(var(--spacing) * 2);
}
Уверен, что вы можете настроить свойства CSS, в которых будет множество переменных, устанавливая тонны пользовательских свойств CSS, а затем позволяя остальной части CSS использовать их по мере необходимости.
Пользовательские свойства также могут ссылаться друг на друга. Вот пример, где используется ряд математических операций (обратите внимание на отсутствие функции calc() сначала). В конечном итоге они должны находиться внутри функции calc().
html {
--spacing: 10px;
--spacing-L: var(--spacing) * 2;
--spacing-XL: var(--spacing) * 3;
}
.module[data-spacing="XL"] {
padding: calc(var(--spacing-XL));
}
Вам это может не понравиться, поскольку нужно помнить о calc(), где вы затем используете это свойство, но это возможно и потенциально интересно с точки зрения удобочитаемости.
Пользовательские свойства могут исходить из HTML, что иногда чертовски круто и полезно. (Посмотрите, как Splitting.js добавляет индексы к словам/символам).
<div style="--index: 1;"> ... </div>
<div style="--index: 2;"> ... </div>
<div style="--index: 3;"> ... </div>
div {
/* Index value comes from the HTML (with a fallback) */
animation-delay: calc(var(--index, 1) * 0.2s);
}
Добавьте единицы позже
В случае, если вы находитесь в ситуации, когда проще хранить числа без единиц измерения или заранее выполнять математические операции с безразмерными числами, вы всегда можете подождать, пока не примените число, чтобы добавить unit, умножив его на 1 и единицу измерения.
html {
--importantNumber: 2;
}
.el {
/* Number stays 2, but it has a unit now */
padding: calc(var(--importantNumber) * 1rem);
}
Игра с цветами
Цветовой формат, такой как RGB и HSL, имеет числа, с которыми вы можете работать, используя calc(). Например, установив базовые значения HSL, а затем изменив их, создав собственную систему, например:
html {
--H: 100;
--S: 100%;
--L: 50%;
}
.el {
background: hsl(
calc(var(--H) + 20),
calc(var(--S) - 10%),
calc(var(--L) + 30%)
)
}
Вы не можете комбинировать calc() и attr()
Функция attr() в CSS выглядит привлекательно, как будто вы можете извлекать значения атрибутов из HTML и использовать их. Но…
<div data-color="red">...</div>
div {
/* Nope */
color: attr(data-color);
}
К сожалению, здесь нет «types», поэтому attr() предназначена только для строк в сочетании со свойством content.
div::before {
content: attr(data-color);
}
Я упоминаю об этом, потому что может возникнуть соблазн попытаться вывести число таким образом, чтобы использовать его в расчетах, например:
<div class="grid" data-columns="7" data-gap="2">...</div>
.grid {
display: grid;
/* Neither of these work */
grid-template-columns: repeat(attr(data-columns), 1fr);
grid-gap: calc(1rem * attr(data-gap));
}
К счастью, это не имеет большого значения, потому что пользовательские свойства в HTML не менее полезны!
<div class="grid" style="--columns: 7; --gap: 2rem;">...</div>
.grid {
display: grid;
/* Yep! */
grid-template-columns: repeat(var(--columns), 1fr);
grid-gap: calc(var(--gap));
}
Инструменты браузера
DevTools браузера попросит вас показать вам calc() в том виде, в котором вы его создали в таблице стилей.
Firefox DevTools
Если вам нужно определить расчетное значение, есть вкладка «Computed» (в DevTools всех браузеров, по крайней мере, тех, о которых я знаю), которая покажет вам его.
Chrome DevTools
Поддержка браузера
Если вам действительно нужна поддержка очень старой версии браузера (например, IE 8 или Firefox 3.6) добавьте другое свойство или значение перед тем, которое использует calc():
.el {
width: 92%; /* Fallback */
width: calc(100% - 2rem);
}
Также известно несколько проблем с функцией calc(), но все они относятся к старым браузерам. Существует список из 13 известных проблем. Вот несколько из них:
- Firefox до 59 версии не поддерживает calc() для функций цвета. Пример: color:
hsl(calc(60 \* 2), 100%, 50%)
. - IE 9–11 не будет отображать свойство box-shadow, если для любого из значений используется calc().
- Ни IE 9–11, ни Edge не поддерживают функцию
width: calc()
в ячейках таблицы.
Сценарии использования
Также известно несколько проблем с функцией calc(), но все они относятся к старым браузерам. Существует список из 13 известных проблем. Вот несколько из них:
Я использовал ее для создания полноценного служебного класса:
.full-bleed { width: 100vw; margin-left: calc(50% - 50vw); }
Я бы сказал, что calc() входит в тройку моих любимых функций CSS.Я использовал ее, чтобы освободить место для липкого футера.
Я использовал ее, чтобы установить fluid type / dynamic typography… вычисленный font-size, основанный на минимуме, максимуме и скорости изменения единиц области просмотра. Не только font-size, но и line-height.
Если вы используете calc() как часть fluid type, которая включает единицы области просмотра, убедитесь, что добавили единицу, использующую rem или em, чтобы у пользователя все еще был контроль над увеличением или уменьшением шрифта с помощью увеличение или уменьшение масштаба по мере необходимости.
Мне очень нравится добавлять пользовательское свойство «content width», а затем использовать его для создания необходимого интервала, например полей:
.margin { width: calc( (100vw - var(--content-width)) / 2); }
Я использовал ее для создания кросс-браузерного компонента drop-cap. Вот его часть:
.drop-cap { --drop-cap-lines: 3; font-size: calc(1em * var(--drop-cap-lines) * var(--body-line-height)); }
Я использовал ее, чтобы некоторые изображения выходили за пределы контейнера на странице статьи.
Я использовал ее, чтобы правильно разместить на странице визуальные элементы, комбинируя ее с отступами и единицами vw/vh.
Я использую ее, чтобы преодолеть ограничения в background-position, но особенно ограничения в позиционировании точек stop-color в градиентах. Например, «stop 0.75em short of the bottom».
Перевод статьи css-tricks.com
Топ коментарі (0)