css calc - округлить с двумя десятичными знаками

У меня следующая ситуация:

div {
    width: calc((100% / 11) - 9.09px);
}

В контексте 100% = 1440px, и 9.09px генерируется в математике с sass.

Результат: 94.55px, потому что функция calc округляется в большую сторону, но мне нужно 94.54px (округляется в меньшую сторону).

Как я могу округлить до ближайшего сотого места?

# Редакция: пример.

6 ответов

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

div {
    --shf: 4.9406564584124654e-322;
    width: calc(((100% / 11) - 9.09px) * var(--shf) / var(--shf));
}

Что это делает: оно умножает округляемое значение на очень маленькое значение, которое выходит за пределы значения, начиная с третьего десятичного знака. Затем он делит усеченное значение обратно, в результате чего получается округленная версия значения. Это предполагает, что все поддерживаемые вами браузеры используют 64-битные значения с плавающей запятой. Если они этого не сделают, это не только будет ошибкой, но и может вернуть ноль при использовании меньших типов данных с плавающей запятой, что полностью нарушит вашу страницу.

Измените показатель степени на -323, чтобы округлить до первой десятичной точки, и на -324, чтобы округлить до целых значений.

К сожалению, в CSS нет собственного способа округлять (или потолок / пол) числа.

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

Например, если у вас было 94.546 вы могли бы использовать decimal-floor(94.546, 2) который бы вернулся 94.54,

К сожалению, это может не помочь, если вам придется использовать calc() рассчитать на лету с помощью CSS. Однако, если вы можете предварительно рассчитать width и напишите это с Sass, это соответствовало бы вашим потребностям. Возможным решением может быть использование @media запросы как способ установки точек останова и использования этих точек прерывания в вашей предварительной обработке Sass.

Функция CSS

В CSS Values ​​and Units Module Level 4 вы можете округлить число или размер по своему усмотрению с помощью round()Функция CSS.

      round(<rounding-strategy>, valueToRound, roundingInterval)

Необязательныйrounding-strategyможет быть одним изup,down,nearest(по умолчанию) иto-zero, которые соответственно соответствуютMath.ceil,Math.floor,Math.round, иMath.truncв Javascript.

Пример

Чтобы изменить размер<canvas>элемента почти (интервал округления представляет собой пиксель CSS , а не пиксель устройства.) соответствует его родительскому элементу, сохраняя соотношение сторон пикселя холста 1:1 (при условии, чтоwindow.devicePixelRatioявляется натуральным числом):

      canvas {
    display: block;
    width: round(down, 100%, 1px);
    height: round(down, 100%, 1px);
}

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

В настоящее время это поддерживает только Safari .

Смотрите также

Если вы динамически генерируете свой CSS, я создал небольшой класс для выполнения простых функций, таких как round(), ceil(), floor(), abs(), mod() и sign() -- его можно использовать для построения сложных вычислений. правила, которые вы можете передавать друг другу в качестве аргументов. Если вы используете «var(--my-variable)» в качестве параметра, будет использоваться переменная CSS. Примечание: он лучше всего работает с необработанными числами и имеет вспомогательную функцию toUnit для преобразования в пиксели (и т. д.) в конце.

вы можете назвать это так, jquery в качестве примера, чтобы установить стиль:

       $('#my-element').css('width', `${cssHack.toUnit(cssHack.round(1000/3),'px')}`)

Ниже приведен код класса (es6, без зависимостей):

      class cssHack
{
    //primary used for dynamic css variables-- pass in 'var(--dynamic-height)'
    static toUnit(val,unit)
    {
        unit='1'+unit;
        return ` calc(${val} * ${unit}) `;
    }

    static round(val)
    {
        // the magic number below is the lowest possible integer, causing a "underflow" that happily results in a rounding error we can use for purposeful rounding.
        return ` calc(${val} * ${Number.MIN_VALUE} / ${Number.MIN_VALUE}) `;
    }

    static abs(val)
    {
        return ` max(${val}, calc(${val} * -1)) `;
    }

    static floor(val)
    {
        return cssHack.round(` calc(${val} - .5) `);
    }

    static ceil(val)
    {
        return cssHack.round( `calc(${val} + .5) `);
    }

    static sign(val)
    {   
        let n = ` min(${val},0) `; //if val is positive then n =0. otherwise n=val.
        let isNegative= ` min(${cssHack.ceil(cssHack.abs(n))},1) `;
        let p = ` max(${val},0) `; //if val is negative then n=0, otherwise n = val;
        let isPositive= ` min(${cssHack.ceil(cssHack.abs(p))},1) `;
        return ` calc(${isPositive} + calc(${isNegative} * -1)) `;
    }

    static mod(val, base)
    {
        let abs = cssHack.abs(val);
        let div = ` calc(${abs} / ${base})`;
        let dec = ` calc(${div} - ${cssHack.floor(div)})`;
        return cssHack.round(` calc(${dec} * ${base}) `);
    }
}

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

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

.some-block {
    width: calc( ( 100% / 11 ) - 9.09px );
    flex-shrink: 0;
}

.some-block:nth-of-type(5) {
    // Will attempt to take the whole width, but its siblings refuse to shrink, so it'll just take every bit of remaining space.
    width: 100%;
}

В зависимости от вашего конкретного случая CSS, вычитания пикселей можно вообще избежать, используя отступы или поля и, возможно, дополнительный вложенный элемент. Таким образом, CSS calc() становится неактуальным, и все это можно сделать в Sass, который имеет гораздо более мощные математические инструменты и не заставляет клиента работать для вычислений.

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

В таблице, поскольку ширина ячеек вычисляется слева направо, можно использовать аналогичный трюк, но вам придется указать width: 100%до последней ячейки, так как нет понятия flex-shrink ширина столбца определяется слева направо.

.my-table {
    table-layout: fixed;
}

.table-cell {
    width: calc( ( 100% / 11 ) - 9.09px );
}

.table-cell:last-of-type {
    // Will attempt to take the whole width, but since previous column widths have already been determined, it cannot grow any further than its allocated width plus any stray pixels.
    width: 100%;
}

ты не можешь с дерзостью? поскольку вы уже используете его, попробуйте его методы round, floor и ceil:https://sass-lang.com/documentation/modules/math#bounding-functions

(также в более старых версиях sass, но с другим синтаксисом)

Другие вопросы по тегам