¿Cómo funciona el factor flex-encogible en el relleno y en el recuadro de borde?

Digamos que tenemos un contenedor flexible con un ancho de 400px.

Dentro de este contenedor hay tres elementos flexibles:

: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; } 

Por lo tanto, el ancho total de los elementos Flex es de 600 px y el espacio libre en el contenedor es de -200 px.

Con la ayuda de esta pregunta , este artículo y la especificación flexbox , aprendí la fórmula básica para distribuir espacio libre negativo entre los elementos flexibles:

Paso 1

Multiplique cada valor de contracción por su base y agréguelas todas juntas:

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

TOTAL = 1000

Paso 2

Divida cada elemento arriba por el total para determinar el factor de contracción :

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

Paso 3

Multiplique el factor de reducción por el espacio libre negativo para determinar el espacio eliminado de cada elemento:

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

Por lo tanto, el tamaño calculado de cada elemento flexible debe ser:

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

Probado en Chrome, Firefox e IE11, los números salen. El cálculo funciona perfectamente.

 .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; } 
 
  • 180px
  • 160px
  • 60px

demo jsFiddle


El problema

Sin embargo, cuando se introduce relleno, los resultados de contracción son diferentes. Cuando se aplica relleno junto con box-sizing: border-box , esto da como resultado otro conjunto de números.

enter image description here

¿Cuál es el cálculo de flex-shrink cuando se trata de padding y box-sizing ?

 .flex { display: flex; width: 400px; height: 50px; margin: 0; padding: 0; list-style: none; text-align: center; border-bottom: 1px solid #ccc; } .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; } .flex2 li { padding: 0 10px; } .flex3 li { padding: 0 10px; box-sizing: border-box; } 
 
  • 180px
  • 160px
  • 60px
  • 144px
  • 148px
  • 48px
  • 175.5px
  • 160px
  • 64.4px

demo jsFiddle

Flexbox define esto como

Para cada elemento no congelado en la línea, multiplique su factor de contracción flexible por su tamaño de base de flexión interior, y tenga en cuenta esto como su factor de contracción flexible a escala . Encuentre la relación entre el factor de contracción flexible a escala del artículo y la sum de los factores de contracción flexible a escala de todos los artículos no congelados en la línea. Establezca el tamaño principal objective del elemento en su tamaño de base flexible menos una fracción del valor absoluto del espacio libre restante proporcional a la relación.

Los artículos flexibles y congelados son aquellos que ya no pueden o no deben doblarse. No asumiré restricciones de min-width ni factores de contracción flexibles distintos de cero. De esta forma, todos los artículos flexibles se descongelan inicialmente y todos se congelan después de una sola iteración del ciclo flexible.

El tamaño base de la flexión interna depende del valor del box-sizing de la box-sizing , definido por CSS2UI como

  • content-box : el ancho y la altura especificados (y las propiedades min / max respectivas) se aplican al ancho y alto, respectivamente, del cuadro de contenido del elemento. El relleno y el borde del elemento se disponen y dibujan fuera del ancho y la altura especificados.

  • border-box : los valores de longitud y porcentajes para ancho y alto (y las propiedades min / max respectivas) en este elemento determinan el recuadro de borde del elemento. Es decir, cualquier relleno o borde especificado en el elemento se presenta y dibuja dentro de este width y height especificados. El ancho y alto del contenido se calcula restando el ancho del borde y del relleno de los lados respectivos de las propiedades de ancho y alto especificadas. […] Los valores utilizados, como los expuestos, por ejemplo, a través de getComputedStyle (), también se refieren a la casilla de borde.

Básicamente, eso significa que los tamaños (anchuras, bases flexibles) tienen una variante interna y una externa. El tamaño interno incluye solo el contenido, el exterior también incluye almohadillas y anchos de bordes. La longitud especificada en la hoja de estilo se usará como el tamaño interno en el caso del tamaño del box-sizing: content-box , o como el exterior en el caso del box-sizing: border-box del box-sizing: border-box . El otro puede calcularse agregando o restando anchos de borde y relleno.

Despreciando muchos detalles, el algoritmo sería algo así como

 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; } 

Sin almohadillas, es como explicas

  (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 

Con los 10px horizontales de 10px , el espacio disponible se reduce en 3 * 2 * 10px = 60px , por lo que ahora es -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 

Cuando se usa box-sizing: border-box , las bases flexibles especificadas son las externas, por lo que se 280px 180px de ellas para calcular las internas, que son 280px , 180px , 80px . Entonces, la sum de los factores de contracción flexible a escala se convierte en 2*280px + 180px + 2*80px = 900px . El espacio disponible es como en el caso sin relleno, porque las bases exteriores flexibles son las mismas. Tenga en cuenta que el width recuperado por getComputedStyle ahora será el externo, por lo que los rellenos se agregarán al final.

  (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 

Además de la respuesta de Oriol (que ahora ha actualizado) , el padding / border-box será

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

donde parece que el relleno primero se elimina de la base flexible y luego se vuelve a agregar, después del cálculo real, lo que tiene algún sentido, ya que eso es lo que sucede con el padding .