Flexbox: насколько велика эта гибкая коробка?

https://www.smashingmagazine.com/2018/09/flexbox-sizing-flexible-box/
  • Перевод

Продолжаю публикацию переводов по особенностям CSS-технологии Flexbox.




Из цикла опубликованы следующие статьи:


  1. Что происходит при создании контейнера Flexbox.
  2. Все, что вам нужно знать о выравнивании во Flexbox.



Краткое резюме


В последних двух статьях мы рассмотрели, что происходит при создании flex-контейнера, а также рассмотрели выравнивание. На этот раз мы рассмотрим часто запутанную проблему размеров во Flexbox. Как Flexbox решает, насколько большие должны быть элементы?


Это третья часть моей серии о Flexbox. В последних двух статьях мы рассмотрели, что происходит при создании flex-контейнера, и исследовали выравнивание, как оно работает во Flexbox. На этот раз мы собираемся взглянуть на размеры. Как мы контролируем размер наших flex элементов, и какой выбор делает браузер, когда он регулирует размер?


Исходное отображение Flex элементов


Если у меня есть набор элементов, которые имеют переменную длину внутреннего содержимого, и установить их родителю display: flex, элементы будут отображаться в виде строки и выстраиваться с начала главной оси. В приведенном ниже примере мои три элемента имеют небольшой размер содержимого и могут отображать содержимое каждого элемента в виде непрерывной строки. В конце flex-контейнера есть пространство, в которое элементы не растут, потому что начальное значение свойства flex-grow каждого элемента равно 0, т.е. им запрещено увеличиваться.


Элементы flex имеют место для отображения в одной строке
Рис_1. Элементы flex имеют место для отображения в одной строке


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


Пространство распределяется, чтобы дать больше места для более длинного элемента
Рис_2. Пространство распределяется, чтобы дать больше места для более длинного элемента


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


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


CSS спецификация расчета внутренних и внешних размеров (CSS Intrinsic And Extrinsic Sizing)


Глядя на что—либо о расчете размеров в спецификации Flexbox, Вы довольно быстро обнаружите, что большая часть необходимой вам информации находится в другой спецификации — CSS Intrisnic and Extrinsic Sizing. Это потому, что концепция расчета размеров, которые мы используем, не уникальна для Flexbox, также как и свойства выравнивания не являются уникальными для Flexbox.


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


Предпочтительный размер (preferred size)


Предпочтительный размер элемента это размер, определяемый шириной или высотой, или логическими псевдонимами этих свойств среди inline-size и block-size. Воспользовавшись:


.box {
    width: 500px;
}

или логическим псевдонимом inline-size:


.box {
    inline-size: 500px;
}

вы заявляете, что хотите, чтобы ваш блок был шириной 500 пикселей или 500 пикселей в ряд.


Минимальный размер содержимого (MIN-CONTENT SIZE)


Минимальный размер содержимого-это наименьший размер содержимого, который может вместить элемент без переполнения. Если элемент содержит текст, то должны быть приняты все меры программного переноса.


Размер максимального содержимого (MAX-CONTENT SIZE)


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


Основной размер гибкого элемента (FLEX ITEM MAIN SIZE)


Основной размер гибкого элемента это его размер в главном направлении. Если вы работаете в строке на английском языке, то основным размером является ширина. В столбце на английском языке основным размером является высота.


Элементы также имеют свойства для обозначения минимума и максимума основного размера в главном направлении, определяемые как min-width или min-height.


Обработка размеров flex-элемента


Теперь, когда мы определили некоторые термины, мы можем посмотреть, как наши flex-элементы рассчитывают размер. Начальные значения flex-свойств выглядят следующим образом:


  • flex-grow: 0;
  • flex-shrink: 1;
  • flex-basis: auto.

flex-basis это база, от которой рассчитывается размер. Если мы установим flex-basis равным 0, а flex-grow равным 1, то все наши элементы не будут иметь начальной ширины, поэтому пространство во flex-контейнере будет распределено равномерно, назначив одинаковый размер пространства для каждого элемента.



Между тем, если flex-basis установить в auto а flex-grow: 1, то будет распределено только свободное пространство, т.е. пространство без контента.



В ситуации, когда нет свободного места, например, когда у нас контента больше, чем может поместиться в одной строке, тогда ничего не распределяется.



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


Определение auto (defining auto)


Когда auto назначается как значение чего-нибудь в CSS, оно будет иметь очень специфическое значение в этом контексте, которое стоит посмотреть. Рабочая группа CSS тратит много времени на выяснение того, что означает auto в любом контексте, как объясняет этот разговор с редактором спецификаций Fantasai.


В спецификации мы можем найти информацию о том, что означает auto при использовании в качестве flex-basis. Термины, определенные выше, должны помочь нам проанализировать это утверждение.


При указании для flex-элемента ключевого слова auto в качестве "flex-basis" возвращается значение свойства main size. Если это значение само по себе auto, то используется значение content.

Таким образом, если наш flex-basis установлен в auto, Flexbox просмотрит значение свойства main size. Мы должны иметь main size, если мы дали одному из наших flex-элементов ширину. В примере ниже, все элементы имеют ширину 110px, поэтому это значение используется в качестве main size т.к. начальное значение flex-basisauto.



Однако в нашем первоначальном примере есть элементы, у которых нет ширины, это означает, что их main sizeauto, и поэтому нам нужно перейти к рассмотрению следующего высказывания: “если это значение само по себе auto, то используется значение content.”


Теперь нам нужно посмотреть, что говорит спецификация о ключевом слове content. Это еще одно значение, которое вы можете использовать (при поддержке браузеров) для свойства flex-basis, например:


.item {
    flex: 1 1 content;
}

Спецификация определяет content следующим образом:


Указывает на автоматический размер, основываясь на содержимом flex-элемента. (Обычно он эквивалентен размеру max-content, но с настройками для обработки пропорций формата, внутренних ограничений размера и ортогональных потоков.

В нашем примере с flex-элементами, содержащими текст, мы можем игнорировать некоторые более сложные настройки и рассматривать content как размер max-content.


Это объясняет, почему, когда у нас есть небольшое количество текста в каждом элементе, текст не переносится. flex-элементы по умолчанию имеют auto размер, поэтому Flexbox берет их размер в свойстве max-content, элементы помещаются в контейнер такого размера, и работа выполнена!


На этом история не заканчивается, так как когда мы добавляем еще контент, элементы не остаются в размере max-content. Если бы они это делали, то вырвались бы из flex-контейнера и вызвали бы переполнение. Как только они заполнят контейнер, содержимое начинает переноситься, и элементы становятся разных размеров, основываясь на содержимом внутри них.


Разрешение гибких длин


Именно в этот момент спецификацию становится достаточно сложно просматривать, но нужно пройти шаги, которые заключаются в следующем:


Во-первых, сложите основной размер всех элементов и посмотрите, он больше или меньше свободного пространства контейнера.


Если размер контейнера больше суммы, то мы поинтересуемся параметром flex-grow, так как у нас есть место для роста элементов.


В первом случае наши элементы имеют доступное пространство для роста
Рис_3. В первом случае наши элементы имеют доступное пространство для роста.


Если размер контейнера меньше суммы, то мы поинтересуемся параметром flex-shrink, поскольку нам нужно сжимать элементы, чтобы вписаться в контейнер.


Во втором случае наши элементы слишком большие и требуют сжатия, чтобы вписаться в контейнер
Рис_4. Во втором случае наши элементы слишком большие и требуют сжатия, чтобы вписаться в контейнер.


Заморозьте все неизменяемые элементы, чьи размеры уже можно установить. Если мы используем flex-grow это будет включать в себя все элементы, которые имеют flex-grow: 0. Это сценарий, который мы имеем, когда у flex-элементов остается свободное пространство в контейнере. Начальное значение flex-grow равно 0, поэтому они становятся такими же большими, как их max-width, а затем они уже не растут от своей main size.


Если мы используем flex-shrink, то это будет включать в себя все элементы с flex-shrink: 0. Мы можем увидеть, что происходит на этом шаге, если мы дадим нашему набору flex элементов flex-shrink: 0. Элементы становятся замороженными в состоянии max-content и поэтому не сгибаются и не укладываются в контейнер.



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


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


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


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


Если вы росли из auto, то flex-basis будет либо обрабатываться как любая ширина или высота элемента или max-content размер. Затем пространство будет удалено в соответствии с размером flex-basis, умноженным на коэффициент flex-shrink, и, следовательно, удаляется пропорционально максимальному размеру элементов.


Контроль роста и сжимания


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


Установив свой собственный flex-basis или предоставив самому элементу размер, который затем используется в качестве flex-basis, вы возвращаете управление алгоритмом, сообщая Flexbox, что вы хотите расти или уменьшаться от этого конкретного размера. Можно полностью отключить рост или сжатие, установив для параметра flex-grow или flex-shrink значение 0. На этом этапе, однако, стоит временно использовать желание контролировать flex элементы, чтобы проверить, используете ли вы правильный метод компоновки. Если вы пытаетесь выровнять элементы flex в двух измерениях, вам лучше выбрать макет сетки.


Вопросы отладки связанных размеров


Если ваши flex элементы заканчиваются неожиданными размерами, то это обычно происходит потому, что ваш flex-basis имеет auto, и есть что-то, что дает этому элементу ширину, которая затем используется в качестве flex-basis. Проверка элемента в DevTools может помочь определить, откуда берется размер. Вы также можете попробовать установить flex-basis в 0, который заставит Flexbox обрабатывать элемент как имеющий нулевую ширину. Даже если это не тот результат, который вы хотите, это поможет определить значение flex-basis в использовании в качестве виновника ваших проблем с размерами.


Flex зазоры


Очень востребованная функция Flexbox-это возможность указать зазоры или желоба между элементами flex таким же образом, как мы можем указать зазоры в макете сетки и макете с несколькими столбцами. Эта функция определена для Flexbox как часть Box Alignment, и первая реализация браузера на подходе. Firefox ожидает поставлять свойства gap для Flexbox в Firefox 63. Следующий пример можно посмотреть в Firefox Nightly.



Как изображение видно в браузере Firefox 63
Рис_5. Как изображение видно в Firefox 63


Как и при компоновке сетки, длина зазора учитывается перед распределением пространства для flex элементов.


Завершая


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

Поделиться публикацией
Комментарии 7
    0
    А как можно бороться с Рис.4? На флексах у меня решить проблему выхода третьего блока за границы контейнера не удалось.
      0
      В селекторе .item разрешите элементу изменяться. Для этого замените строку `flex: 0 0 auto`, например, на `flex: auto`
        +2
        Очень плохо когда переводчик не понимает того материала который переводит. В результате рождаются вот такие статьи которые вредны, потому, что из-за кривого перевода множат неверное понимание материала.

        Как обычно бывает с флексами, все срезаются на понимании двух свойств flex-grow и flex-shrink

        Ниже пример flex-grow. По аналогии с ним работает и shrink.
        flex-grow это коэффициент, которые задает вес, сколько можно взять у существующего пула свободного пространства. Если этот пул есть.
        Понятнее всего будет на примере.
        Есть контейнер, ширина которого 500px
        Есть элемента А, который когда положили на основную ось занял размер 100px. У элемента flex-grow = 1
        Есть элемент B, который занял 200px. У элемента flex-grow = 2

        width A + width B = 100 + 200 = 300px
        свободный пул = 500 (размер контейнера) — 300 px (общий размер двух элементов) = 200px

        Размер свободного пула получился 200px и у нас есть два элемнта у которых указано свойство flex-grow. Расчет дополнительного места которое выделиться для элементов делается следующим образом
        flex-grow элемента А + flex-grow элемента B = 100% свободного пула
        то есть коэффициенты flex-grow показывают то в каком отношении разделиться пул свободного пространства.
        100% / 3 (3 — это сумма значений flex-grow) = 33% (что равно 66.6666px от 200px свободного пространства)
        Элемент А, имеющий flex-grow 1 получит дополнитльно 33.333% от пула свободного пространства. Итого элемент А станет: 100px (его базовая ширина) + 66.66666px (согласно flex-grow 1) = 166.6666px

        Элемент Б, имеющий flex-grow 2 получит дополнительно 66.666666% от пула свободного пространства. Итого элемент Б станет: 200px (его базовая ширина) + 133.33333px (согласно flex-grow 2) = 333.33333px

        Совокупный размер = 166.6666 + 333.33333 = 500px

        Аналогичным образом работает и fkex-shrink с той лишь разницей что заранее на основе базовых размеров вычисляет, сколько нужно дополнительного простраства чтобы все элементы согласно их базовым правилам, посетитель в контейнер. И этот пул распределяет согласно коэффициенту из flex-shrink



          0
          Ну и базис тоже.
          Я как-то на заре знакомства с флексами безуспешно пытался сделать равноширинные пункты меню через flex: 1 1 auto, тогда как правильно будет flex: 1 0 0. Меня поначалу даже пугал этот нулевой базис — ну как же может быть ноль, ведь внутри блока что-то есть, оно же не влезет и что-нибудь разопрёт!
          Система несложная, когда уже разобрался, но поначалу поведение может показаться неочевидным.
            0
            И правильно делал, что пугал. При нулевом базисе вырубится перенос. Поэтому в качестве базиса лучше задавать минимальную разумную ширину до которой айтемы будут ужиматься, до переноса.
          0

          Как-то так, для наглядности. Если заменить у итемов 0 на 1, то изменения сразу будут очевидны

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое