Использование calc(), vw, брейкпоинтов и линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

От автора: сегодня мы поговорим о том, как откалибровать в CSS размер элементов. При работе с креативными дизайнерами над дизайнами веб-страниц довольно часто приходится получать несколько набросков или макетов Sketch или Photoshop, по одному на каждый брейкпоинт.

В таком дизайне элементы (например, h1 заголовок) вероятнее всего будут разных размеров в каждом брейкпоинте. Например:

При малом макете h1 22px

На макете среднего размера 24px

При большой компоновке может быть 34px

Практический курс по верстке адаптивного лендинга с нуля!

Научитесь с нуля верстать адаптивные лендинги на HTML5 и CSS3 за ближайшие 6 дней

Узнать подробнее

Минимальный CSS использует для этого медиа-запросы:

h1 {
  font-size: 22px;
}
@media (min-width:576px) {
  h1 {
 font-size: 22px;
  }
}
@media (min-width:768px) {
  h1 {
 font-size: 24px;
  }
}
@media (min-width:992px) {
  h1 {
 font-size: 34px;
  }
}

Это хорошо, но при изменении размера окна браузера это немного нелепо. В каждом брейкпоинте размер текста скачет выше/ниже по размеру. Было бы здорово, если бы изменение шрифтов между макетами было полностью текучим. Ваш макет жидкий. Почему font-sizes не может быть таким? Для сглаживания этого перехода можно использовать CSS-переход:

h1 {
  font-size: 22px;
  transition: font-size 0.2s;
}

Теперь это не так сильно раздражает, но прыжок явно все еще там. Что еще мы можем с этим сделать?

Помогут ли вьюпорт единицы?

Единицы вьюпорт — это шаг в правильном направлении. Они позволяют вашему тексту гибко изменять размеры с помощью макетов. И поддержка браузеров в наши дни велика.

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

Жизнеспособность единиц вьюпорт очень сильно зависит от оригинальных креативных проектов веб-страницы. Было бы здорово просто настроить font-size, используя vw и сделать так:

h1 {
  font-size: 2vw;
}

Но это работает, только если творческие макеты учитывают это. Думаете, это дизайнер выбрал размер текста, который составил ровно 2% от ширины каждой из его макетов? Конечно нет. Давайте вычислим, какое vw значение должно быть для каждого брейкпоинта:

22px size @ 576px wide = 22/576 = 3.82% of the width
24px size @ 768px wide = 24/768 = 3.13% of the width
34px size @ 992px wide = 34/992 = 3.43% of the width

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

@ 767px, 3,82% ширины окна просмотра — 29 пикселей. Стоит сделать размер браузера на 1 пиксель шире, и font-size внезапное падает до 24 пикселей. Это странно.

Итак, как мы решаем эту проблему?

Статическая линейная регрессия?

Стоп. Что? Да, это статья о CSS, но некоторые основные математические решения могут значительно помочь нам элегантно решить эту проблему. Во-первых, давайте нарисуем наши разрешения и соответствующие размеры текста на графике:

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

Здесь можно увидеть график разброса заданных дизайнером размеров текста при определенной ширине вьюпорт. Видите эту линию? Это называется трендлайн. Это своего рода способ найти интерполированное значение font-size для любой ширины окна просмотра на основе данных, которые мы предоставили.

Трендлайн — ключ ко всему

Если бы вы могли установить font-size позицию в соответствии с трендлайном, то у вас был бы h1, который плавно масштабируется во всех разрешениях, приближенных к тому, что предполагал дизайнер. Давайте вспомним математику. Прямая определяется этим уравнением:

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

m = наклон

b = y-перехват.

x = текущая ширина окна

y = результат font-size

Существуют несколько методов определения наклона и у-перехвата. Но общим методом является установка наименьших квадратов:

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

После выполнения этих расчетов у вас есть уравнение трендлайна.

Как это используется в CSS?

Согласен, это довольно тяжело. Как я действительно использую этот материал в веб-разработке во front-end? Ответ — CSS calc()! Повторю, это довольно новая технология CSS, которая очень хорошо поддерживается.

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

Вы можете использовать трендлайн-уравнение следующим образом:

h1 {
  font-size: calc({slope}*100vw + {y-intercept}px);
}

Как только вы найдете свой наклон и y-перехват, просто подключите их и Viola!

Примечание. Вам нужно умножить наклон на 100, поскольку вы используете его как vw единицу, которая составляет 1/100-й ширины вьюпорт.

Может ли это быть автоматизировано?

Я поместил метод наименьших квадратов в удобную функцию SASS:

/// leastSquaresFit
/// Calculate the least square fit linear regression of provided values
/// @param {map} $map - A SASS map of viewport width and size value combinations
/// @return Linear equation as a calc() function
/// @example
/// font-size: leastSquaresFit((576: 24, 768: 24, 992: 34));
/// @author Jake Wilson <jake.e.wilson@gmail.com>
@function leastSquaresFit($map) {
 
  // Get the number of provided breakpoints
  $length: length(map-keys($map));
 
  // Error if the number of breakpoints is < 2
  @if ($length < 2) {
 @error "leastSquaresFit() $map must be at least 2 values"
  }
 
  // Calculate the Means
  $resTotal: 0;
  $valueTotal: 0;
  @each $res, $value in $map {
 $resTotal: $resTotal + $res;
 $valueTotal: $valueTotal + $value;
  }
  $resMean: $resTotal/$length;
  $valueMean: $valueTotal/$length;

  // Calculate some other stuff
  $multipliedDiff: 0;
  $squaredDiff: 0;
  @each $res, $value in $map {
 
 // Differences from means
 $resDiff: $res - $resMean;
 $valueDiff: $value - $valueMean;
 
 // Sum of multiplied differences
 $multipliedDiff: $multipliedDiff + ($resDiff * $valueDiff);
 
 // Sum of squared resolution differences
 $squaredDiff: $squaredDiff + ($resDiff * $resDiff);
  }

  // Calculate the Slope
  $m: $multipliedDiff / $squaredDiff;

  // Calculate the Y-Intercept
  $b: $valueMean - ($m * $resMean);

  // Return the CSS calc equation
  @return calc(#{$m*100}vw + #{$b}px);

}

Это действительно работает? Откройте этот CodePen и измените размер окна браузера. Оно работает! Размеры шрифта довольно близки к тому, что запрашивал дизайнер, и они плавно масштабируются с помощью макета.

Практический курс по верстке адаптивного лендинга с нуля!

Научитесь с нуля верстать адаптивные лендинги на HTML5 и CSS3 за ближайшие 6 дней

Узнать подробнее

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

Кроме того, чем более разнообразны размеры вашего текста, тем больше ошибок будет в трендлайне. Мы можем сделать ещё лучше?

Полиномиальные наименьшие квадраты

Чтобы получить более точный трендлайн, нужно просмотреть более сложные темы, например, трендлайн полиномиальной регрессии, который может выглядеть примерно так:

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

Теперь это больше похоже на правду! Гораздо точнее нашей прямой линии. Уравнение базовой полиномиальной регрессии выглядит следующим образом:

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

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

font-size: calc(3vw * 3vw); /* This doesn't work in CSS */

Поэтому, пока calc() поддерживает тип нелинейной математики, мы придерживаемся только линейных уравнений. Есть ли что-то еще, что помогло бы нам сделать всё лучше?

Брейкпоинты + множественные линейные уравнения

Что, если бы мы вычисляли только прямую линию между каждой парой брейкпоинтов? Что-то вроде этого:

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

В этом примере мы вычисляем прямую линию между 22px и, 24px, а затем между 24px и 34px. SASS будет выглядеть так:

// SCSS

h1 {
  @media (min-width:576px) {
 font-size: calc(???);
  }
  @media (min-width:768px) {
 font-size: calc(???);
  }
}

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

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

Поскольку сейчас мы говорим о двух пунктах, нахождение наклона (m) и y-перехвата (b) тривиально:

Использование calc(), vw, брейкпоинтов и  линейных уравнений в CSS Poly Fluid Sizing — калибруем размеры

Для этого есть функция SASS:

/// linear-interpolation
/// Calculate the definition of a line between two points
/// @param $map - A SASS map of viewport widths and size value pairs
/// @returns A linear equation as a calc() function
/// @example
/// font-size: linear-interpolation((320px: 18px, 768px: 26px));
/// @author Jake Wilson <jake.e.wilson@gmail.com>
@function linear-interpolation($map) {
  $keys: map-keys($map);
  @if (length($keys) != 2) {
 @error "linear-interpolation() $map must be exactly 2 values";
  }
  // The slope
  $m: (map-get($map, nth($keys, 2)) - map-get($map, nth($keys, 1)))/(nth($keys, 2) - nth($keys,1));
 
  // The y-intercept
  $b: map-get($map, nth($keys, 1)) - $m * nth($keys, 1);
 
  // Determine if the sign should be positive or negative
  $sign: "+";
  @if ($b < 0) {
 $sign: "-";
 $b: abs($b);
  }
 
  @return calc(#{$m*100}vw #{$sign} #{$b});
}

Теперь просто используйте функцию линейной интерполяции на нескольких контрольных точках в SASS. Кроме того, давайте выделим несколько минут и максимизируем font-sizes:

// SCSS

h1 {
  // Minimum font-size
  font-size: 22px;

  // Font-size between 576 - 768
  @media (min-width:576px) {
 $map: (576px: 22px, 768px: 24px);
 font-size: linear-interpolation($map);
  }

  // Font-size between 768 - 992
  @media (min-width:768px) {
 $map: (768px: 24px, 992px: 34px);
 font-size: linear-interpolation($map);
  }

  // Maximum font-size
  @media (min-width:992px) {
 font-size: 34px;
  }
}

Он генерирует этот CSS:

h1 {
  font-size: 22px;
}
@media (min-width: 576px) {
  h1 {
 font-size: calc(1.04166667vw + 16px);
  }
}
@media (min-width: 768px) {
  h1 {
 font-size: calc(4.46428571vw - 10.28571429px);
  }
}
@media (min-width: 992px) {
  h1 {
 font-size: 34px;
  }
}

Давайте завершим все это приятным SASS mixin (для ленивых и эффективных!). Я использую метод: Poly Fluid Sizing:

/// poly-fluid-sizing
/// Generate linear interpolated size values through multiple break points
/// @param $property - A string CSS property name
/// @param $map - A SASS map of viewport unit and size value pairs
/// @requires function linear-interpolation
/// @requires function map-sort
/// @example
/// @include poly-fluid-sizing('font-size', (576px: 22px, 768px: 24px, 992px: 34px));
/// @author Jake Wilson <jake.e.wilson@gmail.com>
@mixin poly-fluid-sizing($property, $map) {
  // Get the number of provided breakpoints
  $length: length(map-keys($map));
 
  // Error if the number of breakpoints is < 2
  @if ($length < 2) {
 @error "poly-fluid-sizing() $map requires at least values"
  }

  // Sort the map by viewport width (key)
  $map: map-sort($map);
  $keys: map-keys($map);

  // Minimum size
  #{$property}: map-get($map, nth($keys,1));
 
  // Interpolated size through breakpoints
  @for $i from 1 through ($length - 1) {
 @media (min-width:nth($keys,$i)) {
 $value1: map-get($map, nth($keys,$i));
 $value2: map-get($map, nth($keys,($i + 1)));
 // If values are not equal, perform linear interpolation
 @if ($value1 != $value2) {
 #{$property}: linear-interpolation((nth($keys,$i): $value1, nth($keys,($i+1)): $value2));
 } @else {
 #{$property}: $value1;
 }
 }
  }
 
  // Maxmimum size
  @media (min-width:nth($keys,$length)) {
 #{$property}: map-get($map, nth($keys,$length));
  }
}

SASS mixin требует следующих функций SASS:

Прежде всего, этот метод, очевидно, относится не только к font-size, но и к любой собственности единицы / длины ( margin, paddingи т.д.). Вы передаете искомое имя свойства в mixin как строку.

Затем вы передаете любое количество сопоставленных пар ширины вьюпорта + размер в любом порядке в poly-fluid-sizing()mixin. Он автоматически сортирует карту в соответствии с шириной вьюпорта от самого низкого до самого высокого . Таким образом, можно передать абсолютно безумную карту, подобную этой, и все прекрасно получится:

h1 {
  $map: (576px: 22px, 320px: 18px, 992px: 34px, 768px: 24px); 
  @include poly-fluid-sizing('font-size', $map);
}

Затем он выполнит линейную интерполяцию для каждой пары ширины вьюпорта, а затем сгенерирует CSS. Вы можете импортировать его в любой проект SASS и легко использовать. Вот окончательный CodePen этого метода:

Единственный недостаток на данный момент, который я вижу, заключается в том, что нельзя ввести смешанные единицы в mixin, например 3em @ 576px ширина. SASS просто не знает, что с этим делать. Я собираюсь продолжать искать способ справиться с тем, что в не входит базовое em значение.

Вывод

Это лучшее, что мы можем сделать? Является ли Poly Fluid калибровкой размеров Святого Грааля в CSS? Может быть. Если бы нелинейная, полиномиальная регрессия использовалась calc(), было бы гораздо лучше. Но, возможно… всё это не зависит от того, хотите вы линейного масштабирования или нет.

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

Я начал изучать эту идею в начале 2017 года и в итоге разработал вышеупомянутое решение. С тех пор я видел, как некоторые разработчики придумывали похожие идеи и разные части этой головоломки. Я подумал, что пришло время мне поделиться своим методом и тем, как я к нему пришёл.

Автор: Jake Wilson

Источник: https://medium.com/

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

Практический курс по верстке адаптивного лендинга с нуля!

Научитесь с нуля верстать адаптивные лендинги на HTML5 и CSS3 за ближайшие 6 дней

Узнать подробнее

Препроцессоры. Быстрый старт

Овладейте азами работы с препроцессорами с полного нуля

Получить

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree