Mantieni le proporzioni del div ma riempi la larghezza e l'altezza dello schermo in CSS?


122

Devo mettere insieme un sito che ha un rapporto di aspetto fisso di circa 16:9 orizzontali, come un video.

Voglio che sia centrato ed espanso per riempire la larghezza disponibile e l'altezza disponibile, ma non ingrandirsi mai su entrambi i lati.

Per esempio:

  1. Una pagina alta e sottile avrebbe il contenuto che si estende su tutta la larghezza mantenendo un'altezza proporzionale.
  2. Una pagina corta e larga avrebbe il contenuto che si estende per tutta l'altezza, con una larghezza proporzionale.

Ci sono due metodi che ho esaminato:

  1. Usa un'immagine con le proporzioni giuste per espandere un contenitore div, ma non sono riuscito a farlo comportare allo stesso modo sui principali browser.
  2. L'impostazione di un'imbottitura inferiore proporzionale, ma che funziona solo relativamente alla larghezza e ignora l'altezza. Continua a ingrandirsi con la larghezza e visualizza le barre di scorrimento verticali.

So che potresti farlo con JS abbastanza facilmente, ma mi piacerebbe una soluzione CSS pura.

Qualche idea?


sitepoint.com/… Imposta div contenitore suposition: relative (default) height: 0 padding-<top/bottom>: H/W*100%
Ujjwal Singh

Risposte:


280

Utilizza le nuove unità di visualizzazione CSS vw e vh(larghezza del riquadro di visualizzazione / altezza del riquadro di visualizzazione)

VIOLINO

Ridimensiona verticalmente e orizzontalmente e vedrai che l'elemento riempirà sempre la dimensione massima del viewport senza rompere il rapporto e senza barre di scorrimento!

CSS (PURO)

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 */
}

Se vuoi usare un massimo di diciamo 90% di larghezza e altezza del viewport: FIDDLE

Inoltre, anche il supporto del browser è abbastanza buono: IE9 +, FF, Chrome, Safari- caniuse


1
Questa è davvero una bella soluzione pulita. Tuttavia, idealmente potrei fare con il supporto del browser un po 'più indietro. Immagino che su un browser più vecchio avresti solo una larghezza / altezza minima e impostarla su quella - se avesse le barre di scorrimento, così sia
Henry Gibson

8
Uhm .. grazie per avermi risparmiato 2 giorni di lavoro e una quantità infinita di frustrazioni degli utenti. 1000+ se potessi.
Schoening

2
Mi hai appena risparmiato così tanto tempo. Grazie immensamente.
Dylan Gattey

1
Purtroppo, le versioni più recenti di iOS non supportano , o hanno un'implementazione rotto di unità viewport ( vh, vw, vmin, vmax). Tuttavia, esiste una soluzione alternativa , che utilizza le media query per indirizzare i dispositivi iOS.
Tim

1
ti amo. ottima soluzione!
Guttemberg

20

Basta riformulare Danieldla risposta di in un mixin MENO, per un ulteriore utilizzo:

// 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;
}

11

Usa una media query CSS @media insieme alle unità viewport CSS vw e vhadattalo alle proporzioni del viewport. Questo ha il vantaggio di aggiornare anche altre proprietà, come font-size.

Questo violino mostra le proporzioni fisse per il div e il testo che corrisponde esattamente alla sua scala.

La dimensione iniziale si basa sulla larghezza intera:

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;
}

Quindi, quando la visualizzazione è più ampia delle proporzioni desiderate, passa all'altezza come misura di base:

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

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

Questo è molto simile in vena all'approcciomax-width e di @Danield, solo più flessibile.max-height


7

La mia domanda originale per questo era come avere entrambi un elemento di un aspetto fisso, ma adattarlo esattamente a un contenitore specificato, il che lo rende un po 'complicato. Se vuoi semplicemente che un singolo elemento mantenga le sue proporzioni, è molto più semplice.

Il metodo migliore in cui mi sono imbattuto è dare a un elemento un'altezza zero e quindi utilizzare la percentuale di riempimento inferiore per dargli altezza. Il riempimento percentuale è sempre proporzionale alla larghezza di un elemento e non alla sua altezza, anche se il riempimento superiore o inferiore.

Proprietà di riempimento W3C

Quindi utilizzandolo puoi dare a un elemento una larghezza percentuale per sedersi all'interno di un contenitore, quindi riempire per specificare le proporzioni, o in altri termini, la relazione tra la sua larghezza e altezza.

.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;
}

Quindi nell'esempio sopra l'oggetto prende l'80% della larghezza del contenitore e quindi la sua altezza è il 56,25% di quel valore. Se la larghezza fosse 160 px, l'imbottitura inferiore, quindi l'altezza sarebbe 90 px, con un aspetto 16: 9.

Il piccolo problema qui, che potrebbe non essere un problema per te, è che non c'è spazio naturale all'interno del tuo nuovo oggetto. Se, ad esempio, è necessario inserire del testo e quel testo deve assumere i propri valori di riempimento, è necessario aggiungere un contenitore all'interno e specificare le proprietà al suo interno.

Inoltre, le unità vw e vh non sono supportate su alcuni browser meno recenti, quindi la risposta accettata alla mia domanda potrebbe non essere possibile per te e potresti dover usare qualcosa di più lo-fi.


Per evitare di usare markup extra qui puoi aggiungere un elemento usando :: before o :: after e dare a quell'elemento un'altezza di 0 e un padding-bottom rispetto alla larghezza degli oggetti e una larghezza pari a 0. Ciò avrà lo stesso effetto ma con meno markup e renderà l'intera area dell'oggetto utilizzabile per inserire il contenuto senza bisogno di un contenitore.
Henry Gibson

6

Mi risulta che hai chiesto di volere una CSSsoluzione specifica. Per mantenere le proporzioni, è necessario dividere l'altezza per le proporzioni desiderate. 16: 9 = 1.777777777778.

Per ottenere l'altezza corretta per il contenitore, è necessario dividere la larghezza corrente per 1.777777777778. Dal momento che non puoi controllare il widthcontenitore con solo CSSo dividere per una percentuale è CSS, questo non è possibile senza JavaScript(per quanto ne so ).

Ho scritto uno script funzionante che manterrà le proporzioni desiderate.

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);
    }
}

Puoi usare di functionnuovo se desideri visualizzare qualcos'altro con un rapporto diverso usando

keepAspectRatio(id, width, height);

Grazie per la tua risposta. Farlo nello script è relativamente semplice, ma volevo davvero una soluzione basata su CSS. Voglio una soluzione reattiva affidabile e scattante e trovo che a volte gli eventi di ridimensionamento in browser diversi siano un po 'lenti. Finisci per fare la tua trasformazione un po 'dopo il ridimensionamento del browser, quindi non offre la migliore esperienza utente. Vuoi qualcosa che renda il momento in cui il browser cambia dimensione, quindi CSS puro.
Henry Gibson

1
@Henry Gibson Lo script è stato testato in Chrome, FF, IE, Opera e Safari. Tutti funzionano in modo scattante e sono affidabili anche nelle versioni precedenti dei browser. Ogni browser attiva i propri eventi. window.onresizeviene attivato nel momento in cui il browser cambia dimensione.
MCSharp

ricorda che gli utenti finali non ridimensionano i browser tanto quanto fanno gli sviluppatori
Simon_Weaver


1

La risposta di Danield è buona, ma può essere usata solo quando il div riempie l'intero viewport, oppure usando un po 'di calc, può essere usata se la larghezza e l'altezza degli altri contenuti nella viewport è nota.

Tuttavia, combinando il margin-bottomtrucco con il metodo nella risposta di cui sopra, il problema può essere ridotto al solo fatto di dover conoscere l'altezza dell'altro contenuto. Ciò è utile se hai un'intestazione ad altezza fissa, ma la larghezza della barra laterale, ad esempio, non è nota.

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>

Eccolo in un jsfiddle usando scss , il che rende più ovvio da dove provengono i valori.


0

Sulla base della risposta di Daniel , ho scritto questo mixin SASS con interpolation ( #{}) per l'iframe di un video di YouTube che si trova all'interno di una finestra di dialogo modale 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}
  }
}

Uso:

.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>

Questo mostrerà sempre il video senza quelle parti nere che youtube aggiunge all'iframe quando la larghezza o l'altezza non sono proporzionali al video. Appunti:

  • $sides-adjustment è un leggero aggiustamento per compensare una minuscola parte di queste parti nere che si vedono sui lati;
  • in dispositivi di altezza molto bassa (<480px) il modale standard occupa molto spazio, quindi ho deciso di:
    1. nascondere il .modal-footer;
    2. regolare il posizionamento del .modal-dialog;
    3. ridurre le dimensioni del file .modal-header.

Dopo molti test, ora funziona perfettamente.


-1

qui una soluzione basata sulla soluzione @Danield, che funziona anche all'interno di un div.

In questo esempio, sto usando Angular ma può passare rapidamente a vanilla JS o un altro framework.

L'idea principale è solo quella di spostare la soluzione @Danield all'interno di un iframe vuoto e copiare le dimensioni dopo che le dimensioni della finestra dell'iframe cambiano.

Quell'iframe deve adattarsi alle dimensioni con il suo elemento padre.

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

Ecco alcuni screenshot:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.