Manter a proporção da imagem ao alterar a altura

114

Usando o modelo CSS flex box, como posso forçar uma imagem a manter sua proporção?

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

Observe que as imagens aumentam ou diminuem para preencher a largura do contêiner. Tudo bem, mas também podemos esticar ou diminuir a altura para manter as proporções da imagem?

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;
}
coderMe
fonte
1
Pelo que eu sei, a melhor solução é usar um <div>com CSSbackground-image: url(...);background-size:contain; background-repeat:no-repeat
Blazemonger
1
Existem algumas pistas interessantes aqui, mas e quando as imagens já não têm a mesma proporção? Adicionar um div 'extra' é a última coisa com a qual precisamos nos preocupar. Por que não usar um caso de teste como este: jsfiddle.net/sheriffderek/999Lg9qv
sheriffderek

Respostas:

68

Para tags img, se você definir um lado, o outro lado será redimensionado para manter a proporção e, por padrão, as imagens serão expandidas para seu tamanho original.

Usando esse fato, se você envolver cada tag img na tag div e definir sua largura para 100% do div pai, a altura ficará de acordo com a proporção desejada.

http://jsfiddle.net/0o5tpbxg/

* {
    margin: 0;
    padding: 0;
}
.slider {
    display: flex;
}
.slider .slide img {
    width: 100%;
}
Muhammad Umer
fonte
2
Sei que há duas respostas que sugerem o uso de divs, mas estou marcando essa resposta como correta porque ela explica por que as alturas das imagens são diferentes do que eu esperava. Na verdade, este é um comportamento normal e correto e é improvável que seja "consertado", visto que não é um bug.
coderMe de
18
Mas essa resposta não fala nada sobre flexbox, porque as imagens se comportam de maneira diferente dentro do contêiner flexbox. Definir um lado não redimensionará proporcionalmente o outro. Envolvê-los em um contêiner não flexbox ajuda, mas adiciona marcação desnecessária e perde semanticidade.
Robert Koritnik de
3
Não parece que você precisa de uma div envolvente para fazer isso funcionar: jsfiddle.net/xLc2Le0k/120
Rick Strahl
4
Mas e quanto a isso? jsfiddle.net/xLc2Le0k/15 Acho que essa solução é um acaso que só funciona porque as imagens são todas da mesma proporção. Nota lateral: não há nenhuma 'semanticidade' perdida ao colocar uma imagem em uma div para posicionamento / especialmente quando se trata de imagens responsivas.
sheriffderek
1
Sério a solução mais limpa e funciona com imagem!
shaneonabike,
64

Para evitar que as imagens se estendam em qualquer um dos eixos dentro de um pai flexível, encontrei algumas soluções.

Você pode tentar usar ajuste de objeto na imagem que, por exemplo

object-fit: contain;

http://jsfiddle.net/ykw3sfjd/

Ou você pode adicionar regras flex-specfic que podem funcionar melhor em alguns casos.

align-self: center;
flex: 0 0 auto;
Matty Balaam
fonte
4
o ajuste do objeto poderia ser uma boa solução se não faltasse o suporte no IE e no Edge mesmo até o Edge 14
daniels
1
Confira este fallback de ajuste de objeto para Edge an IE .
Andrei Telteu
Tenho usado esse recurso, funciona muito bem.
Matty Balaam
1
@daniels Edge agora tem ajuste de objeto em desenvolvimento: developer.microsoft.com/en-us/microsoft-edge/platform/status/…
AMDG
1
object-fit: contain;funcionou para mim, quando eu só tinha o problema no cromo, ff estava bem.
Benjamin Peter
53

Não há necessidade de adicionar um div contendo.

O padrão para a propriedade css "align-items" é "stretch" que é o que está fazendo com que suas imagens sejam esticadas até sua altura original. Definir a propriedade css "align-items" para "flex-start" corrige o problema.

.slider {
    display: flex;
    align-items: flex-start;  /* ADD THIS! */
}
Marvin Herbold
fonte
3
Ahh ... obrigado por realmente fazer as coisas da maneira com suporte. Eu estava lendo as respostas até aquela e não conseguia acreditar que tudo o que tínhamos eram hacks sujos ..
Félix Gagnon-Grenier
35

A maioria das imagens com dimensões intrínsecas , ou seja, um tamanho natural, como uma jpegimagem. Se o tamanho especificado definir tanto a largura quanto a altura, o valor ausente é determinado usando a proporção intrínseca ... - consulte MDN .

Mas isso não funciona como esperado se as imagens que estão sendo definidas como itens flexíveis diretos com o Módulo de Layout de Caixa Flexível atual Nível 1 , até onde eu sei.

Veja essas discussões e os relatórios de bugs podem estar relacionados:

  • Flexbugs # 14 - Dimensionamento intrínseco do Chrome / Flexbox não implementado corretamente.
  • Firefox Bug 972595 - Flex containers devem usar "flex-basis" ao invés de "width" para calcular larguras intrínsecas de flex items
  • Chromium Issue 249112 - No Flexbox , permite relações de aspecto intrínsecas para informar o cálculo do tamanho principal.

Como solução alternativa, você pode envolver cada <img>um com um <div>ou um <span>ou assim.

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 Tamanho mínimo implícito de itens flexíveis

Para fornecer um tamanho mínimo padrão mais razoável para itens flexíveis , esta especificação apresenta um novo valor auto como o valor inicial das propriedades min-width e min-height definidas no CSS 2.1.


Como alternativa, você pode usar o tablelayout CSS , no qual obterá resultados semelhantes flexbox, pois funcionará em mais navegadores, até mesmo para o 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>

Adesivos
fonte
O primeiro snippet precisa .slider > div{min-width:0}de novos navegadores que suportem min-width: auto.
Oriol
3
A especificação do flexbox introduziu um novo autovalor como o valor padrão para min-widthe min-height(anteriormente era 0). O Chrome ainda não o implementa, então seu primeiro snippet funciona. Mas os novos navegadores precisam disso para permitir que os itens flexíveis sejam menores do que a largura padrão das imagens.
Oriol
2
+1 esta resposta deve ser aceita porque fala sobre imagens no modelo flexbox que é a raiz do problema neste caso ...
Robert Koritnik
A tabela é ótima se seu layout não muda em vários pontos de interrupção, mas isso é improvável atualmente.
sheriffderek
1
E quando as imagens não são todas da mesma proporção? jsfiddle.net/sheriffderek/5u7y21we
sheriffderek
6

Tenho brincado com o flexbox ultimamente e cheguei a uma solução para isso através da experimentação e do seguinte raciocínio. No entanto, na realidade, não tenho certeza se isso é exatamente o que acontece.

Se a largura real for afetada pelo sistema flexível. Assim, após a largura dos elementos atingir a largura máxima do pai, a largura extra definida no css é ignorada. Então é seguro definir a largura para 100%.

Como a altura da tag img é derivada da própria imagem, definir a altura como 0% pode fazer algo. (é aqui que eu não estou certo sobre o quê ... mas fazia sentido para mim que deveria consertar)

DEMO

(lembre-se de ter visto aqui primeiro!)

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

Funciona apenas em cromo ainda

Muhammad Umer
fonte
Interessante, mas parece um bug. De acordo com a especificação , " A porcentagem é calculada em relação à altura do bloco de conteúdo da caixa gerada. Se a altura do bloco de conteúdo não for especificada explicitamente (ou seja, depende da altura do conteúdo), e este elemento não está absolutamente posicionado , o valor é calculado paraauto ". Isso é o que acontece no Firefox.
Oriol
jsfiddle.net/xLc2Le0k/8 explica a diferença em ff e chrome. para isso
Muhammad Umer
a largura parece estar fazendo em ff o que a altura está fazendo no cromo.
Muhammad Umer
É interessante, mas temo que as soluções apenas para o navegador X não sejam realmente soluções. Seu violino comentado, aqui: jsfiddle.net/xLc2Le0k/8 é absolutamente fascinante , no entanto, em FF. Isso parece mostrar que FF está tornando a altura da imagem "proporcional" à altura da imagem se ela realmente fosse 100%. Em outras palavras, o img não "sabe" sobre a tela do contêiner: flex. Isso explica por que, em FF, se você definir a largura da imagem como 100%, a imagem será mais alta do que se você definir a largura como automática.
coderMe de
Isso é meio perto ... mas só funciona no Chrome: jsfiddle.net/sheriffderek/xLc2Le0k/270
sheriffderek
2

Esse comportamento é esperado. flex container estenderá todos os seus filhos por padrão. A imagem não tem exceção. (ou seja, o pai terá align-items: stretchpropriedade)

Para manter a proporção da imagem, temos duas soluções:

  1. Substitua a align-items: stretchpropriedade padrão por align-items: flex-startou align-self: centeretc, http://jsfiddle.net/e394Lqnt/3/

ou

  1. Defina a propriedade de alinhamento exclusivamente para o próprio filho: like, align-self: centerou align-self: flex-startetc. http://jsfiddle.net/e394Lqnt/2/
Asim KT
fonte
-1

Na verdade descobri que o container da tag da imagem precisa ter uma altura para manter a proporção da imagem. por exemplo>

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

então, em seu html, seu contêiner envolverá a imagem da tag

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

isso funciona para o IE e outros navegadores

Kengreg
fonte
-1

Eu simplesmente tive o mesmo problema. Use o alinhamento flexível para centralizar seus elementos. Você precisa usar em align-items: centervez de align-content: center. Isso manterá a proporção de aspecto, enquanto o alinhamento de conteúdo não manterá a proporção de aspecto.

Clinton Green
fonte