Узнаем как управлять размерами и отступами флекс-элементов и как работают коэффициенты растяжения и сжатия, а также потренируемся создавать «гибкие» раскладки и элементы интерфейса.
В предыдущем курсе про флексбокс мы знакомились с базовыми понятиями этого механизма раскладки и подробно изучали, как работает выравнивание флекс-элементов. Ну а этот курс будет посвящён управлению размерами флекс-элементов.
Начнём с простого вопроса. Как работает привычная нам блочная модель внутри флекс-контейнера? Есть ли какие-нибудь отличия в поведении привычных свойств?
Ширина, высота, внутренние отступы и рамки для флекс-контейнеров и флекс-элементов работают как обычно: общий размер элементов складывается из этих компонентов. Это поведение так же можно менять с помощью свойства box-sizing
.
Есть и несколько важных отличий:
float
.Теперь поработаем с уже знакомыми свойствами и убедимся, что они работают привычным нам образом.
.skate
, на которых сидят еноты, ширину 75px
и высоту 300px
(минимальные размеры изменять не нужно),10px
.box-sizing: border-box
.В предыдущем задании мы не упомянули про свойство margin
, ведь оно таит много сюрпризов:
внешние отступы не схлопываются, ни по горизонтали, ни по вертикали;
внешние отступы не выпадают, ни из флекс-контейнера, ни из флекс-элементов;
значение auto
получило премию журнала Форбс в номинации «Самое влиятельное значение CSS-свойства внутри флекс-контейнера».
Всё дело в механизме распределения свободного места. Если внутри флекс-контейнера есть свободное место, то оно перераспределяется так:
находятся элементы, у которых есть внешние отступы со значением auto
;
всё свободное место в соответствующих направлениях отдаётся таким отступам (то есть задаётся определённый размер отступа в пикселях);
если элементов с автоматическими отступами на одном направлении несколько, то место между ними перераспределяется поровну;
только после этого запускаются механизмы выравнивания.
Поэтому margin: auto;
влияет на положение флекс-элементов на обеих осях, а также «ломает» механизмы выравнивания, ведь выравнивание происходит, когда есть свободное место. Но если всё свободное место «перетекло» в автоматический отступ, то и выравнивать нечего.
Эти особенности можно использовать во благо. Например, с помощью автоматических отступов вы можете управлять расположением элементов вдоль главной оси. Давайте поэкспериментируем.
.skate
внешние отступы со всех сторон 10px
.margin-left: auto;
второму скейту,margin-right: auto;
четвёртому скейту..spot
распределение по главной оси на flex-end
и убедитесь, что ничего не изменилось
.В прошлом задании мы проверили, как автоматический внешний отступ влияет на положение флекс-элементов на главной оси. Кроме того, мы убедились, что такие отступы «ломают» свойство justify-content
.
Автоматический margin
влияет и на выравнивание флекс-элементов вдоль поперечной оси.
Если у флекс-элемента отступ сверху или снизу автоматический, то на него не влияют, ни align-items
, ни align-self
. Такой элемент прижмётся либо к верху контейнера, либо к низу.
А если задать автоматические отступы с противоположных сторон, то элемент разместится по центру флекс-контейнера, так как свободное место «впитается» отступами поровну.
.spot
выравнивание флекс-элементов вдоль поперечной оси на stretch
.margin-top: auto;
второму скейту.margin-bottom: auto;
.Будет ли результат таким, как на картинке снизу, если повернуть главную ось в предыдущем задании?
Нет, не будет!
Дело в том, что «старые нефлексовые» свойства, такие как отступы или размеры, ничего не знают про направление осей. Они «мыслят по-старому», понятиями «верх» и «низ», «право» и «лево».
Поэтому когда главная ось направлена слева направо, горизонтальные отступы перемещают флекс-элементы вдоль главной оси. Но если направить главную ось сверху вниз, то те же отступы начнут работать вдоль поперечной оси.
То же относится и к вертикальным отступам.
Давайте повернём ось, а затем внесём правки и получим результат, как на картинке выше.
На момент написания курса в Safari и других WebKit-браузерах есть баг, из-за которого блоки могут позиционироваться некорректно.
.spot
направление главной оси на column
.margin-top
и добавьте свойство margin-left: auto;
.margin-bottom
и добавьте свойство margin-right: auto;
.На примере отступов видно, что «старые» свойства внутри флекс-контейнера ведут себя достаточно глупо. Ширина и высота тоже не умеют реагировать на поворот главной оси. Поэтому ввели понятия главный размер или main size и поперечный размер или cross size.
Если главная ось направлена горизонтально, то главный размер — это ширина, свойство width
, а поперечный размер — это высота, свойство height
. Если главная ось направлена вертикально, то всё наоборот.
А хотелось бы иметь «умное» свойство для задания размера флекс-элементов, которое знает про главную ось и «поворачивается» вместе с ней.
И такое свойство есть — это flex-basis
. Оно задаёт базовый размер флекс-элемента или размер вдоль главной оси.
Если flex-basis
не задан или его значение равно auto
, то базовый размер берётся из width
или height
.
Свойство flex-basis
принимает те же значения, что и width
/height
:
flex-basis: 100px; /* базовый размер 100 пикселей */ flex-basis: 50%; /* базовый размер 50% контейнера */
Свойство flex-basis
«сильнее» свойств width
и height
, и если у флекс-элемента заданы все три свойства, то flex-basis
переопределит либо ширину, либо высоту в зависимости от направления главной оси.
.racoon-orange
задайте ширину 300px
и высоту 100px
.100%
..spot
на column
. Смотрите, как ширина и высота меняются при повороте оси из-за заданного базового размера.
Итак, пришла пора проверить знания.
В этом испытании вам предстоит расположить блоки на палитре в нужном порядке. Задача — подобрать размеры элементов и их внешние отступы.
Обратите внимание, что HTML-код изменять нельзя, и в этом коде заблокированы свойства для выравнивания флекс-элементов. Поэтому выравнивать придётся по-другому.
Подсказка: размеры и неавтоматические отступы кратны 5
.
В предыдущем задании мы кое о чём умолчали. На самом деле, базовый размер — это не просто размер элемента вдоль главной оси, это начальный или исходный размер вдоль главной оси.
Почему так важны эти начальный или исходный?
И снова всё дело в механизме перераспределения свободного места во флексбоксе.
Если внутри флекс-контейнера по главной оси остаётся свободное место, то мы можем попросить флекс-элемент, чтобы он увеличился и занял это место. Это делается с помощью свойства flex-grow
, которое можно назвать «коэффициентом флекс-жадности» флекс-элемента.
Свойство flex-grow
принимает неотрицательные числовые значения, его значение по умолчанию — 0
.
Если значение flex-grow
равно нулю, то флекс-элемент «не претендует» на оставшееся свободное место во флекс-контейнере и не будет увеличиваться, чтобы занять это место.
Если значение flex-grow
больше нуля, то флекс-элемент будет увеличиваться, «захватывая» оставшееся свободное место.
Получается, что базовый размер — это исходный размер флекс-элементов до применения flex-grow
.
Задайте коэффициент растягивания flex-grow
равный 1
:
.racoon-green
.racoon-orange
.Если сразу у нескольких флекс-элементов значение flex-grow
больше нуля, то они будут делить свободное место между собой.
Свободное место будет добавляться флекс-элементам пропорционально значениям их «коэффициента жадности». Например, если во флекс-контейнере есть два элемента:
.element-1 { flex-grow: 1; } .element-2 { flex-grow: 2; }
То второму элементу достанется в два раза больше свободного места, чем первому. Если изменить значения коэффициентов у этих элементов на такие:
.element-1 { flex-grow: 50; } .element-2 { flex-grow: 100; }
То ничего не изменится, так как отношение коэффициентов не изменилось: 100 в два раза больше 50. То есть важно не само значение коэффициента, а его соотношение с коэффициентами остальных элементов.
Это задание-загадка на подбор пропорций. Вам нужно подобрать такие значения flex-grow
, чтобы итоговый размер элементов оказался таким же, как и у «линеек» снизу. Базовые размеры элементов и размеры флекс-контейнера вы узнаете из кода.
Сейчас поэкспериментируйте самостоятельно, а в следующем задании мы детально разберём алгоритм расчёта итогового размера флекс-элементов с ненулевыми flex-grow
.
Подсказка: в простейшем решении коэффициенты будут целыми числами не больше 5.
.spot-1
значения flex-grow
так, чтобы их ширина стала равной ширине «линеек»..spot-2
.В этом задании можно менять только значения flex-grow
.
1 шаг. Рассчитываем свободное место во флекс-контейнере:
Свободное место = Ширина контейнера - Сумма базовых размеров элементов
2 шаг. Считаем размер минимальной доли свободного места:
Доля свободного места = Свободное место / Сумма flex-grow всех элементов
3 шаг. Базовый размер каждого флекс-элемента увеличиваем на размер минимальной доли свободного места умноженной на значение flex-grow
этого элемента:
Итоговый размер = Базовый размер + (Доля свободного места * flex-grow)
Для верхнего блока с енотами хочется задать коэффициенты 1
и 2
. Но нужные размеры блоков получаются с коэффициентами 1
и 3
. Давайте посчитаем:
Свободное место = 300px - (50px * 2) = 200px Доля свободного места = 200px / (1 + 3) = 50px Итоговый размер зелёного енота = 50px + (50px * 1) = 100px Итоговый размер коричневого енота = 50px + (50px * 3) = 200px
Но если задать флекс-элементам нулевой базовый размер, свободное место будет занимать всю ширину флекс-контейнера, и коэффициенты жадности будут другими.
flex-basis: 0
и flex-grow
для точного управления относительными размерами не стоит. Лучше использовать базовый размер в процентах.Тонкость. На размер оставшегося свободного места влияет не только flex-basis
, но и рамки, и отступы. Если flex-basis
явно задано нулевое значение, то min-width
на размер свободного места влиять не будет, так как ограничения размеров к флекс-элементам применяются уже после перераспределения свободного места.
.spot-1
значения flex-grow
так, чтобы их ширина стала равной ширине «линеек»..spot-2
.В этом задании можно менять только значения flex-grow
.
Если сумма базовых размеров флекс-элементов больше, чем размер флекс-контейнера, то возникает отрицательное пространство.
Механизм перераспределения работает не только для свободного места, но и для отрицательного пространства. Флекс-элементы умеют распределять отрицательное пространство между собой и сжиматься.
За уменьшение флекс-элементов отвечает свойство flex-shrink
, которое можно назвать «коффициентом сжатия».
Свойство flex-shrink
принимает неотрицательные числовые значения, его значение по умолчанию — 1
.
Если значение flex-shrink
больше нуля, то флекс-элемент будет уменьшаться, «впитывая» часть отрицательного пространства, если оно существует.
Если значение flex-shrink
равно нулю, то флекс-элемент уменьшаться не будет.
Флекс-элементы стараются быть максимально «гибкими» и не выпадать из своего контейнера, поэтому у flex-shrink
значение по умолчанию равно 1
. Но если задавать нулевые значения для коэффициента сжатия, то выпадения элементов добиться можно.
.spot-1 .skate
задайте базовый размер флекс-элементов 300px
..racoon-brown
Свойство flex-shrink
очень похоже на свойство flex-grow
. Оно задаёт пропорции, в которых флекс-элементы «впитывают» отрицательное пространство.
Чем больше значение коэффициента сжатия у элемента, тем больше отрицательного пространства он «впитает» и тем сильнее сожмётся.
Когда базовые размеры флекс-элементов одинаковы, пропорции сжатия элементов считаются так же, как пропорции увеличения. Если базовые размеры флекс-элементов отличаются, то механизм усложняется. Подробно мы разберём его в следующем задании.
А пока снова поэкспериментируйте с коэффицентами в задании-загадке.
Вам нужно подобрать такие значения flex-shrink
, чтобы итоговый размер элементов оказался таким же, как у коричневой и зелёной «линеек», и чтобы над красной «линейкой» ничего не было.
На красной линейке показан размер отрицательного пространства, которое распределяется при сжатии флекс-элементов.
Базовые размеры элементов и размеры флекс-контейнера вы узнаете из кода.
Подсказка: в простейшем решении коэффициенты будут целыми числами не больше 5.
flex-shrink
так, чтобы их ширина стала равной ширине «линеек».В этом задании можно менять только значения flex-shrink
.
Ниже описан механизм расчёта размеров элементов, когда места в контейнере не хватает:
1 шаг. Рассчитываем отрицательное пространство (ОП) во флекс-контейнере:
ОП = Ширина контейнера - Сумма базовых размеров элементов
2 шаг. Находим сумму произведений базовых размеров (СПБР) элементов на их коэффициенты сжатия:
СПБР = (Базовый размер1 * flex-shrink1) + (Базовый размер2 * flex-shrink2) + … + (Базовый размерn * flex-shrinkn)
3 шаг. Для каждого элемента считаем «нормированный коэффициент сжатия» (НКС), для чего произведение базового размера элемента на его коэффициент сжатия делим на СПБР:
НКС = (Базовый размер * flex-shrink) / СПБР
4 шаг. Базовый размер элемента уменьшаем на часть ОП пропорциональную НКС элемента:
Итоговый размер = Базовый размер - (НКС * ОП)
Получается, что доля отрицательного пространства, которую «впитает» элемент, зависит от двух факторов:
Именно поэтому в формулах присутствуют нормировки. А теперь снова попробуйте подобрать коэффициенты сжатия.
flex-shrink
так, чтобы их ширина стала равной ширине «линеек».В этом задании можно менять только значения flex-shrink
.
Решение предыдущего задания: коэффициенты должны быть 1
и 1
Давайте рассчитаем размеры элементов из предыдущего задания и убедимся в правильности описанного алгоритма.
Отрицательное пространство = 200px - 100px - 300px = -200px Сумма произведений размеров на коэффициенты = (1 * 100px) + (1 * 300px) = 400px Нормированный коэффициент 1 элемента = (1 * 100px) / 400px = 0.25 Нормированный коэффициент 2 элемента = (1 * 300px) / 400px = 0.75 Итоговый размер 1 элемента = 100px - (200px * 0.25) = 50px Итоговый размер 2 элемента = 300px - (200px * 0.75) = 150px
Есть несколько тонкостей, касающихся сжатия флекс-элементов:
min-width
, применяются к элементам после этапа перераспределения свободного места или отрицательного пространства.И эти тонкости могут приводить к неожиданным эффектам, когда элементы выпадают из флекс-контейнера. Давайте поэкспериментируем.
.racoon-brown
задайте нулевой коэффициент сжатия..racoon-green
задайте минимальную ширину 50px
25px
.По многочисленным просьбам фанатов заданий на подбор коэффициентов мы создали это испытание, в котором вы снова сможете насладиться математикой и подсчётом дробей.
В заблокированном HTML-коде заданы базовые размеры блоков, которые вам изменять нельзя. Но отталкиваясь от этих размеров, вы можете понять, в каком блоке использовать коэффициенты растяжения, а в каком — коэффициенты сжатия. И, конечно, подобрать значения коэффициентов.
Подсказка: итоговые размеры блоков кратны 50
, а коэффициенты — небольшие целые числа.
С помощью сокращённого свойства flex
можно одновременно задать коэффициенты растягивания, сжатия и базовый размер флекс-элемента.
Свойство flex
состоит из трёх компонентов, которые пишутся через пробел в следующем порядке: flex-grow
, flex-shrink
и flex-basis
. В примере ниже два правила аналогичны:
.flex-item { flex: 1 2 300px; } .flex-item { flex-grow: 1; flex-shrink: 2; flex-basis: 300px; }
Ещё у свойства flex
есть особые значения: initial
, auto
, none
. Также второй и третий компоненты необязательны. Ниже показаны различные значения свойства и их расшифровки.
flex: initial; -> flex: 0 1 auto; flex: auto; -> flex: 1 1 auto; flex: none; -> flex: 0 0 auto; flex: 1 0; -> flex: 1 0 0%; flex: 1; -> flex: 1 1 0%;
flex
интерпретируются с ошибками. Поэтому лучше задавать все три компоненты в значении этого свойства.Попробуем это свойство на практике.
flex
задайте всем скейтбордам .skate
коэффициент растягивания 0
, коэффициент сжатия 1
, базовый размер 200px
.1
, коэффициент сжатия 0
, базовый размер 200px
,2
.Во всех примерах, рассмотренных раньше, флекс-контейнер был однострочным, ведь перенос флекс-элементов на новую строку по умолчанию запрещён — работает flex-wrap: nowrap;
.
А как будут растягиваться и сжиматься элементы в многострочном контейнере, с flex-wrap: wrap;
?
В таком контейнере свойство flex-shrink
будет работать как обычно, но необходимость в нём будет возникать намного реже. Ведь при нехватке места в строке флекс-элементы будут переноситься на новую строку.
Но если появятся флекс-элементы, базовый размер которых больше размера флекс-контейнера, то такие элементы будут сжиматься и занимать целую строку. Наверное, это единственный случай, когда flex-shrink
делает что-то полезное в многострочном контейнере.
500px
..spot
.В отличие от flex-shrink
, свойство flex-grow
в многострочном флекс-контейнере срабатывает намного чаще и пользы приносит намного больше.
В каждой строке такого контейнера может быть свободное место и механизм перераспределения этого места работает построчно.
Поэтому возможность «растянуть» флекс-элементы, чтобы строки заполнялись по ширине полностью, будет возникать достаточно часто.
.spot
последним скейт с Зелёным Енотом — див с классами skate
и racoon-green
..spot
задайте перенос флекс-элементов на новую строку,1
.skate
и racoon-brown
.Познакомимся с интересным эффектом, который возникает при использовании базовых размеров в процентах.
Если задать базовый размер флекс-элемента 100%
и при этом включить перенос элементов на новую строку, то элементы расположатся столбцом, хотя главная ось контейнера будет по-прежнему направлена слева направо.
Поэкспериментируем.
100%
,.spot
задайте перенос флекс-элементов на новую строку.Мы рассмотрели теоретическую часть и приступаем к практической части этого курса. Давайте разберём на реальных элементах интерфейсов, в каких случаях бывает уместно использовать флексбокс.
Довольно распространённый ход в интерфейсе — блок с заголовком и небольшим уточняющим подзаголовком на одной строке. Заголовок находится в начале строки, а подзаголовок прижат к правому краю.
Если текст заголовка или подзаголовка сильно увеличится, то вёрстка не должна ломаться — тексты просто должны расположиться друг под другом.
Сверстать элемент с таким гибким поведением с помощью float
или display: table
не получится. Ведь нужно, чтоб блоки подписей одновременно и занимали свободное пространство, и чтобы их размеры зависели от текстового содержания, и чтобы в случае переполнения сетка перестраивалась.
Здесь нам поможет только флексбокс с flex-grow
.
Для наглядности зададим текстовым блокам фоновый цвет и рамку, а в следующем задании уберём их.
.card-title
задайте фоновый цвет #c8dcff
и сплошную рамку толщиной 1px
и цветом #999999
..card-title
задайте раскладку флексбокса.card-title-main
задайте коэффициент растягивания 1
.А теперь давайте проверим, как сработает наша раскладка, если текста станет немного больше.
На случай «переполнения» зададим контейнеру перенос флекс-элементов на новую строку.
Кекс любит флекс
на Инструктор Кекс и удивительный флекс
,.card-title
задайте перенос флекс-элементов на новую строку..card-title
рамку и фоновый цвет.А теперь давайте создадим ещё одно «гибкое» меню, похожее на то, что мы делали в задании прошлого курса.
Напомним, что в прошлом варианте пункты равномерно распределялись внутри контейнера меню с помощью justify-content: space-around
.
Новый вариант меню будет более адаптивным: пункты меню будут переноситься на следующую строку, если места будет не хватать. А ещё для достижении красивой симметрии пункты будут растягиваться, чтобы занимать весь контейнер меню. И снова нам поможет flex-grow
.
Создадим базовую раскладку и зададим перенос строк.
.menu
задайте раскладку флексбокса,Акула
.Теперь остаётся задать элементам меню коэффициент растяжения и проверить, как будет вести себя меню, если мы решим изменить его ширину, либо добавить пункты.
Получившееся решение удобно применять для адаптации к мобильным интерфейсам. При этом даже не потребуется добавлять дополнительные стили для мобильных вьюпортов.
.menu
задайте коэффициент растягивания 1
,Сом
..menu
ширину 300px
,200px
.Ещё один случай, когда может пригодиться флексбокс — поля ввода с динамической шириной. Требования к ним такие:
Решить эту задачу можно только при помощи флексбокса. Превратим контейнер поля ввода во флекс-контейнер, все элементы внутри него превратятся во флекс-элементы, базовый размер которых будет зависеть от их содержания — flex-basis: auto;
. И останется только задать ненулевой коэффициент растягивания полям ввода.
В широком контейнере всё будет работать отлично. Проблемы могут появиться в слишком узких контейнерах: по умолчанию поля ввода не будут сжиматься после определённой ширины, что приведёт к выпаданию текста из остальных элементов.
Чтобы справиться с этими проблемами, надо задать всем элементам кроме полей ввода нулевой коэффициент сжатия, а самим полям ввода явно прописать минимальную ширину.
В этом задании в селекторах используется псевдокласс :not
, который разбирается в курсе «Селекторы. Часть 2».
На момент написания курса в Safari и других WebKit-браузерах есть баг, из-за которого блоки могут позиционироваться некорректно.
.input-container
задайте раскладку флексбокса,.input
задайте коэффициент растягивания 1
..input-container
до 200px
,.add-on
задайте нулевой коэффициент сжатия,.input
задайте минимальную ширину 50px
.Теперь давайте соберём на флексбоксе интерфейс с карточками курсов.
Флексбокс нужен, чтобы сделать этот блок адаптивным. Вы увидите, как гибко он адаптируется в следующих заданиях.
Начнём работу с отдельной карточки. Сделаем карточку флекс-контейнером и направим её главную ось сверху вниз. Это необходимо, чтобы в дальшнейшем гибко управлять внутренними блоками карточки.
Напомним, что флекс-элементы могут одновременно быть и флекс-контейнерами. Мы превратим блок с мета-информацией в флекс-контейнер, чтобы расположить блоки с тегами и временем чтения.
.card
задайте раскладку флексбокса,.card-meta
,.card-category
задайте коэффициент растягивания 1
.Пришло время добавить вторую карточку. Её код уже есть в разметке.
Для управления раскладкой карточек превратим их родительский блок в флекс-контейнер.
И теперь уже карточки стали одновременно и флекс-элементами, и флекс-контейнерами.
.cards
задайте раскладку флексбокса,.card
задайте внешний отступ 10px
.Карточки выстроились в один ряд и теперь их высота одинакова. Это произошло потому, что их родительский элемент — флекс-контейнер с поперечным выравниванием stretch
.
Сейчас мы сделаем так, чтобы кнопки «Пройти курс» выравнивались по нижнему краю карточки. Для этого:
.card-content
положительный коэффициент растяжения, чтобы он занял всё свободное место в карточке..card-content
в флекс-контейнер с главной осью, направленной сверху вниз..card-content
.Вспомните, как на первом шаге мы сделали карточки флекс-контейнерами. Без этого нельзя было бы растянуть вложенные блоки «в высоту» с помощью flex-grow
.
.card-content
задайте коэффициент растягивания 1
,.card-button
задайте внешний отступ сверху auto
.На последнем шаге мы поработаем с раскладкой карточек.
Сделаем так, чтобы карточки переносились на новую строку, если им не хватает места в контейнере. А если свободное место на строке осталось, то карточки будут его занимать.
А чтобы было видно, как будут вести себя несколько карточек, мы добавили специальную обёртку с зумом.
.cards
..card
удалите максимальную ширину, задайте ей базовый размер 300px
1
..cards
до 700px
. Обратите внимание, как перестроятся карточки.Теперь вы знаете достаточно, чтобы пройти финальное испытание.
В нём снова нужно создать гибкую раскладку, но на этот раз задача усложнилась. Теперь блоков стало больше, они расположены не в один ряд и у них разные размеры.
Вам понадобится подобрать базовые размеры, отступы, некоторые коэффициенты и пару других флексовых свойств.
Подсказка: как всегда отступы и базовые размеры (до перераспределения свободного места) кратны 5
.
До встречи на следующих курсах. Продолжение следует!