css - para - ¿Cómo funciona el factor flex-encogible en el relleno y en el recuadro de borde?
margin css (2)
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;
}
<ul class="flex1 flex">
<li>180px</li>
<li>160px</li>
<li>60px</li>
</ul>
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.
¿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;
}
<ul class="flex1 flex">
<li>180px</li>
<li>160px</li>
<li>60px</li>
</ul>
<ul class="flex2 flex">
<li>144px</li>
<li>148px</li>
<li>48px</li>
</ul>
<ul class="flex3 flex">
<li>175.5px</li>
<li>160px</li>
<li>64.4px</li>
</ul>
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
.
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 suma 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 objetivo 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 estewidth
yheight
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 suma 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