Как фактор flex-shrink при заполнении и приграничном поле?

Скажем, у нас есть гибкий контейнер шириной 400 пикселей.

Внутри этого контейнера находятся три элемента гибкости:

:nth-child(1) { flex: 0 2 300px; }   /* flex-grow, flex-shrink, flex-basis */
:nth-child(2) { flex: 0 1 200px; }
:nth-child(3) { flex: 0 2 100px; }

Таким образом, общая ширина элементов flex составляет 600 пикселей, а свободное пространство в контейнере равно -200px.

С помощью этого вопроса, в этой статье, а flexbox spec, я узнал основную формулу для распределения отрицательного свободного пространства между элементами flex:

Шаг 1

Умножьте каждое значение усадки на его базу и добавьте их все вместе:

:nth-child(1)  2 * 300 = 600
:nth-child(2)  1 * 200 = 200
:nth-child(3)  2 * 100 = 200

TOTAL = 1000

Шаг 2

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

:nth-child(1)  600 / 1000 = .6
:nth-child(2)  200 / 1000 = .2
:nth-child(3)  200 / 1000 = .2

Шаг 3

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

:nth-child(1)  .6 * -200px = -120px
:nth-child(2)  .2 * -200px =  -40px
:nth-child(3)  .2 * -200px =  -40px

Итак, вычисленный размер каждого элемента flex должен быть:

:nth-child(1)  300px - 120px = 180px
:nth-child(2)  200px -  40px = 160px
:nth-child(3)  100px -  40px =  60px

Протестировано в Chrome, Firefox и IE11, номера проверяются. Расчет отлично работает.

.flex {
  display: flex;
  width: 400px;
  height: 50px;
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
}

.flex li:nth-child(1) {
  flex: 0 2 300px;
  background: orange;
}

.flex li:nth-child(2) {
  flex: 0 1 200px;
  background: chartreuse;
}

.flex li:nth-child(3) {
  flex: 0 2 100px;
  background: aqua;
}
<ul class="flex1 flex">
  <li>180px</li>
  <li>160px</li>
  <li>60px</li>
</ul>

Ответ 1

Flexbox определяет это как

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

Упрощение, замороженные элементы гибкости - это те, которые больше не могут или не нужно сгибать. Я не буду принимать никаких ограничений min-width и ненулевых коэффициентов усадки flex. Таким образом, все элементы flex изначально разморожены, и все они замерзают после одной итерации гибкого цикла.

Размер внутренней гибкой основы зависит от значения box-sizing, определенного CSS2UI как

  • content-box: Указанная ширина и высота (и соответствующие свойства min/max) применяются к ширине и высоте соответственно поле содержимого элемента. Прокладка и граница элемента выложены и выведены за пределы указанной ширины и высоты.

  • border-box: Значения длины и процента для ширины и высоты (и соответствующих свойств min/max) этого элемента определяют поле границы элемента. То есть, любое заполнение или граница, указанные на элемент выложен и нарисован внутри указанного widthи height. Ширина и высота содержимого вычисляются по формуле вычитая границы и ширину заполнения соответствующих сторон из заданные свойства ширины и высоты. [...] Используемые значения, как например, через getComputedStyle(), также ссылаются на поле границы.

В основном это означает, что размеры (ширины, основания изгиба) имеют внутренний внешний вариант. Внутренний размер включает только контент, внешний также включает прокладки и ширину границы. Длина, указанная в таблице стилей, будет использоваться как внутренний размер в случае box-sizing: content-box, или как внешний в случае box-sizing: border-box. Другой может быть рассчитан путем добавления или вычитания ширины границы и заполнения.

Пренебрегая множеством деталей, алгоритм будет похож на

let sumScaledShrinkFactors = 0,
    remainingFreeSpace = flexContainer.innerMainSize;
for (let item of flexItems) {
  remainingFreeSpace -= item.outerFlexBasis;
  item.scaledShrinkFactor = item.innerFlexBasis * item.flexShrinkFactor;
  sumScaledShrinkFactors += item.scaledShrinkFactor;
}
for (let item of flexItems) {
  let ratio = item.scaledShrinkFactor / sumScaledShrinkFactors;
  item.innerWidth = item.innerFlexBasis + ratio * remainingFreeSpace;
}

Без paddings, это, как вы объясните

                                   (width)
                                   innerW │ padd │ outerW
                                   ───────┼──────┼───────
300px * (1 + 2 / 1000px * -200px) = 180px │  0px │  180px
200px * (1 + 1 / 1000px * -200px) = 160px │  0px │  160px
100px * (1 + 2 / 1000px * -200px) =  60px │  0px │   60px
                                   ───────┼──────┼───────
                                    400px │  0px │  400px

С горизонтальными прокладками 10px доступное пространство уменьшается на 3 * 2 * 10px = 60px, поэтому теперь оно -260px.

                                   (width)
                                   innerW │ padd │ outerW
                                   ───────┼──────┼───────
300px * (1 + 2 / 1000px * -260px) = 144px │ 20px │  164px
200px * (1 + 1 / 1000px * -260px) = 148px │ 20px │  168px
100px * (1 + 2 / 1000px * -260px) =  48px │ 20px │   68px
                                   ───────┼──────┼───────
                                    340px │ 60px │  400px

Когда вы используете box-sizing: border-box, указанные основания flex являются внешними, поэтому выкладки вычитаются из них для вычисления внутренних, которые 280px, 180px, 80px. Тогда сумма масштабированных коэффициентов усадки flex становится 2*280px + 180px + 2*80px = 900px. Доступное пространство похоже на случай без прокладки, потому что внешние основания гибки одинаковы. Обратите внимание, что width, полученный с помощью getComputedStyle, теперь будет внешним, поэтому paddings добавляются обратно в конец.

                                                    (width)
                                    innerW │ padd │  outerW
                                   ────────┼──────┼────────
280px * (1 + 2 / 900px * -200px) ≈ 155.6px │ 20px │ 175.6px
180px * (1 + 1 / 900px * -200px) = 140.0px │ 20px │ 160.0px
 80px * (1 + 2 / 900px * -200px) ≈  44.4px │ 20px │  64.4px
                                   ────────┼──────┼────────
                                   340.0px │ 60px │ 400.0px

Ответ 2

Помимо ответа Oriol (который он теперь обновил), padding/border-box будет

280px * (1 + 2 / 900px * -200px) + 20px = 175.5px
180px * (1 + 1 / 900px * -200px) + 20px = 160px
 80px * (1 + 2 / 900px * -200px) + 20px = 64.4px

где, как представляется, заполнение сначала удаляется из основы flex, а затем добавляется обратно после фактического вычисления, что делает некоторый смысл, так как это происходит с padding.