Статьи

Понимание Sass Units

В прошлом я писал о юнитах в Sass для своего собственного блога, и не так давно, как фрагмент кода Sass CSS-Tricks , но учитывая то, как часто я замечаю замешательство и неправильное представление о том, как работают юниты в нашем любимом препроцессоре, я подумал, что полная статья не была бы такой плохой идеей в конце концов.

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

Эта битва, очевидно, еще не окончена, поэтому будем надеяться, что эта статья будет в некотором роде переворотом .

Единицы в реальной жизни

Прежде чем рассматривать код, я думаю, что важно понять, как числа и единицы ведут себя в реальной жизни. Возможно, вы еще этого не заметили, но в нашей повседневной жизни очень мало цифр, у которых нет устройства. Когда мы говорим «ограничение скорости 50», мы подразумеваем «50 километров в час»; в этом случае единица измерения — км / ч . Когда мы говорим «смотри, яйца! У меня их будет дюжина », блок — яйца : дюжина яиц. Конечно, есть исключения, но в большинстве случаев использования чисел в нашей повседневной жизни обычно используется единица измерения.

Аналогичным образом, выполнение математических операций над числами с единицами следуют некоторым правилам. Например, есть причины, по которым площадь 5 метров на 5 метров дает 25 квадратных метров ( ). То же самое для куба со стороной 1 метр: он имеет объем 1 м 3, потому что 1 метр на 1 метр на 1 метр дает не 3 метра, а 1 кубический метр.

Мне нравится, как говорит Крис Эппштейн :

2 фута ^ 2 — это 4 квадратных фута (площадь), а не 4 фута (что было бы длиной). В этом расчете правильно сделать так, чтобы единицы умножались вместе с числом. Самый простой способ думать, как правильно делать математику с единицами, — думать о единице, как о неизвестной величине в алгебре.
(2 * x) ^ 2 равно 4 * x 2. Если единицы измерения в конце вычисления не отменяются, чтобы стать ожидаемым измерением, это хороший признак того, что была допущена математическая ошибка.

Мы также должны понимать, что некоторые единицы буквально несовместимы. Например, каким будет результат 2 банана + 3 яблока? Что ж, получается, что мы не можем вычислить что-либо больше, чем 2 банана + 3 яблока , потому что яблоки и бананы не являются совместимыми единицами. С другой стороны, результат 1 метр + 10 сантиметров дает 1,1 метра или 110 сантиметров. Это потому, что метры и сантиметры являются частью метрической системы и, следовательно, совместимы.

Конечно, единицы измерения не обязательно должны быть частью метрической системы, чтобы быть совместимыми друг с другом. Например, световые годы и дюймы являются совместимыми единицами, поскольку они оба служат одной цели: измерению расстояний. Конечно, нет никакого способа, чтобы уравнение включало бы световые годы и дюймы, поскольку обе единицы живут на разных краях спектра единиц измерения расстояния. Тем не менее, они совместимы.

Единицы в Sass

Теперь, когда мы полностью осознали, как юниты работают в реальной жизни, пришло время заняться юнитами в Sass. Надеюсь, я вас не удивлю, когда скажу, что юниты в Sass ведут себя точно так же, как юниты в реальной жизни. Некоторые устройства совместимы, некоторые нет. Два числа с одинаковыми единицами, которые умножаются вместе, образуют квадратные единицы и так далее.

Давайте разберемся с одной вещью за раз и начнем со сравнения единиц. У Sass есть таблица совместимых единиц, которая позволяет выяснить, являются ли две единицы сопоставимыми и совместимыми или нет. Например, повороты (повороты) и градусы (градусы) совместимы; s (секунды) и pt (точки) — нет.

Это означает, что 12px + 1in — это совершенно корректная операция, результатом которой станет 108px . Почему 108px? ты спрашиваешь. Потому что 1 дюйм равен 96 пикселям, и результат операции выражается в единицах первого члена, которые в данном случае являются пикселями. Если бы мы написали 1in + 12px , результат был бы 1.125in .

Теперь квадратные единицы. Хотя верно, что умножение двух чисел на одну и ту же единицу должно давать квадратные единицы, на практике Sass не допускает использование квадратных единиц, поскольку они не имеют смысла в CSS. Когда такой случай возникает, Sass просто выдает ошибку примерно так:

4px * px не является допустимым значением CSS.

Мне потребовалось некоторое время, чтобы понять, что это сообщение не означает, что мы пытались выполнить 4px * px . В вышеупомянутом сообщении об ошибке px*px означает px² , поэтому сообщение также может быть:

4px² не является допустимым значением CSS.

Все это имеет смысл: любое значение, использующее px² в качестве единицы, или в принципе любую квадратную единицу в этом отношении, не является допустимым значением CSS. CSS не поддерживает квадратные единицы, поскольку нет смысла их иметь. Теперь, просто для записи: если вы попытаетесь запустить 4px * px , Sass выдаст другое сообщение об ошибке:

Неопределенная операция: «4 пикселя на пиксель».

В чем дело?

Итак, это уже было довольно долго, и, возможно, вы все еще не знаете, в чем смысл всего этого. Это момент, когда я делаю это, не волнуйтесь.

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

При применении логических арифметических принципов к Sass достаточно умножить наше значение на 1 единицу желаемой единицы.

 $value: 42; .foo { font-size: ($value * 1px); // {number} 42px } 

Теперь вы, вероятно, спросите себя, в чём проблема с простым добавлением модуля в виде строки, если он работает одинаково, и вы будете совершенно правы в большинстве сценариев. Теперь рассмотрим это:

 $value: 13.37; .foo { font-size: round($value + em); // {string} 13.37em } 

Можете ли вы догадаться, что произойдет, если мы попробуем скомпилировать этот код? Если ваш ответ 13em , прошу прощения, что вы ошибаетесь. Правильный ответ:

$ value: «13.37em» не является числом для «тура»

Это происходит потому, что простое добавление единицы справа от числа неявно приводит к приведению значения к строке, что приводит к неверному параметру для функции round (которая ожидает число).

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

На самом деле, все это имеет смысл, когда вы переворачиваете проблему с ног на голову и хотите удалить единицу числа. Если решение, которое сразу приходит вам на ум, является чем-то вроде:

 $value: 42px; $unitless-value: str-slice($value + ', 1, 2); // {string} 42 

… тогда я боюсь, что вы делаете это неправильно. Снятие устройства так же просто, как деление на одного члена устройства. Опять же, как в реальной жизни. Если вы хотите получить 25 из 25 м, вам нужно разделить 25 м на 1 м.

 $value: 42px; $unitless-value: (42px / 1px); // {number} 42 

Тогда у нас есть число, а не строка. Это абсолютно логично, элегантно и предотвращает любое странное поведение, которое может быть удивительным.

Случай 0

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

Мы можем использовать это в наших интересах, когда мы хотим преобразовать один модуль в другой (совместимый) модуль по той или иной причине. Например, допустим, вы хотите преобразовать секунды в миллисекунды. Я полагаю, вы могли бы умножить значение на 1000, чтобы получить миллисекунды. С другой стороны, вы можете оставить это для Sass, добавив значение в 0 миллисекунд, например так:

 $value: 42s; transition-duration: (0ms + $value); // {number} 42000ms 

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

 $resolution: 0dppx + 42dpcm; // {string} 1587.40157dppx $angle: 0rad + 42deg; // {string} 0.73304rad 

Если вы боитесь, что этот маленький трюк выглядит слишком «хакерским», чтобы использовать его как есть, вы можете создать короткую функцию, чтобы сделать API немного более дружественным. Я не уверен, стоит ли это хлопот, но я позволю вам судить об этом. Вот мой взгляд на эту функцию:

  

Переписав наш предыдущий пример:

 $resolution: convert-unit(42dpcm, 'dppx'); // {string} 1587.40157dppx $angle: convert-unit(42deg, 'rad'); // {string} 0.73304rad 

Последние мысли

Итак, если мы подведем итог тому, что мы видели в этой статье:

  • Юниты в Sass работают точно так же, как юниты в реальной жизни;
  • Чтобы присвоить числу единицу, умножьте значение на одного члена желаемой единицы (например, 42 * 1px );
  • Чтобы удалить единицу из числа, разделите на одного члена соответствующей единицы (например, 42px / 1px );
  • При сложении или вычитании двух чисел с разными совместимыми единицами результат выражается в единицах первого члена;
  • Чтобы преобразовать значение из одной единицы в другую (совместимо), добавьте значение к 0 члену последней единицы (например, 0px + 42in ).

Вот и все. Я надеюсь, что теперь мы увидим все меньше и меньше аберраций юнитов для более элегантной обработки юнитов. Как говорит Крис:

Я думаю, что математика Сасса действительно хороша, как только лампочка загорается!