Главная » Статьи » Полное руководство по calc() в CSS

Полное руководство по calc() в CSS

Полное руководство по calc() в CSS

От автора: в CSS есть специальная функция calc() для выполнения основных математических действий. В этом руководстве мы рассмотрим практически все, что нужно знать об этой очень полезной функции.

Вот пример:

.main-content { /* Вычесть 80px из 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 { /* Нет! */ counter-reset: calc("My " + "counter");
}
.el::before { /* Нет! */ 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 { /* Немного странно, но это работает */ width: calc(20px);
}

Не для медиа-запросов

Когда calc() используется правильно (единицы длины используются как значение для свойства), к сожалению, calc() не будет работать при применении к медиа-запросам.

@media (max-width: 40rem) { /* Уже или равно 40rem */
} /* Нет! */
@media (min-width: calc(40rem + 1px)) { /* Шире 40rem */
}

Было бы здорово получить это когда-нибудь, потому что вы могли бы выполнять взаимоисключающие медиа-запросы довольно логичным способом (как выше).

Различные единицы измерения

Это, пожалуй, самая полезная особенность calc()! Почти во всех приведенных выше примерах уже использовалось это, но просто чтобы подчеркнуть еще раз, мы можем смешивать разные единицы:

/* Проценты используются вместе с пикселями */
width: calc(100% - 20px);

Это значит: как ширина элемента, минус 20 пикселей.

Не существует никакого способа заранее рассчитать, каково будет значение в пикселях при использовании гибких единиц. Другими словами, вы не можете предварительно обработать calc(), как в Sass, как попытку завершить полизаполнение. Но это и не нужно, так как браузеры поддерживают это нормально. Но дело в том, что это должно быть сделано в браузере (во время выполнения), когда вы смешиваете таким образом единицы измерения, что является очень полезным.

Вот еще несколько примеров смешивания единиц измерения:

transform: rotate(calc(1turn + 45deg)); animation-delay: calc(1s + 15ms);

Это, вероятно, может быть предварительно обработано, поскольку здесь смешиваются единицы, которые не относятся ни к чему, что определяется во время выполнения.

Сравнение с математикой препроцессора

Мы только что рассмотрели, что вы не можете предварительно обработать самые полезные вещи с calc(). Но есть несколько дублирований. Например, в Sass встроена математика, поэтому вы можете делать такие вещи, как:

$padding: 1rem; .el[data-padding="extra"] { padding: $padding + 2rem; // предварительно вычисляется 3rem; margin-bottom: $padding * 2; // предварительно вычисляется 2rem; }

Даже математика с единицами измерения там работает, складывая значения одинаковых единиц вместе или умножая на числа без единиц измерения. Но вы не можете смешивать единицы измерения, и здесь ограничения подобны calc() (например, умножение и деление должно выполняться с числами без единиц измерения).

Отображение математики

Даже если вы не используете функцию, которая возможна только с помощью calc(), ее можно использовать, чтобы «показать свою работу» в CSS. Например, скажем, вам нужно точно рассчитать 1/7 ширины элемента…

.el { /* Это проще понять */ width: calc(100% / 7); /* Чем это */ 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 { /* Действительно */ margin: calc(10px + 10px); /* Недействительно */ margin: calc(10px + 5);
}

Недействительные значения делают недействительным все объявление.

Для деления (/) требуется, чтобы второе число было без единиц измерения.

.el { /* Действительно */ margin: calc(30px / 3); /* Недействительно */ margin: calc(30px / 10px); /* Недействительно (нельзя делить на 0) */ margin: calc(30px / 0);
}

Для умножения (*) требуется, чтобы одно из чисел было без единиц измерения.

.el { /* Действительно */ margin: calc(10px * 3); /* Действительно */ margin: calc(3 * 10px); /* Недействительно */ margin: calc(30px * 3px);
}

Пробелы имеют значение. Ну, это относится к сложению и вычитанию.

.el { /* Действительно */ font-size: calc(3vw + 2px); /* Действительно */ font-size: calc(3vw+2px); /* Действительно */ font-size: calc(3vw - 2px); /* Недействительно */ font-size: calc(3vw-2px);
}

Отрицательные числа допустимы (например, calc(5vw — -5px)), но это пример того, где пробел не только требуется, но и полезен.

Таб Аткинс говорит, что причина необходимости разделения + и — на самом заключается в проблемах с парсингом. Я не могу сказать, что полностью понимаю это, но, например, 2px-3px анализируется как число «2» и единица «px-3px», что никому не дает ничего хорошего, а у «+» есть другие проблемы, такие как «применение по числовому синтаксису». Я бы предположил, что пробел был бы связан с — синтаксисом пользовательских свойств, но нет!

Умножение и деление не требуют пробелов вокруг операторов. Но я думаю, что хороший общий совет — включить пробел для читаемости и мышечной памяти для других операторов. Пустое пространство вокруг не имеет значения. Вы даже можете вставить разрывы строк, если хотите:

.el { /* Действительно */ width: calc( 100% / 3 );
}

Но осторожнее с этим: между calc() и открывающимися скобками не должно быть пробелов.

.el { /* Недействительно */ 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 { /* Это */ width: calc(100% + 2rem / 2); /* существенно отличается от этого */ width: calc((100% + 2rem) / 2);
}

Пользовательские свойства CSS и 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));
}

Может быть, вам это не понравится, так как вам нужно запоминать, где вы используете свойство, но это возможно и потенциально интересно с точки зрения читабельности.

Пользовательские свойства могут исходить из HTML, который иногда бывает чертовски крутым и полезным. (Посмотрите в качестве примера, как Splitting.js добавляет индексы для слов / символов.)

<div style="--index: 1;"> ... </div>
<div style="--index: 2;"> ... </div>
<div style="--index: 3;"> ... </div>

div { /* Значение индекса исходит из HTML (с резервным вариантом) */ animation-delay: calc(var(--index, 1) * 0.2s);
}

Добавление единиц измерения позже

Если в вашей ситуации проще хранить числа без единиц измерения или выполнять математику с числами без единиц измерения заранее, вы всегда можете подождать, пока не примените число, чтобы добавить единицы измерения путем умножения на 1 с единицей измерения.

html { --importantNumber: 2;
} .el { /* Число остается 2, но теперь оно имеет единицы измерения */ 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 { /* Нет */ color: attr(data-color);
}

К сожалению, здесь нет «типов», поэтому единственное, что нужно attr() — это строки в сочетании со свойством content. Это означает, что это работает:

div::before { content: attr(data-color);
}

Я упоминаю об этом, потому что может показаться заманчивым попытаться получить число таким образом, чтобы использовать его в вычислениях, например:

<div class="grid" data-columns="7" data-gap="2">...</div>

.grid { display: grid; /* Ничего из этого не будет работать */ 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; /* Да! */ grid-template-columns: repeat(var(--columns), 1fr); grid-gap: calc(var(--gap));
}

Инструменты браузера

Инструменты разработчика браузера показывают calc(), как вы его создали в таблице стилей.

Полное руководство по calc() в CSS

Инструменты разработчика Firefox — правила

Если вам нужно выяснить вычисленное значение, есть вкладка Вычисленные (в инструментах разработчика всех браузеров, по крайней мере тех, о которых я знаю), которая показывает это.

Полное руководство по calc() в CSS

Инструменты разработчика Chrome — Вычисленные

Поддержка браузерами

Эти данные по поддержке браузерами взяты с Caniuse, где вы можете найти более подробную информацию. Число указывает, что браузер поддерживает функцию с этой версии и выше.

Если вам действительно нужно поддерживать супер-старую версию (например, IE 8 или Firefox 3.6), обычный хак — добавить еще одно свойство или значение перед тем, которое использует calc():

.el { width: 92%; /* Резервный вариант */ width: calc(100% - 2rem);
}

Есть также несколько известных проблем с calc(), но они все связаны со старыми браузерами. На Can I Use… перечислены 13, вот некоторые из них:

Firefox <59 не поддерживает calc() для функций цветов. Пример: color: hsl(calc(60 * 2), 100%, 50%).

IE 9 — 11 не будет отображать свойство box-shadow, когда calc() используется для любого из значений.

Ни IE 9-11, ни Edge не поддерживают width: calc() для ячеек таблицы.

Примеры использования

Я спросил некоторых разработчиков CSS, когда они использовали в последний раз calc(), чтобы дать вам представление о том, как другие применяют ее в своей повседневной работе.

Я использовал ее, чтобы создать полноценный служебный класс: .full-bleed { width: 100vw; margin-left: calc(50% — 50vw); } Я бы сказал, что calc() входит в мой Топ-3 вещей CSS.

Я использовал ее, чтобы освободить место для липкого футера.

Я использовал ее, чтобы установить некоторый гибкий шрифт / динамическую типографику … для расчетов font-size на основе минимумов, максимумов и степени изменения в единицах измерения, связанных с областью просмотра. Не только font-size, но и line-height тоже.

Если вы используете calc() как часть ситуации с гибкими шрифтами, которая включает единицы измерения, связанные с областью просмотра и тому подобное, убедитесь, что вы включаете единицу, которая использует rem или em так, чтобы пользователь все еще имел некоторый контроль над увеличением или уменьшением этого шрифта путем увеличения или уменьшения масштаба.

Мне действительно нравится иметь собственное свойство «ширина содержимого», а затем использовать его для создания нужного мне отступа, например полей: .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, но особенно ограничения в позиционировании переходов цветов в градиентах. Например, «переход на 0.75em от низа».

Автор: Chris Coyier

Источник: https://css-tricks.com

Редакция: Команда webformyself.