Conserver le rapport hauteur / largeur de l'image lors du changement de hauteur

114

En utilisant le modèle de boîte flexible CSS, comment puis-je forcer une image à conserver ses proportions?

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

Notez que les images s'étirent ou se rétrécissent afin de remplir la largeur du conteneur. C'est bien, mais pouvons-nous aussi l'étirer ou réduire la hauteur pour conserver les proportions de l'image?

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
la source
1
Autant que je sache, la meilleure solution est d'utiliser un <div>avec le CSSbackground-image: url(...);background-size:contain; background-repeat:no-repeat
Blazemonger
1
Il y a des pistes intéressantes ici, mais qu'en est-il lorsque les images ne sont pas déjà au même rapport? L'ajout d'un div "supplémentaire" est la dernière chose dont nous devons nous soucier. Pourquoi ne pas utiliser un cas de test comme celui-ci à la place: jsfiddle.net/sheriffderek/999Lg9qv
sheriffderek

Réponses:

68

Pour les balises img, si vous définissez un côté, l'autre côté est redimensionné pour conserver les proportions et, par défaut, les images se développent à leur taille d'origine.

En utilisant ce fait, si vous enveloppez chaque balise img dans une balise div et définissez sa largeur à 100% de la div parent, la hauteur sera en fonction du rapport hauteur / largeur souhaité.

http://jsfiddle.net/0o5tpbxg/

* {
    margin: 0;
    padding: 0;
}
.slider {
    display: flex;
}
.slider .slide img {
    width: 100%;
}
Muhammad Umer
la source
2
Je sais qu'il y a deux réponses qui suggèrent l'utilisation de divs, mais je marque cette réponse comme étant correcte car cela explique pourquoi les hauteurs d'image sont différentes de celles auxquelles je m'attendais. En fait, c'est un comportement normal et correct, et il est peu probable qu'il soit «corrigé», car ce n'est pas un bogue.
coderMe
18
Mais cette réponse ne parle pas de flexbox, car les images se comportent différemment dans le conteneur flexbox. Définir un côté ne redimensionnera pas proportionnellement l'autre. Les envelopper dans un conteneur non flexbox aide mais cela ajoute un balisage inutile perdant la sémantique.
Robert Koritnik
3
Il ne semble pas que vous ayez besoin d'une division d'
Rick Strahl
4
Mais qu'en est-il de ça? jsfiddle.net/xLc2Le0k/15 Je pense que cette solution est un hasard qui ne fonctionne que parce que les images sont toutes au même ratio. note latérale: il n'y a pas de «sémantique» perdue en plaçant une image dans un div pour le positionnement / en particulier lorsqu'il s'agit d'images réactives.
sheriffderek
1
Sérieusement la solution la plus propre et cela fonctionne avec l'image!
shaneonabike
64

Pour empêcher les images de s'étirer dans les deux axes à l'intérieur d'un parent flex, j'ai trouvé quelques solutions.

Vous pouvez essayer d'utiliser object-fit sur l'image qui, par exemple

object-fit: contain;

http://jsfiddle.net/ykw3sfjd/

Ou vous pouvez ajouter des règles flex-specfic qui peuvent mieux fonctionner dans certains cas.

align-self: center;
flex: 0 0 auto;
Matty Balaam
la source
4
object-fit pourrait être une bonne solution s'il ne manquait pas de support dans IE et Edge même jusqu'à Edge 14
Daniels
1
Découvrez cette solution de secours adaptée aux objets pour Edge an IE .
Andrei Telteu
J'utilise moi-même cette solution de secours, cela fonctionne très bien.
Matty Balaam
1
@daniels Edge a maintenant une adaptation aux objets en développement: developer.microsoft.com/en-us/microsoft-edge/platform/status/…
AMDG
1
object-fit: contain;a fait l'affaire pour moi, quand je n'avais que le problème de chrome, ff allait bien.
Benjamin Peter
53

Pas besoin d'ajouter un div contenant.

La valeur par défaut de la propriété css "align-items" est "stretch", ce qui provoque l'étirement de vos images à sa hauteur d'origine. La définition de la propriété css "align-items" sur "flex-start" résout votre problème.

.slider {
    display: flex;
    align-items: flex-start;  /* ADD THIS! */
}
Marvin Herbold
la source
3
Ahh ... merci de faire les choses de la manière soutenue. Je lisais les réponses jusqu'à celle-là et je n'arrivais pas à croire que tout ce que nous avions était de sales hacks ..
Félix Gagnon-Grenier
35

La plupart des images avec des dimensions intrinsèques , c'est-à-dire une taille naturelle, comme une jpegimage. Si la taille spécifiée définit à la fois la largeur et la hauteur, la valeur manquante est déterminée en utilisant le rapport intrinsèque ... - voir MDN .

Mais cela ne fonctionne pas comme prévu si les images définies comme éléments flexibles directs avec le module de mise en page de boîte flexible actuel de niveau 1 , pour autant que je sache.

Consultez ces discussions et les rapports de bogue peuvent être liés:

  • Flexbugs # 14 - Le dimensionnement intrinsèque de Chrome / Flexbox n'est pas implémenté correctement.
  • Firefox Bogue 972595 - Les conteneurs Flex doivent utiliser "flex-base" au lieu de "width" pour calculer les largeurs intrinsèques des éléments flex
  • Chromium Issue 249112 - Dans Flexbox, autorisez les proportions intrinsèques à informer le calcul de la taille principale.

Pour contourner ce problème, vous pouvez encapsuler chacun <img>avec un <div>ou un <span>, ou deux.

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 Taille minimale implicite des éléments flexibles

Pour fournir une taille minimale par défaut plus raisonnable pour les éléments flexibles , cette spécification introduit une nouvelle valeur automatique comme valeur initiale des propriétés min-width et min-height définies dans CSS 2.1.


Alternativement, vous pouvez utiliser la tablemise en page CSS à la place, ce qui vous donnera des résultats similaires car flexboxcela fonctionnera sur plus de navigateurs, même pour 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>

Autocollants
la source
Le premier extrait de code nécessite .slider > div{min-width:0}les nouveaux navigateurs prenant en charge min-width: auto.
Oriol
3
La spécification flexbox a introduit une nouvelle autovaleur comme valeur par défaut pour min-widthet min-height(auparavant 0). Chrome ne le met pas encore en œuvre, votre premier extrait de code fonctionne donc. Mais les nouveaux navigateurs en ont besoin pour permettre aux éléments flexibles de rétrécir plus petit que la largeur par défaut des images.
Oriol
2
+1 cela devrait être une réponse acceptée car il parle d'images dans le modèle flexbox qui est le problème racine dans ce cas ...
Robert Koritnik
La table est excellente si votre mise en page ne change pas à divers points de rupture, mais cela est peu probable de nos jours.
sheriffderek
1
Et quand les images ne sont pas toutes du même rapport? jsfiddle.net/sheriffderek/5u7y21we
sheriffderek
6

J'ai joué avec flexbox ces derniers temps et j'en suis venu à une solution à travers l'expérimentation et le raisonnement suivant. Cependant, en réalité, je ne sais pas si c'est exactement ce qui se passe.

Si la largeur réelle est affectée par le système flex. Ainsi, une fois que la largeur des éléments a atteint la largeur maximale du parent, la largeur supplémentaire définie dans css est ignorée. Ensuite, il est prudent de définir la largeur à 100%.

Étant donné que la hauteur de la balise img est dérivée de l'image elle-même, régler la hauteur à 0% pourrait faire quelque chose. (c'est là que je ne sais pas quoi ... mais il me semblait logique que cela corrige le problème)

DEMO

(rappelez-vous l'avoir vu ici en premier!)

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

Fonctionne encore uniquement dans Chrome

Muhammad Umer
la source
Intéressant, mais cela semble être un bug. Selon les spécifications , " Le pourcentage est calculé par rapport à la hauteur du bloc contenant de la boîte générée. Si la hauteur du bloc contenant n'est pas spécifiée explicitement (c'est-à-dire qu'elle dépend de la hauteur du contenu), et que cet élément n'est pas positionné de manière absolue , la valeur correspond àauto ". C'est ce qui se passe sur Firefox.
Oriol
jsfiddle.net/xLc2Le0k/8 explique la différence entre ff et chrome. pour cela
Muhammad Umer
largeur semble faire en ff ce que fait la hauteur en chrome.
Muhammad Umer
C'est intéressant, mais je crains que les solutions exclusivement réservées au navigateur X ne soient pas vraiment des solutions. Votre violon commenté, ici: jsfiddle.net/xLc2Le0k/8 est absolument fascinant , cependant, dans FF. Cela semble montrer que FF rend la hauteur de l'image "proportionnelle" à la hauteur de l'image si elle était vraiment de 100%. En d'autres termes, l'img ne "connaît" pas l'affichage du conteneur: flex. Cela explique pourquoi, dans FF, si vous définissez la largeur de l'image à 100%, l'image est plus haute que si vous définissez la largeur sur auto.
coderMe
C'est un peu proche ... mais ne fonctionne que dans Chrome: jsfiddle.net/sheriffderek/xLc2Le0k/270
sheriffderek
2

Ce comportement est attendu. Le conteneur flex étendra tous ses enfants par défaut. L'image n'a pas d'exception. (c'est-à-dire que le parent aura la align-items: stretchpropriété)

Pour conserver les proportions, nous avons deux solutions:

  1. Remplacez la align-items: stretchpropriété par défaut par align-items: flex-startou align-self: centeretc, http://jsfiddle.net/e394Lqnt/3/

ou

  1. Définissez la propriété align exclusivement sur l'enfant lui-même: comme, align-self: center ou align-self: flex-startetc. http://jsfiddle.net/e394Lqnt/2/
Asim KT
la source
-1

En fait, j'ai découvert que le conteneur de la balise d'image doit avoir une hauteur pour conserver le rapport de l'image. par exemple>

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

puis dans votre html votre conteneur emballera l'image de la balise

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

ce travail pour IE et d'autres navigateurs

Kengreg
la source
-1

J'ai juste eu le même problème. Utilisez l'alignement flex pour centrer vos éléments. Vous devez utiliser à la align-items: centerplace de align-content: center. Cela maintiendra le rapport hauteur / largeur, tandis que align-content ne conservera pas le rapport hauteur / largeur.

Clinton Green
la source