Zachowaj proporcje div, ale wypełnij szerokość i wysokość ekranu w CSS?

122

Mam witrynę do złożenia, która ma stały współczynnik proporcji, w przybliżeniu 16:9poziomy, jak wideo.

Chcę, aby był wyśrodkowany i rozszerzony, aby wypełnić dostępną szerokość i dostępną wysokość, ale nigdy nie powiększał się po obu stronach.

Na przykład:

  1. Wysoka i cienka strona miałaby treść rozciągającą się na całą szerokość, zachowując proporcjonalną wysokość.
  2. Krótka, szeroka strona miałaby treść rozciągającą się na całą wysokość, z proporcjonalną szerokością.

Patrzyłem na dwie metody:

  1. Użyj obrazu o odpowiednim współczynniku proporcji, aby rozwinąć kontener div, ale nie mogłem sprawić, by zachowywał się tak samo w głównych przeglądarkach.
  2. Ustawienie proporcjonalnego wypełnienia dolnego, ale działa tylko w odniesieniu do szerokości i ignoruje wysokość. Po prostu powiększa się wraz z szerokością i wyświetla pionowe paski przewijania.

Wiem, że można by to łatwo zrobić za pomocą JS, ale chciałbym mieć czyste rozwiązanie CSS.

Jakieś pomysły?

Henry Gibson
źródło
sitepoint.com/… Ustaw kontener div naposition: relative (default) height: 0 padding-<top/bottom>: H/W*100%
Ujjwal Singh

Odpowiedzi:

280

Użyj nowych jednostek rzutni CSS vw i vh(szerokość rzutni / wysokość rzutni)

SKRZYPCE

Zmień rozmiar w pionie i poziomie, a zobaczysz, że element zawsze wypełni maksymalny rozmiar widocznego obszaru bez zrywania proporcji i bez pasków przewijania!

(PURE) CSS

div
{
    width: 100vw; 
    height: 56.25vw; /* height:width ratio = 9/16 = .5625  */
    background: pink;
    max-height: 100vh;
    max-width: 177.78vh; /* 16/9 = 1.778 */
    margin: auto;
    position: absolute;
    top:0;bottom:0; /* vertical center */
    left:0;right:0; /* horizontal center */
}

Jeśli chcesz użyć maksimum, powiedzmy, 90% szerokości i wysokości widoku: FIDDLE

Również obsługa przeglądarek jest całkiem dobra: IE9 +, FF, Chrome, Safari- caniuse

Danield
źródło
1
To naprawdę fajne, czyste rozwiązanie. Jednak najlepiej byłoby, gdyby obsługa przeglądarki znajdowała się nieco dalej. Wydaje mi się, że w starszej przeglądarce miałbyś po prostu minimalną szerokość / wysokość i ustawić ją na taką - gdyby miała paski przewijania, niech tak będzie
Henry Gibson,
8
Uhm ... dzięki za zaoszczędzenie 2 dni pracy i nieskończonej ilości frustracji użytkowników. 1000+, gdybym mógł.
Schoening
2
Właśnie zaoszczędziłeś mi tyle czasu. Dziękuję bardzo.
Dylan Gattey,
1
Niestety, najnowsze wersje iOS nie obsługuje lub ma uszkodzony realizację jednostek rzutni ( vh, vw, vmin, vmax). Istnieje jednak obejście , które używa zapytań o media do kierowania na urządzenia z iOS.
Tim
1
kocham Cię. świetne rozwiązanie!
Guttemberg,
20

Wystarczy przeformułować Danieldodpowiedź w MNIEJSZEJ mieszance, do dalszego wykorzystania:

// Mixin for ratio dimensions    
.viewportRatio(@x, @y) {
  width: 100vw;
  height: @y * 100vw / @x;
  max-width: @x / @y * 100vh;
  max-height: 100vh;
}

div {

  // Force a ratio of 5:1 for all <div>
  .viewportRatio(5, 1);

  background-color: blue;
  margin: 0;
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
}
Christophe Marois
źródło
11

Użyj zapytania o media CSS @media razem z jednostkami rzutni CSS vw i vhdostosuj do współczynnika proporcji widoku. Ma to tę zaletę, że aktualizuje również inne właściwości font-size.

To skrzypce pokazuje stały współczynnik proporcji elementu div oraz tekst dokładnie pasujący do jego skali.

Rozmiar początkowy dotyczy pełnej szerokości:

div {
  width: 100vw; 
  height: calc(100vw * 9 / 16);

  font-size: 10vw;

  /* align center */
  margin: auto;
  position: absolute;
  top: 0px; right: 0px; bottom: 0px; left: 0px;

  /* visual indicators */
  background:
    url(data:image/gif;base64,R0lGODlhAgAUAIABAB1ziv///yH5BAEAAAEALAAAAAACABQAAAIHhI+pa+EPCwA7) repeat-y center top,
    url(data:image/gif;base64,R0lGODlhFAACAIABAIodZP///yH5BAEAAAEALAAAAAAUAAIAAAIIhI8Zu+nIVgEAOw==) repeat-x left center,
    silver;
}

Następnie, gdy rzutnia jest szersza niż wymagany współczynnik proporcji, przełącz na wysokość jako miarę podstawową:

/* 100 * 16/9 = 177.778 */
@media (min-width: 177.778vh) {
  div {
    height: 100vh;
    width: calc(100vh * 16 / 9);

    font-size: calc(10vh * 16 / 9);
  }
}

Jest to bardzo podobne w duchu max-widthi max-height podejście @Danield, tylko bardziej elastyczne.

Paweł
źródło
7

Moje pierwotne pytanie dotyczyło tego, jak oba mają element o stałym aspekcie, ale aby dokładnie dopasować go do określonego kontenera, co sprawia, że ​​jest trochę kłopotliwy. Jeśli po prostu chcesz, aby pojedynczy element zachował swoje proporcje, jest to o wiele łatwiejsze.

Najlepszą metodą, z jaką się spotkałem, jest podanie zerowej wysokości elementu, a następnie użycie procentowego wypełnienia dolnego, aby nadać mu wysokość. Wypełnienie procentowe jest zawsze proporcjonalne do szerokości elementu, a nie do jego wysokości, nawet jeśli jest to dopełnienie górne lub dolne.

Właściwości wypełnienia W3C

Używając więc, możesz nadać elementowi procentową szerokość, aby znalazł się w kontenerze, a następnie dopełnić, aby określić współczynnik proporcji lub innymi słowy, związek między jego szerokością a wysokością.

.object {
    width: 80%; /* whatever width here, can be fixed no of pixels etc. */
    height: 0px;
    padding-bottom: 56.25%;
}
.object .content {
    position: absolute;
    top: 0px;
    left: 0px;
    height: 100%;
    width: 100%;
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    padding: 40px;
}

Zatem w powyższym przykładzie obiekt zajmuje 80% szerokości kontenera, a następnie jego wysokość to 56,25% tej wartości. Jeśli szerokość wynosiłaby 160 pikseli, to dolne wypełnienie, a zatem wysokość wyniosłaby 90 pikseli - aspekt 16: 9.

Drobny problem, który może nie stanowić dla ciebie problemu, polega na tym, że w twoim nowym obiekcie nie ma naturalnej przestrzeni. Jeśli chcesz na przykład umieścić jakiś tekst, a tekst musi mieć własne wartości dopełnienia, musisz dodać kontener do środka i określić tam właściwości.

Również jednostki vw i vh nie są obsługiwane w niektórych starszych przeglądarkach, więc zaakceptowana odpowiedź na moje pytanie może nie być dla Ciebie możliwa i być może będziesz musiał użyć czegoś więcej lo-fi.

Henry Gibson
źródło
Aby uniknąć stosowania dodatkowych znaczników w tym miejscu, możesz dodać element za pomocą :: before lub :: after i nadać temu elementowi wysokość 0 i dopełnienie-dolne względem szerokości obiektu i jego szerokości. Będzie to miało ten sam efekt, ale z mniejszą liczbą znaczników, i sprawi, że cały obszar obiektu będzie można wykorzystać do umieszczania treści bez konieczności używania kontenera.
Henry Gibson,
6

Rozumiem, że prosiłeś o CSSkonkretne rozwiązanie. Aby zachować proporcje, musisz podzielić wysokość przez żądany współczynnik proporcji. 16: 9 = 1,777777777778.

Aby uzyskać prawidłową wysokość kontenera, należałoby podzielić bieżącą szerokość przez 1,777777777778. Ponieważ nie możesz sprawdzić widthkontenera za pomocą samego CSSlub podzielenia przez procent CSS, nie jest to możliwe bezJavaScript (o mi wiadomo).

Napisałem działający skrypt, który zachowa pożądane proporcje.

HTML

<div id="aspectRatio"></div>

CSS

body { width: 100%; height: 100%; padding: 0; margin: 0; }
#aspectRatio { background: #ff6a00; }

JavaScript

window.onload = function () {
    //Let's create a function that will scale an element with the desired ratio
    //Specify the element id, desired width, and height
    function keepAspectRatio(id, width, height) {
        var aspectRatioDiv = document.getElementById(id);
        aspectRatioDiv.style.width = window.innerWidth;
        aspectRatioDiv.style.height = (window.innerWidth / (width / height)) + "px";
    }

    //run the function when the window loads
    keepAspectRatio("aspectRatio", 16, 9);

    //run the function every time the window is resized
    window.onresize = function (event) {
        keepAspectRatio("aspectRatio", 16, 9);
    }
}

Możesz użyć functionponownie, jeśli chcesz wyświetlić coś innego z innym współczynnikiem za pomocą

keepAspectRatio(id, width, height);
MCSharp
źródło
Dziękuję za odpowiedź. Robienie tego w skrypcie jest stosunkowo proste, ale naprawdę chciałem rozwiązania opartego na CSS. Chcę naprawdę zgrabnego, niezawodnego, responsywnego rozwiązania, a czasami wydarzenia związane ze zmianą rozmiaru w różnych przeglądarkach są nieco opóźnione. W końcu dokonujesz transformacji trochę po zmianie rozmiaru przeglądarki, więc nie zapewnia to najlepszych wrażeń użytkownika. Chcesz czegoś, co odwzorowuje moment zmiany rozmiaru przeglądarki, stąd czysty CSS.
Henry Gibson,
1
@Henry Gibson Skrypt został przetestowany w Chrome, FF, IE, Opera i Safari. Wszystkie działają szybko i są niezawodne nawet w starszych wersjach przeglądarek. Każda przeglądarka uruchamia własne zdarzenia. window.onresizejest uruchamiany w momencie zmiany rozmiaru przeglądarki.
MCSharp
pamiętaj, że użytkownicy końcowi nie
zmieniają
1

Odpowiedź Danielda jest dobra, ale można jej użyć tylko wtedy, gdy element div wypełnia cały widoczny obszar, lub używając trochę calc, można użyć, jeśli znana jest szerokość i wysokość innych treści w widocznym obszarze.

Jednak łącząc margin-bottomtrik z metodą opisaną w powyższej odpowiedzi, problem można sprowadzić do konieczności znajomości wysokości innych treści. Jest to przydatne, jeśli masz nagłówek o stałej wysokości, ale na przykład szerokość paska bocznego nie jest znana.

body {
  margin: 0;
  margin-top: 100px; /* simulating a header */
}

main {
  margin: 0 auto;
  max-width: calc(200vh - 200px);
}

section {
  padding-bottom: 50%;
  position: relative;
}

div {
  position:absolute;
  background-color: red;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}
<main>
  <section>
    <div></div>
  </section>
</main>

Tutaj jest w jsfiddle przy użyciu scss , co sprawia, że ​​jest bardziej oczywiste, skąd pochodzą wartości.

Cameron Martin
źródło
0

Opierając się na odpowiedzi Daniela , napisałem ten miks SASS z interpolacją ( #{}) dla elementu iframe wideo z YouTube, który znajduje się w modalnym oknie dialogowym Bootstrap:

@mixin responsive-modal-wiframe($height: 9, $width: 16.5,
                                $max-w-allowed: 90, $max-h-allowed: 60) {
  $sides-adjustment: 1.7;

  iframe {
    width: #{$width / $height * ($max-h-allowed - $sides-adjustment)}vh;
    height: #{$height / $width * $max-w-allowed}vw;
    max-width: #{$max-w-allowed - $sides-adjustment}vw;
    max-height: #{$max-h-allowed}vh;
  }

  @media only screen and (max-height: 480px) {
    margin-top: 5px;
    .modal-header {
      padding: 5px;
      .modal-title {font-size: 16px}
    }
    .modal-footer {display: none}
  }
}

Stosowanie:

.modal-dialog {
  width: 95vw; // the modal should occupy most of the screen's available space
  text-align: center; // this will center the titles and the iframe

  @include responsive-modal-wiframe();
}

HTML:

<div class="modal">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
        <h4 class="modal-title">Video</h4>
      </div>
      <div class="modal-body">
        <iframe src="https://www.youtube-nocookie.com/embed/<?= $video_id ?>?rel=0" frameborder="0"
          allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
          allowfullscreen></iframe>
      </div>
      <div class="modal-footer">
        <button class="btn btn-danger waves-effect waves-light"
          data-dismiss="modal" type="button">Close</button>
      </div>
    </div>
  </div>
</div>

Spowoduje to wyświetlenie wideo bez tych czarnych części, które YouTube dodaje do elementu iframe, gdy szerokość lub wysokość nie jest proporcjonalna do wideo. Uwagi:

  • $sides-adjustment to niewielka regulacja, aby skompensować niewielką część tych czarnych części widocznych po bokach;
  • w urządzeniach o bardzo małej wysokości (<480px) standardowy modal zajmuje dużo miejsca, dlatego postanowiłem:
    1. ukryj .modal-footer;
    2. wyregulować położenie .modal-dialog ;
    3. zmniejszyć rozmiary pliku .modal-header.

Po wielu testach teraz działa to bezbłędnie.

CPHPython
źródło
-1

tutaj rozwiązanie oparte na rozwiązaniu @Danield, które działa nawet w obrębie div.

W tym przykładzie używam Angulara, ale mogę szybko przejść do vanilla JS lub innego frameworka.

Głównym pomysłem jest po prostu przeniesienie rozwiązania @Danield w pustej ramce iframe i skopiowanie wymiarów po zmianie rozmiaru okna iframe.

Ten element iframe musi pasować do wymiarów z elementem macierzystym.

https://stackblitz.com/edit/angular-aspect-ratio-by-iframe?file=src%2Findex.html

Oto kilka zrzutów ekranu:

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

Angel Fraga Parodi
źródło