Seitenverhältnis von div beibehalten, aber Bildschirmbreite und -höhe in CSS füllen?

122

Ich muss eine Site zusammenstellen, die ein festes Seitenverhältnis von ungefähr 16:9Landschaft hat, wie ein Video.

Ich möchte, dass es zentriert und erweitert wird, um die verfügbare Breite und Höhe zu füllen, aber niemals auf beiden Seiten größer wird.

Beispielsweise:

  1. Bei einer hohen und dünnen Seite würde sich der Inhalt über die gesamte Breite erstrecken, während eine proportionale Höhe beibehalten wird.
  2. Bei einer kurzen, breiten Seite würde sich der Inhalt über die gesamte Höhe mit einer proportionalen Breite erstrecken.

Ich habe mir zwei Methoden angesehen:

  1. Verwenden Sie ein Bild mit dem richtigen Seitenverhältnis, um einen Container zu erweitern div, aber ich konnte nicht erreichen, dass es sich in allen gängigen Browsern gleich verhält.
  2. Das Einstellen einer proportionalen Bodenpolsterung funktioniert jedoch nur relativ zur Breite und ignoriert die Höhe. Es wird mit der Breite immer größer und zeigt vertikale Bildlaufleisten an.

Ich weiß, dass Sie dies mit JS ganz einfach tun können, aber ich hätte gerne eine reine CSS-Lösung.

Irgendwelche Ideen?

Henry Gibson
quelle
sitepoint.com/… Setzen Sie container div aufposition: relative (default) height: 0 padding-<top/bottom>: H/W*100%
Ujjwal Singh

Antworten:

278

Verwenden Sie die neuen CSS-Ansichtsfenstereinheiten vw und vh(Ansichtsfensterbreite / Ansichtsfensterhöhe)

GEIGE

Ändern Sie die Größe vertikal und horizontal und Sie werden sehen, dass das Element immer die maximale Größe des Ansichtsfensters ausfüllt, ohne das Verhältnis zu unterbrechen und ohne Bildlaufleisten!

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

Wenn Sie maximal 90% Breite und Höhe des Ansichtsfensters verwenden möchten : FIDDLE

Auch die Browserunterstützung ist ziemlich gut: IE9 +, FF, Chrome, Safari- caniuse

Danield
quelle
1
Dies ist eine wirklich schöne saubere Lösung. Idealerweise könnte ich die Browserunterstützung etwas weiter hinten nutzen. Ich denke, in einem älteren Browser hätten Sie nur eine minimale Breite / Höhe und würden diese auf diese einstellen - wenn es Bildlaufleisten hätte
Henry Gibson
8
Ähm ... danke, dass Sie mir 2 Arbeitstage und unendlich viele Benutzerfrustrationen erspart haben. 1000+ wenn ich könnte.
Schöning
2
Du hast mir so viel Zeit gespart. Vielen Dank.
Dylan Gattey
1
Leider letzten iOS - Versionen nicht unterstützen , oder eine gebrochene Implementierung von Darstellungseinheiten ( vh, vw, vmin, vmax). Es gibt jedoch eine Problemumgehung , bei der Medienabfragen verwendet werden, um auf iOS-Geräte abzuzielen.
Tim
1
ich liebe dich. tolle Lösung!
Guttemberg
20

Nur die DanieldAntwort in einem WENIGER Mixin neu formulieren , zur weiteren Verwendung:

// 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
quelle
11

Verwenden Sie eine CSS-Medienabfrage @media zusammen mit den CSS-Ansichtsfenstereinheiten vw und vhpassen Sie sie an das Seitenverhältnis des Ansichtsfensters an. Dies hat den Vorteil, dass auch andere Eigenschaften aktualisiert werden font-size.

Diese Geige zeigt das feste Seitenverhältnis für das Div und den Text, der genau zu seiner Skalierung passt.

Die anfängliche Größe basiert auf der vollen Breite:

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

Wenn das Ansichtsfenster breiter als das gewünschte Seitenverhältnis ist, wechseln Sie als Basismaß zur Höhe:

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

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

Dies ist sehr ähnlich in der Ader zu dem max-widthund max-height Ansatz von @Danield, nur flexibler.

Paul
quelle
7

Meine ursprüngliche Frage dazu war, wie man beide ein Element eines festen Aspekts hat, aber das genau in einen bestimmten Container passt, was es ein wenig fummelig macht. Wenn Sie einfach möchten, dass ein einzelnes Element sein Seitenverhältnis beibehält, ist dies viel einfacher.

Die beste Methode, auf die ich gestoßen bin, besteht darin, einem Element die Höhe Null zu geben und dann den prozentualen Polsterungsboden zu verwenden, um ihm die Höhe zu geben. Die prozentuale Polsterung ist immer proportional zur Breite eines Elements und nicht zu seiner Höhe, selbst wenn es oben oder unten gepolstert ist.

W3C-Polsterungseigenschaften

Wenn Sie dies verwenden, können Sie einem Element eine prozentuale Breite geben, um in einem Container zu sitzen, und dann auffüllen, um das Seitenverhältnis oder mit anderen Worten die Beziehung zwischen seiner Breite und Höhe anzugeben.

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

Im obigen Beispiel nimmt das Objekt 80% der Containerbreite ein, und dann beträgt seine Höhe 56,25% dieses Werts. Wenn die Breite 160px wäre, wäre die untere Polsterung und damit die Höhe 90px - ein 16: 9-Aspekt.

Das kleine Problem hier, das für Sie möglicherweise kein Problem darstellt, ist, dass sich in Ihrem neuen Objekt kein natürlicher Raum befindet. Wenn Sie beispielsweise Text einfügen müssen und dieser Text seine eigenen Auffüllwerte annehmen muss, müssen Sie einen Container hinzufügen und die darin enthaltenen Eigenschaften angeben.

Außerdem werden vw- und vh-Einheiten in einigen älteren Browsern nicht unterstützt, sodass die akzeptierte Antwort auf meine Frage für Sie möglicherweise nicht möglich ist und Sie möglicherweise etwas mehr Lo-Fi verwenden müssen.

Henry Gibson
quelle
Um zu vermeiden, dass hier zusätzliches Markup verwendet wird, können Sie ein Element mit :: before oder :: after hinzufügen und diesem Element eine Höhe von 0 und einen Auffüllboden relativ zur Objektbreite und 0breite geben. Dies hat den gleichen Effekt, jedoch mit weniger Markup, und macht den gesamten Objektbereich nutzbar, um Inhalte ohne Container einzufügen.
Henry Gibson
6

Ich verstehe, dass Sie gefragt haben, ob Sie eine CSSbestimmte Lösung wünschen. Um das Seitenverhältnis beizubehalten, müssten Sie die Höhe durch das gewünschte Seitenverhältnis teilen. 16: 9 = 1,777777777778.

Um die richtige Höhe für den Container zu erhalten, müssten Sie die aktuelle Breite durch 1,777777777778 teilen. Da Sie widthden Container nicht mit nur überprüfen CSSoder durch einen Prozentsatz dividieren können CSS, ist dies ohne JavaScript(meines Wissens) nicht möglich .

Ich habe ein funktionierendes Skript geschrieben, das das gewünschte Seitenverhältnis beibehält.

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

Sie können das functionerneut verwenden, wenn Sie mithilfe von etwas anderes mit einem anderen Verhältnis anzeigen möchten

keepAspectRatio(id, width, height);
MCSharp
quelle
Danke für deine Antwort. Dies im Skript zu tun ist relativ einfach, aber ich wollte wirklich eine CSS-basierte Lösung. Ich möchte eine wirklich schnelle, zuverlässige und reaktionsschnelle Lösung, und ich finde, dass Größenänderungsereignisse in verschiedenen Browsern manchmal etwas verzögert sind. Am Ende führen Sie Ihre Transformation ein wenig nach der Größenänderung des Browsers durch, sodass die Benutzererfahrung nicht optimal ist. Willst du etwas, das in dem Moment rendert, in dem der Browser seine Größe ändert, daher reines CSS.
Henry Gibson
1
@ Henry Gibson Das Skript wurde in Chrome, FF, IE, Opera und Safari getestet. Alle arbeiten bissig und sind auch in älteren Browserversionen zuverlässig. Jeder Browser löst seine eigenen Ereignisse aus. window.onresizewird ausgelöst, sobald der Browser seine Größe ändert.
MCSharp
Denken Sie
1

Danields Antwort ist gut, aber sie kann nur verwendet werden, wenn das div das gesamte Ansichtsfenster ausfüllt, oder indem ein bisschen von verwendet wirdcalc verwendet wird, wenn die Breite und Höhe des anderen Inhalts im Ansichtsfenster bekannt ist.

Durch Kombinieren des margin-bottomTricks mit der Methode in der oben genannten Antwort kann das Problem jedoch darauf reduziert werden, nur die Höhe des anderen Inhalts kennen zu müssen. Dies ist nützlich, wenn Sie einen Header mit fester Höhe haben, die Breite der Seitenleiste jedoch nicht bekannt ist.

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>

Hier ist es in einer jsfiddle mit scss , was es offensichtlicher macht, woher die Werte kommen.

Cameron Martin
quelle
0

Basierend auf Daniels Antwort habe ich dieses SASS-Mixin mit Interpolation ( #{}) für den Iframe eines Youtube-Videos geschrieben, der sich in einem modalen Bootstrap-Dialog befindet:

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

Verwendung:

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

Dadurch wird das Video immer ohne die schwarzen Teile angezeigt, die YouTube dem Iframe hinzufügt, wenn die Breite oder Höhe nicht proportional zum Video ist. Anmerkungen:

  • $sides-adjustment ist eine leichte Anpassung, um einen winzigen Teil dieser schwarzen Teile zu kompensieren, die an den Seiten auftauchen;
  • Bei Geräten mit sehr geringer Höhe (<480px) nimmt das Standardmodal viel Platz ein, daher habe ich beschlossen:
    1. verstecke das .modal-footer;
    2. Stellen Sie die Position des ein .modal-dialog .
    3. Reduzieren Sie die Größe der .modal-header.

Nach vielen Tests funktioniert dies jetzt für mich einwandfrei.

CPHPython
quelle
-1

hier eine lösung basierend auf @ danield lösung, die auch innerhalb eines div funktioniert.

In diesem Beispiel verwende ich Angular, aber es kann schnell zu Vanilla JS oder einem anderen Framework wechseln.

Die Hauptidee besteht darin, die @ Danield-Lösung in einen leeren Iframe zu verschieben und die Abmessungen zu kopieren, nachdem sich die Größe des Iframe-Fensters geändert hat.

Dieser Iframe muss den Abmessungen seines Vaterelements entsprechen.

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

Hier ein paar Screenshots:

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Engel Fraga Parodi
quelle