Резиновый десктопный адаптив: как сделать большие экраны одинаковыми

dbf5979b4fa16bb2bb38196373bce8b2.jpg

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

Проблема

Давайте сделаем совсем примерную верстку онлайн-кинотеатра Кекфликс. Не буду вдаваться в подробности, как именно это сверстано, тут нет особых сложностей, я детально буду рассматривать лишь стили. Сначала необходимо договориться о некоторых нерушимых постулатах:

  • Базовый размер шрифта равен 16px.

  • Эталонный размер десктопного экрана 1600х900. Это означает, что мы будем стараться делать другие десктопные экраны максимально пропорционально похожими на экран 1600х900.

Вот что у нас получилось:

Базовая верстка. Экран 1600х900Базовая верстка. Экран 1600×900

Полный скриншот страницы выглядит так:

Полный скриншот базовой верстки. Экран 1600х900Полный скриншот базовой верстки. Экран 1600×900

В данной верстке у нас все задано в пикселях: размер шрифта, размер скруглений, ширины, высоты и другое. Именно поэтому данный интерфейс будет выглядеть следующим образом на разрешении iMac 2560х1664:

Базовая верстка. Экран 2560x1664Базовая верстка. Экран 2560×1664

Интерфейс никак не заскейлился: например, как был размер шрифта у названия фильма 16px на экране 1600х900, так и остался. Стало вмещаться больше контента, вследствие чего глазу очень тяжело остановиться на чем-то одном, а мышкой тяжело дотянуться до маленьких элементов. Как выглядит данный интерфейс на 4K разрешении можете предугадать: все заметно уменьшилось.

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

Базовая верстка с боковыми распорками. Экран 2560х1660Базовая верстка с боковыми распорками. Экран 2560×1660

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

Наивное решение

Самый простой способ заскейлить интерфейс — это прописать размеры относительно базовой величины 1rem, которую впоследствии заадаптивить. Можно также использовать величину em, но будьте аккуратны, ведь это значение высчитывается от размера шрифта родителя, поэтому могут быть проблемы при наследовании стилей у вложенных элементов. Величина rem фиксирована размером шрифта, указанном на теге html. По умолчанию во всех современных браузерах 1rem=16px, что нам подходит согласно нашим постулатам.

html
	// ... some styles ...
	font-size: 16px
	// ... some styles ...

Чтобы нам проще было переписать стили относительно базовой величины, создадим вспомогательную SASS функцию, которая будет переводить количество пикселей в rem-ы:

@use 'sass:math'
@function rem(value, $delimeter) * 1rem

Таким образом, например, стили красного квадрата размером 200×200 (width: 200px; height: 200px) перепишутся следующим образом:

.box
	width: rem(200)
	height: rem(200)
	background-color: red

Что после компиляции примет вид:

.box {
	width: 12.5rem;
	height: 12.5rem;
	background-color: red;
}

После того как мы перевели все пиксели на rem, необходимо приступать к адаптиву этой величины. Мы бы могли наивно написать следующее медиа-выражение, которое пропорционально экрану 1600x900 увеличит базовую величину 1rem=16px до значения 1rem=19.2px для разрешения 1920х1080.

@media (min-width: 1920px)
	html
		font-size: 19.2px

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

Базовая верстка. Ресайз окна с 1600 до 1920 по ширинеБазовая верстка. Ресайз окна с 1600 до 1920 по ширине

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

Миксин адаптивности через CSS шлюзы

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

Давайте попробуем создать СSS шлюз для изменения базовой величины rem. То есть создадим шлюз для управления размером шрифта для тега html. Согласно нашим постулатам мы договорились, что у нас базовый шрифт равен 16px, а базовый экран имеет разрешение 1600х900. Стоит отметить, что мы будем добавлять резиновости нашему интерфейсу лишь по ширине экрана, но при необходимости такие же манипуляции можно сделать и с высотой.

Если для ширины 1600 мы имеем размер шрифта 16px, то прибегнув к несложным математическим операциям можно выяснить, что для ширины экрана 1920 размер шрифта должен составлять 19.2px. Это можно увидеть на графике для прямой y=0.01x, где по оси X у нас находятся ширины экранов, а по оси Y размер шрифта:

График функци y=0.01xГрафик функци y=0.01x

Давайте абстрагируемся от конкретных значений и построим общую прямую по двум точкам (x1, y1) и (x2, y2) согласно линейной функции y=kx+b, где k — это наклон функции, а b — смещение по оси ординат. Коэффициент k вычисляется как первая производная функции (или как тангенс угла наклона, если так удобнее):

k = \frac{(y_2 - y_1)}{(x_2-x_1)}.

Коэффициент b можно вычислить, если подставить любую точку, например, (x1, y1) и найденный коэффициент k. Не буду тратить ваше время на математические манипуляции и покажу сразу исходный вид нашей функции, построенной для любых двух точек:

y = \frac{y_2 - y_1}{x_2 - x_1}(x - x_1) + y_1.

Давайте преобразуем это в SASS функцию: y — это то, что мы ищем, поэтому воспользуемся функцией calc(), а x заменим на ширину всего вьюпорта 100vw:

@function calc-between-width($width-start, $value-start, $width-end, $value-end)
  @return calc(#{$value-start} * 1px + (#{$value-end} - #{$value-start}) * (100vw - #{$width-start} * 1px) / (#{$width-end} - #{$width-start}))

Получили SASS функцию calc-between-width, которая выставит пропорциональное значение в пикселях любому CSS свойству в зависимости от переданных величин в промежутке указанных ширин.

Время магии

Необходимо применить SASS функцию на нашей базовой верстке, чтобы заадаптивить базовую величину rem. Для этого добавим следующий стиль

html 
	font-size: calc-between-width(1600, 16, 1920, 19.2)

Тогда получим желанный результат: все десктопные экраны стали выглядеть одинаково согласно нашему базовому разрешению 1600х900:

Верстка через CSS шлюзы. Экран 1920х1080Верстка через CSS шлюзы. Экран 1920×1080Верстка через CSS шлюзы. Экран 2560х1664Верстка через CSS шлюзы. Экран 2560×1664

А вот как это выглядит в динамике:

Базовая верстка через CSS шлюзы. Ресайз окна с 1600 до 1920 по ширинеБазовая верстка через CSS шлюзы. Ресайз окна с 1600 до 1920 по ширине

Заключение

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

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

В данной статье мы адаптивили CSS свойство font-size, что позволило добавить нам резиновость для всех разрешений. Но мы сделали это самым простым способом через линейную функцию. Однако нам никто не запрещает использовать другие виды функций. Например, при необходимости можно построить кусочно-линейную функцию, которая будет увеличивать размер шрифта от ширины экрана 1600 до 1800, далее уменьшать размер шрифта до ширины 2000 и после снова увеличивать. Или можно написать новую SASS функцию, которая будет вычислять значение согласно квадратичной функции. К тому же, функция calc-between-width применятся к любому CSS свойству, которое может содержать пиксели. Поэтому вы можете, таким же образом через CSS шлюзы добавлять резиновости любым таким свойствам. К примеру, вы можете резиново манипулировать скруглениями через border-radius. Вы ограничены лишь своей фантазией и возможностями браузера!

Ссылка на github: https://github.com/progikusok/desktop-adaptive-example

© Habrahabr.ru