web-dev-qa-db-fra.com

Maintenir les proportions de div mais remplir la largeur et la hauteur de l'écran en CSS?

J'ai un site à mettre en place qui a un ratio d'aspect fixe d'environ 16:9 paysage, comme une vidéo. 

Je veux qu'il soit centré et développé pour remplir la largeur disponible et la hauteur disponible, mais jamais pour devenir plus grand de chaque côté. 

Par exemple:

  1. Une page haute et fine aurait le contenu étirant sur toute la largeur tout en maintenant une hauteur proportionnelle.
  2. Une courte page large aurait le contenu qui s'étendait sur toute la hauteur, avec une largeur proportionnelle.

J'ai étudié deux méthodes:

  1. Utilisez une image avec le bon rapport d'aspect pour développer un conteneur div, mais je ne pouvais pas le faire se comporter de la même manière sur les principaux navigateurs. 
  2. Définir un remplissage inférieur proportionnel, mais cela ne fonctionne que par rapport à la largeur et ignore la hauteur. Il ne cesse de grossir avec la largeur et affiche des barres de défilement verticales.

Je sais que vous pouvez le faire facilement avec JS, mais j'aimerais une solution CSS pure.

Des idées?

95
Henry Gibson

Il existe maintenant une nouvelle propriété CSS spécifiée pour adresser ceci: object-fit.

http://docs.webplatform.org/wiki/css/properties/object-fit

La prise en charge des navigateurs fait toujours défaut ( http://caniuse.com/#feat=object-fit ) - fonctionne actuellement dans une certaine mesure dans la plupart des navigateurs, à l'exception de Microsoft - mais, avec le temps, c'est exactement ce qui était requis pour cette question .

1
Henry Gibson

Utilisez les nouvelles unités de fenêtres CSSvw et vh (largeur de la fenêtre/hauteur de la fenêtre)

VIOLON

Redimensionnez verticalement et horizontalement et vous verrez que l'élément remplira toujours la taille maximale de la fenêtre sans casser le rapport et sans barres de défilement!

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

* {
  margin: 0;
  padding: 0;
}
div {
  width: 100vw;
  height: 56.25vw;
  /* 100/56.25 = 1.778 */
  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 */
}
<div></div>

Si vous souhaitez utiliser au maximum, par exemple, 90% de la largeur et de la hauteur de la fenêtre: FIDDLE 

* {
  margin: 0;
  padding: 0;
}
div {
  width: 90vw;
  /* 90% of viewport vidth */
  height: 50.625vw;
  /* ratio = 9/16 * 90 = 50.625 */
  background: pink;
  max-height: 90vh;
  max-width: 160vh;
  /* 16/9 * 90 = 160 */
  margin: auto;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}
<div></div>

En outre, le support du navigateur est également très bon: IE9 +, FF, Chrome, Safari- caniuse

237
Danield

Reformulez simplement la réponse de Danield dans un mixin LESS, pour une utilisation ultérieure:

// 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;
}
16
Christophe Marois

Ma question initiale était de savoir comment intégrer à la fois un élément d'aspect fixe, mais l'adapter exactement à l'intérieur d'un conteneur spécifié, ce qui le rend un peu délicat. Si vous voulez simplement qu'un élément individuel maintienne son format d'image, c'est beaucoup plus facile. 

La meilleure méthode que j'ai rencontrée est de donner à un élément une hauteur nulle et d'utiliser ensuite un pourcentage de remplissage pour le bas. Le pourcentage de remplissage est toujours proportionnel à la largeur d'un élément et non à sa hauteur, même s'il est supérieur ou inférieur. 

Propriétés de remplissage W3C

Vous pouvez donc attribuer à un élément une largeur en pourcentage dans un conteneur, puis un remplissage pour spécifier le rapport de format ou, en d’autres termes, la relation entre la largeur et la hauteur.

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

Ainsi, dans l'exemple ci-dessus, l'objet prend 80% de la largeur du conteneur, puis sa hauteur correspond à 56,25% de cette valeur. Si sa largeur était de 160px, le rembourrage du bas correspondrait donc à une hauteur de 90px - un aspect 16: 9.

Le petit problème ici, qui peut ne pas être un problème pour vous, est qu'il n'y a pas d'espace naturel à l'intérieur de votre nouvel objet. Par exemple, si vous devez insérer du texte et que ce texte doit prendre ses propres valeurs de remplissage, vous devez ajouter un conteneur à l'intérieur et spécifier les propriétés qu'il contient.

De plus, les unités vw et vh ne sont pas prises en charge sur certains navigateurs plus anciens. Par conséquent, la réponse acceptée à ma question pourrait ne pas vous être possible et vous devrez peut-être utiliser quelque chose de plus lo-fi.

7
Henry Gibson

Utilisez un requête de média CSS@media avec les unités de fenêtres de vues CSSvw et vh pour s'adapter au rapport de format de la fenêtre d'affichage. Cela présente l'avantage de mettre à jour d'autres propriétés, telles que font-size.

Ce violon illustre le rapport de format fixe de la div et le texte dont l'échelle correspond exactement.

La taille initiale est basée sur toute la largeur:

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

Ensuite, lorsque la fenêtre est plus large que le format d'image souhaité, passez à la hauteur comme mesure de 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);
  }
}

Cela ressemble beaucoup au max-width et au max-heightapproche de @Danield, mais plus flexible.

6
Paul

Je comprends que vous ayez demandé à souhaiter une solution spécifique à CSS. Pour conserver les proportions, vous devrez diviser la hauteur par les proportions souhaitées. 16: 9 = 1,777777777778.

Pour obtenir la hauteur correcte pour le conteneur, vous devez diviser la largeur actuelle par 1,777777777778. Puisque vous ne pouvez pas vérifier la width du conteneur avec seulement CSS ou diviser par un pourcentage est CSS, ceci n'est pas possible sans JavaScript (à ma connaissance).

J'ai écrit un script de travail qui conservera le format d'image souhaité.

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

Vous pouvez utiliser à nouveau function si vous souhaitez afficher autre chose avec un rapport différent en utilisant 

keepAspectRatio(id, width, height);
5
MCSharp

La réponse de Danield est bonne, mais elle ne peut être utilisée que si la div remplit toute la fenêtre, ou en utilisant un peu de calc, si la largeur et la hauteur de l'autre contenu de la fenêtre sont connues.

Cependant, en combinant l'astuce margin-bottom avec la méthode de la réponse susmentionnée, le problème peut être réduit à la simple nécessité de connaître la hauteur de l'autre contenu. Ceci est utile si vous avez un en-tête de hauteur fixe, mais la largeur de la barre latérale, par exemple, n'est pas connue.

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>

Ici, il est dans un jsfiddle en utilisant scss , ce qui le rend plus évident d'où viennent les valeurs.

1
Cameron Martin

Basé sur La réponse de Daniel , j'ai écrit ce mix SASS avec interpolation (#{}) pour l'iframe d'une vidéo youtube qui se trouve dans une boîte de dialogue 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}
  }
}

Usage:

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

Cela affichera toujours la vidéo sans les parties noires que youtube ajoute à l'iframe lorsque la largeur ou la hauteur n'est pas proportionnelle à la vidéo. Remarques:

  • $sides-adjustment est un léger ajustement pour compenser une infime partie de ces parties noires apparaissant sur les côtés;
  • dans les appareils de très basse hauteur (<480px), le modal standard prend beaucoup de place, alors j'ai décidé de:
    1. cacher le .modal-footer;
    2. ajuster le positionnement du .modal-dialog;
    3. réduire les tailles du .modal-header.

Après de nombreux tests, cela fonctionne parfaitement pour moi maintenant.

0
CPHPython