Mantenga la relación de aspecto de la imagen al cambiar la altura

114

Usando el modelo de caja flexible CSS, ¿cómo puedo forzar una imagen a mantener su relación de aspecto?

JS Fiddle: http://jsfiddle.net/xLc2Le0k/2/

Observe que las imágenes se estiran o encogen para llenar el ancho del contenedor. Está bien, pero ¿podemos hacer que también se estire o encoja la altura para mantener las proporciones de la imagen?

HTML

<div class="slider">
    <img src="http://www.placebacon.net/400/300" alt="Bacn">
    <img src="http://www.placebacon.net/400/300" alt="Bacn">
    <img src="http://www.placebacon.net/400/300" alt="Bacn">
    <img src="http://www.placebacon.net/400/300" alt="Bacn">
</div>

CSS

.slider {
    display: flex;
}
.slider img {
    margin: 0 5px;
}
codificador
fuente
1
Hasta donde yo sé, la mejor solución es usar un <div>con CSSbackground-image: url(...);background-size:contain; background-repeat:no-repeat
Blazemonger
1
Hay algunas pistas interesantes aquí, pero ¿qué pasa cuando las imágenes ya no tienen la misma proporción? Agregar un div 'extra' es lo último de lo que debemos preocuparnos. ¿Por qué no usar un caso de prueba como este en su lugar: jsfiddle.net/sheriffderek/999Lg9qv
sheriffderek

Respuestas:

68

Para las etiquetas img, si define un lado, el otro lado cambia de tamaño para mantener la relación de aspecto y, por defecto, las imágenes se expanden a su tamaño original.

Usando este hecho, si envuelve cada etiqueta img en la etiqueta div y establece su ancho al 100% del div principal, la altura estará de acuerdo con la relación de aspecto que desee.

http://jsfiddle.net/0o5tpbxg/

* {
    margin: 0;
    padding: 0;
}
.slider {
    display: flex;
}
.slider .slide img {
    width: 100%;
}
Muhammad Umer
fuente
2
Sé que hay dos respuestas que sugieren el uso de divs, pero estoy marcando esta respuesta como correcta porque explica por qué las alturas de la imagen son diferentes de lo que esperaba. De hecho, este es un comportamiento normal y correcto, y es poco probable que se "arregle", ya que no es un error.
coderMe
18
Pero esta respuesta no habla nada sobre flexbox, porque las imágenes se comportan de manera diferente dentro del contenedor flexbox. Establecer un lado no cambiará proporcionalmente el tamaño del otro. Envolverlos en un contenedor que no sea flexbox ayuda, pero eso agrega un marcado innecesario y pierde semanticidad.
Robert Koritnik
3
No parece que necesite un div de envoltura para que esto funcione: jsfiddle.net/xLc2Le0k/120
Rick Strahl
4
Pero, ¿y esto? jsfiddle.net/xLc2Le0k/15 Creo que esta solución es una casualidad que solo funciona porque las imágenes tienen la misma proporción. nota al margen: no se pierde ninguna 'semanticidad' al colocar una imagen en un div para posicionar / especialmente cuando se trata de imágenes receptivas.
sheriffderek
1
¡En serio, la solución más limpia y funciona con imagen!
shaneonabike
64

Para evitar que las imágenes se estiren en cualquiera de los ejes dentro de un padre flexible, he encontrado un par de soluciones.

Puede intentar usar el ajuste de objeto en la imagen que, por ejemplo,

object-fit: contain;

http://jsfiddle.net/ykw3sfjd/

O puede agregar reglas específicas de flexibilidad que pueden funcionar mejor en algunos casos.

align-self: center;
flex: 0 0 auto;
Matty Balaam
fuente
4
object-fit podría ser una buena solución si no faltara soporte en IE y Edge incluso hasta Edge 14
daniels
1
Echa un vistazo a esta alternativa de ajuste a objetos para Edge y IE .
Andrei Telteu
Yo mismo he estado usando esa alternativa, funciona muy bien.
Matty Balaam
1
@daniels Edge ahora tiene ajuste de objetos en desarrollo: developer.microsoft.com/en-us/microsoft-edge/platform/status/…
AMDG
1
object-fit: contain;hizo el truco para mí, cuando solo tuve el problema en Chrome, ff estaba bien.
Benjamin Peter
53

No es necesario agregar un div contenedor.

El valor predeterminado de la propiedad css "align-items" es "stretch", que es lo que hace que las imágenes se estiren a su altura original completa. Establecer la propiedad css "align-items" en "flex-start" soluciona el problema.

.slider {
    display: flex;
    align-items: flex-start;  /* ADD THIS! */
}
Marvin Herbold
fuente
3
Ahh ... gracias por hacer las cosas de la forma admitida. Estuve leyendo las respuestas hasta ese y no podía creer que todo lo que teníamos eran trucos sucios ..
Félix Gagnon-Grenier
35

La mayoría de imágenes con dimensiones intrínsecas , es decir, un tamaño natural, como una jpegimagen. Si el tamaño especificado define tanto el ancho como el alto, el valor faltante se determina utilizando la relación intrínseca ... - ver MDN .

Pero eso no funciona como se esperaba si las imágenes que se configuran como elementos de flexión directa con el módulo de diseño de caja flexible actual Nivel 1 , hasta donde yo sé.

Vea estas discusiones y los informes de errores pueden estar relacionados:

  • Flexbugs # 14 - El tamaño intrínseco de Chrome / Flexbox no se implementó correctamente.
  • Error de Firefox 972595 : los contenedores flexibles deben usar "base flexible" en lugar de "ancho" para calcular los anchos intrínsecos de los elementos flexibles
  • Chromium Issue 249112 : en Flexbox, permita relaciones de aspecto intrínsecas para informar el cálculo del tamaño principal.

Como solución alternativa, puede envolver cada uno <img>con a <div>o a <span>, o así.

jsFiddle

.slider {
  display: flex;
}

.slider>div {
  min-width: 0; /* why? see below. */
}

.slider>div>img {
  max-width: 100%;
  height: auto;
}
<div class="slider">
  <div><img src="https://picsum.photos/400/300?image=0" /></div>
  <div><img src="https://picsum.photos/400/300?image=1" /></div>
  <div><img src="https://picsum.photos/400/300?image=2" /></div>
  <div><img src="https://picsum.photos/400/300?image=3" /></div>
</div>

4.5 Tamaño mínimo implícito de los elementos flexibles

Para proporcionar un tamaño mínimo predeterminado más razonable para los elementos flexibles , esta especificación introduce un nuevo valor automático como el valor inicial de las propiedades min-width y min-height definidas en CSS 2.1.


Alternativamente, puede usar el tablediseño CSS en su lugar, que obtendrá resultados similares ya flexboxque funcionará en más navegadores, incluso para IE8.

jsFiddle

.slider {
  display: table;
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

.slider>div {
  display: table-cell;
  vertical-align: top;
}

.slider>div>img {
  max-width: 100%;
  height: auto;
}
<div class="slider">
  <div><img src="https://picsum.photos/400/300?image=0" /></div>
  <div><img src="https://picsum.photos/400/300?image=1" /></div>
  <div><img src="https://picsum.photos/400/300?image=2" /></div>
  <div><img src="https://picsum.photos/400/300?image=3" /></div>
</div>

Pegatinas
fuente
El primer fragmento debe ser .slider > div{min-width:0}compatible con los nuevos navegadores min-width: auto.
Oriol
3
La especificación flexbox introdujo un nuevo autovalor como valor predeterminado para min-widthy min-height(anteriormente lo era 0). Chrome aún no lo implementa, por lo que su primer fragmento funciona. Pero los nuevos navegadores lo necesitan para permitir que los elementos flexibles se encojan más pequeño que el ancho predeterminado de las imágenes.
Oriol
2
+1 esto debería ser una respuesta aceptada porque habla de imágenes en el modelo flexbox, que es la raíz del problema en este caso ...
Robert Koritnik
La tabla es excelente si su diseño no cambia en varios puntos de interrupción, pero eso es poco probable en estos días.
sheriffderek
1
¿Qué pasa cuando las imágenes no tienen la misma proporción? jsfiddle.net/sheriffderek/5u7y21we
sheriffderek
6

He estado jugando con flexbox últimamente y llegué a la solución a través de la experimentación y el siguiente razonamiento. Sin embargo, en realidad no estoy seguro de si esto es exactamente lo que sucede.

Si el ancho real se ve afectado por el sistema flexible. Entonces, después de que el ancho de los elementos alcanza el ancho máximo del padre, se ignora el ancho adicional establecido en css. Entonces es seguro establecer el ancho al 100%.

Dado que la altura de la etiqueta img se deriva de la propia imagen, establecer la altura en 0% podría hacer algo. (aquí es donde no tengo claro qué ... pero tenía sentido para mí que debería arreglarlo)

MANIFESTACIÓN

(¡recuerda que lo vi aquí primero!)

.slider {
    display: flex;
}
.slider img {
    height: 0%;
    width: 100%;
    margin: 0 5px;
}

Funciona solo en Chrome todavía

Muhammad Umer
fuente
Interesante, pero parece un error. Según la especificación , " El porcentaje se calcula con respecto a la altura del bloque contenedor de la caja generada. Si la altura del bloque contenedor no se especifica explícitamente (es decir, depende de la altura del contenido), y este elemento no está absolutamente posicionado , el valor se calcula enauto ". Eso es lo que sucede en Firefox.
Oriol
jsfiddle.net/xLc2Le0k/8 explica la diferencia en ff y chrome. para esto
Muhammad Umer
El ancho parece estar haciendo en ff lo que hace la altura en Chrome.
Muhammad Umer
Es interesante, pero me temo que las soluciones solo para navegador X no son realmente soluciones. Su violín comentado, aquí: jsfiddle.net/xLc2Le0k/8 es absolutamente fascinante , sin embargo, en FF. Esto parece mostrar que FF hace que la altura de la imagen sea "proporcional" a la altura de la imagen si realmente fuera del 100%. En otras palabras, el img no "sabe" acerca de la pantalla del contenedor: flex. Esto explica por qué, en FF, si establece el ancho del img en 100%, la imagen es más alta que si configura el ancho en automático.
coderMe
Esto está un poco cerca ... pero solo funciona en Chrome: jsfiddle.net/sheriffderek/xLc2Le0k/270
sheriffderek
2

Se espera este comportamiento. flex container estirará todos sus hijos de forma predeterminada. La imagen no tiene excepción. (es decir, el padre tendráalign-items: stretch propiedad)

Para mantener la relación de aspecto, tenemos dos soluciones:

  1. Reemplace la align-items: stretchpropiedad predeterminada en align-items: flex-starto align-self: centeretc, http://jsfiddle.net/e394Lqnt/3/

o

  1. Establezca la propiedad de alineación exclusivamente para el propio niño: me gusta, align-self: centero align-self: flex-startetc. http://jsfiddle.net/e394Lqnt/2/
Asim KT
fuente
-1

En realidad, descubrí que el contenedor de la etiqueta de la imagen debe tener una altura para mantener la proporción de la imagen. por ejemplo>

.container{display:flex; height:100%;} img{width:100%;height:auto;}

luego en su html su contenedor envolverá la imagen de la etiqueta

<div class="container"><img src="image.jpg" alt="image" /></div>

este trabajo para IE y otros navegadores

Kengreg
fuente
-1

Solo tuve el mismo problema. Utilice la alineación flexible para centrar sus elementos. Necesita usar en align-items: centerlugar de align-content: center. Esto mantendrá la relación de aspecto, mientras que align-content no mantendrá la relación de aspecto.

Clinton Green
fuente