Zachowaj proporcje obrazu podczas zmiany wysokości

114

Używając modelu CSS flex box, jak zmusić obraz do zachowania proporcji?

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

Zwróć uwagę, że obrazy rozciągają się lub kurczą, aby wypełnić szerokość pojemnika. W porządku, ale czy możemy też rozciągnąć lub zmniejszyć wysokość, aby zachować proporcje obrazu?

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
źródło
1
O ile wiem, najlepszym rozwiązaniem jest użycie <div>z CSSbackground-image: url(...);background-size:contain; background-repeat:no-repeat
Blazemonger
1
Jest tu kilka interesujących wskazówek, ale co, jeśli obrazy nie są już w tym samym stosunku? Dodanie i „dodatkowe” div to ostatnia rzecz, o którą musimy się martwić. Dlaczego nie używać przypadek testowy jak ten Zamiast: jsfiddle.net/sheriffderek/999Lg9qv
sheriffderek

Odpowiedzi:

68

W przypadku tagów img, jeśli zdefiniujesz jedną stronę, rozmiar drugiej strony zostanie zmieniony, aby zachować proporcje i domyślnie obrazy będą rozszerzane do ich oryginalnego rozmiaru.

Korzystając z tego faktu, jeśli zawiniesz każdy znacznik img w znacznik div i ustawisz jego szerokość na 100% nadrzędnego elementu div, wówczas wysokość będzie zgodna z proporcjami zgodnie z oczekiwaniami.

http://jsfiddle.net/0o5tpbxg/

* {
    margin: 0;
    padding: 0;
}
.slider {
    display: flex;
}
.slider .slide img {
    width: 100%;
}
Muhammad Umer
źródło
2
Wiem, że istnieją dwie odpowiedzi, które sugerują użycie elementów div, ale zaznaczam tę odpowiedź jako poprawną, ponieważ wyjaśnia, dlaczego wysokości obrazu są inne niż się spodziewałem. W rzeczywistości jest to normalne i poprawne zachowanie i jest mało prawdopodobne, aby zostało to „naprawione”, ponieważ nie jest to błąd.
coderMe
18
Ale ta odpowiedź nie mówi nic o flexboksie, ponieważ obrazy zachowują się inaczej w kontenerze flexbox. Ustawienie jednej strony nie spowoduje proporcjonalnej zmiany rozmiaru drugiej. Pakowanie ich w kontenerze innym niż flexbox pomaga, ale dodaje niepotrzebne znaczniki, tracąc semantyczność.
Robert Koritnik
3
To nie wygląda jak trzeba div zawijania do tej pracy: jsfiddle.net/xLc2Le0k/120
Rick Strahl
4
Ale co z tym? jsfiddle.net/xLc2Le0k/15 Myślę, że to rozwiązanie to fuks, który działa tylko dlatego, że obrazy mają ten sam stosunek. uwaga dodatkowa: nie ma żadnej „semantyczności” utraconej przez umieszczenie obrazu w div w celu pozycjonowania / szczególnie w przypadku responsywnych obrazów.
szeryfderek,
1
Poważnie najczystsze rozwiązanie i działa z obrazem!
shaneonabike
64

Aby zapobiec rozciągnięciu obrazów w obu osiach wewnątrz elastycznego rodzica, znalazłem kilka rozwiązań.

Możesz spróbować dopasować obiekt do obrazu, który np

object-fit: contain;

http://jsfiddle.net/ykw3sfjd/

Lub możesz dodać reguły specyficzne dla flex, które mogą działać lepiej w niektórych przypadkach.

align-self: center;
flex: 0 0 auto;
Matty Balaam
źródło
4
Dopasowanie obiektów mogłoby być dobrym rozwiązaniem, gdyby nie brakowało wsparcia w IE i Edge nawet do Edge 14
daniels
1
Zapoznaj się z tym zastępczym dopasowaniem do obiektów dla Edge i IE .
Andrei Telteu
Sam używałem tego rozwiązania awaryjnego, działa bardzo dobrze.
Matty Balaam
1
@daniels Edge ma teraz dopasowanie obiektów w fazie rozwoju: developer.microsoft.com/en-us/microsoft-edge/platform/status/…
AMDG
1
object-fit: contain;załatwił mi sprawę, gdy miałem tylko problem w chrome, ff było w porządku.
Benjamin Peter
53

Nie ma potrzeby dodawania zawierającego div.

Domyślną właściwością css „align-items” jest „stretch”, co powoduje rozciąganie obrazów do pełnej pierwotnej wysokości. Ustawienie właściwości css „align-items” na „flex-start” rozwiązuje problem.

.slider {
    display: flex;
    align-items: flex-start;  /* ADD THIS! */
}
Marvin Herbold
źródło
3
Ahh ... dzięki, że robisz wszystko w obsługiwany sposób. Czytałem odpowiedzi aż do tamtego i nie mogłem uwierzyć, że jedyne, co mieliśmy, to brudne hacki ...
Félix Gagnon-Grenier
35

Większość obrazów ma wewnętrzne wymiary , czyli naturalny rozmiar, jak jpegobraz. Jeśli podany rozmiar definiuje jedną z szerokości i wysokości, brakująca wartość jest określana za pomocą wewnętrznego współczynnika ... - patrz MDN .

Ale to nie działa zgodnie z oczekiwaniami, jeśli obrazy, które są ustawiane jako bezpośrednie elementy flex z aktualnym modułem układu elastycznego pudełka na poziomie 1 , o ile wiem.

Zobacz te dyskusje, a raporty o błędach mogą być powiązane:

  • Flexbugs # 14 - Rozmiar wewnętrzny Chrome / Flexbox nie został zaimplementowany poprawnie.
  • Błąd przeglądarki Firefox 972595 - kontenery Flex powinny używać „podstawy elastycznej” zamiast „szerokości” do obliczania wewnętrznych szerokości elementów elastycznych
  • Chromium Issue 249112 - W Flexbox, zezwalaj na wewnętrzne współczynniki kształtu, aby informować o obliczeniach głównego rozmiaru.

Aby obejść ten problem, możesz owinąć każdy <img>znak a <div>lub a <span>, lub tak.

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 Domniemany minimalny rozmiar elementów elastycznych

Aby zapewnić bardziej rozsądny domyślny rozmiar minimalny dla elementów elastycznych , ta specyfikacja wprowadza nową wartość auto jako początkową wartość właściwości min-width i min-height zdefiniowanych w CSS 2.1.


Alternatywnie możesz tablezamiast tego użyć układu CSS, co da podobne wyniki flexbox, ponieważ będzie działać na większej liczbie przeglądarek, nawet dla 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>

Naklejki
źródło
Pierwszy fragment musi być używany .slider > div{min-width:0}w nowych przeglądarkach, które obsługują min-width: auto.
Oriol
3
Specyfikacja flexbox wprowadziła nową autowartość jako domyślną wartość dla min-widthi min-height(poprzednio była 0). Chrome jeszcze go nie implementuje, więc Twój pierwszy fragment kodu działa. Ale nowe przeglądarki potrzebują tego, aby umożliwić kurczenie elementów elastycznych do rozmiaru mniejszego niż domyślna szerokość obrazów.
Oriol
2
+1 to należy zaakceptować odpowiedź, ponieważ mówi o obrazach w modelu flexbox, który jest głównym problemem w tym przypadku ...
Robert Koritnik
Tabela jest świetna, jeśli układ nie zmienia się w różnych punktach przerwania, ale obecnie jest to mało prawdopodobne.
szeryfderek
1
A co, jeśli obrazy nie mają tego samego współczynnika? jsfiddle.net/sheriffderek/5u7y21we
sheriffderek
6

Ostatnio bawiłem się flexboksem i doszedłem do rozwiązania tego problemu poprzez eksperymenty i następujące rozumowanie. Jednak w rzeczywistości nie jestem pewien, czy dokładnie tak się dzieje.

Jeśli system flex ma wpływ na rzeczywistą szerokość. Więc po osiągnięciu maksymalnej szerokości elementu rodzica dodatkowa szerokość ustawiona w css jest ignorowana. Wtedy można bezpiecznie ustawić szerokość na 100%.

Ponieważ wysokość tagu img pochodzi z samego obrazu, ustawienie wysokości na 0% może coś zrobić. (tutaj nie jestem pewien, co ... ale dla mnie miało sens, że powinien to naprawić)

PRÓBNY

(pamiętaj, że zobaczyłem to najpierw tutaj!)

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

Działa jeszcze tylko w chromie

Muhammad Umer
źródło
Ciekawe, ale wydaje się, że to błąd. Zgodnie ze specyfikacjąProcent jest obliczany w odniesieniu do wysokości bloku zawierającego wygenerowane pole. Jeśli wysokość bloku zawierającego nie jest wyraźnie określona (tj. Zależy od wysokości zawartości), a element ten nie jest pozycjonowany absolutnie , wartość jest obliczana naauto „. Tak dzieje się w przeglądarce Firefox.
Oriol
jsfiddle.net/xLc2Le0k/8 wyjaśniają różnicę w ff i chrome. za to
Muhammad Umer
width wydaje się robić w ff, co wysokość robi w chrome.
Muhammad Umer
To ciekawe, ale obawiam się, że rozwiązania oparte tylko na przeglądarce X nie są tak naprawdę rozwiązaniami. Twoje skomentowane skrzypce, tutaj: jsfiddle.net/xLc2Le0k/8, są jednak absolutnie fascynujące w FF. Wydaje się to wskazywać, że FF sprawia, że ​​wysokość obrazu jest „proporcjonalna” do wysokości obrazu, gdyby była naprawdę w 100%. Innymi słowy, img nie „wie” o wyświetlaniu kontenera: flex. To wyjaśnia, dlaczego w FF, jeśli ustawisz szerokość obrazu na 100%, obraz będzie wyższy niż w przypadku ustawienia szerokości na auto.
coderMe
To jest trochę blisko ... ale działa tylko w Chrome: jsfiddle.net/sheriffderek/xLc2Le0k/270
sheriffderek
2

Takie zachowanie jest oczekiwane. flex container domyślnie rozciągnie wszystkie swoje elementy podrzędne. Obraz nie ma wyjątku. (tj. rodzic będzie miał align-items: stretchwłasność)

Aby zachować proporcje, mamy dwa rozwiązania:

  1. Albo zamień align-items: stretchwłaściwość domyślną na align-items: flex-startlub align-self: centeritp., Http://jsfiddle.net/e394Lqnt/3/

lub

  1. Ustaw właściwość align wyłącznie do samego dziecka: jak, align-self: centerlub align-self: flex-startitp http://jsfiddle.net/e394Lqnt/2/
Asim KT
źródło
-1

Właściwie dowiedziałem się, że pojemnik z tagiem obrazu musi mieć wysokość, aby zachować proporcje obrazu. na przykład>

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

wtedy w twoim html twój kontener zawinie obraz tagu

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

to działa dla IE i innych przeglądarek

kengreg
źródło
-1

Po prostu miałem ten sam problem. Użyj wyrównania flex, aby wyśrodkować elementy. Musisz użyć align-items: centerzamiast align-content: center. Pozwoli to zachować proporcje, a align-content nie zachowa proporcji.

Clinton Green
źródło