Pertahankan rasio aspek div tetapi isi lebar dan tinggi layar di CSS?

122

Saya memiliki situs untuk digabungkan yang memiliki rasio aspek tetap kira-kira 16:9lanskap, seperti video.

Saya ingin membuatnya terpusat dan berkembang untuk mengisi lebar yang tersedia, dan tinggi yang tersedia, tetapi tidak pernah tumbuh lebih besar di kedua sisi.

Sebagai contoh:

  1. Halaman yang tinggi dan tipis akan memiliki konten yang membentang selebar penuh dengan tetap menjaga tinggi proporsional.
  2. Halaman lebar yang pendek akan memiliki konten yang membentang dari ketinggian penuh, dengan lebar proporsional.

Ada dua metode yang saya pelajari:

  1. Gunakan gambar dengan rasio aspek yang tepat untuk memperluas penampung div, tetapi saya tidak bisa membuatnya berperilaku dengan cara yang sama di browser utama.
  2. Menetapkan bantalan bawah yang proporsional, tetapi itu hanya berfungsi secara relatif terhadap lebar dan mengabaikan tingginya. Itu terus bertambah besar dengan lebar dan menampilkan bilah gulir vertikal.

Saya tahu Anda dapat melakukan ini dengan JS dengan cukup mudah, tetapi saya ingin solusi CSS murni.

Ada ide?

Henry Gibson
sumber
sitepoint.com/… Setel div kontainer keposition: relative (default) height: 0 padding-<top/bottom>: H/W*100%
Ujjwal Singh

Jawaban:

280

Gunakan unit viewport CSS baru vwdan vh(lebar viewport / tinggi viewport)

BIOLA

Ubah ukuran secara vertikal dan horizontal dan Anda akan melihat bahwa elemen akan selalu memenuhi ukuran viewport maksimum tanpa merusak rasio dan tanpa scrollbar!

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

Jika Anda ingin menggunakan maksimal, katakanlah 90% lebar dan tinggi viewport: FIDDLE

Selain itu, dukungan browser juga cukup bagus: IE9 +, FF, Chrome, Safari- caniuse

Danield
sumber
1
Ini adalah solusi bersih yang sangat bagus. Idealnya saya bisa melakukannya dengan dukungan browser sedikit lebih jauh ke belakang. Saya kira pada browser yang lebih lama Anda hanya akan memiliki lebar / tinggi minimum dan mengaturnya seperti itu - jika memiliki bilah gulir, biarlah
Henry Gibson
8
Uhm .. terima kasih telah menyelamatkan saya 2 hari kerja dan frustrasi pengguna yang tak terbatas. 1000+ jika saya bisa.
Schoening
2
Anda baru saja menghemat banyak waktu saya. Terima kasih banyak.
Dylan Gattey
1
Sayangnya, iOS baru-baru ini versi tidak mendukung , atau memiliki implementasi patah unit viewport ( vh, vw, vmin, vmax). Namun, ada solusi , yang menggunakan kueri media untuk menargetkan perangkat iOS.
Tim
1
Aku cinta kamu. solusi yang bagus!
Guttemberg
20

Hanya merumuskan kembali Danieldjawaban dalam campuran KURANG, untuk penggunaan lebih lanjut:

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

Gunakan kueri media CSS @media bersama dengan unit area pandang CSS vw dan vhuntuk menyesuaikan dengan rasio aspek area pandang. Ini memiliki keuntungan untuk memperbarui properti lain, seperti font-sizejuga.

Biola ini mendemonstrasikan rasio aspek tetap untuk div, dan teks yang sama persis dengan skalanya.

Ukuran awal didasarkan pada lebar penuh:

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

Kemudian, jika area pandang lebih lebar dari rasio aspek yang diinginkan, alihkan ke tinggi sebagai ukuran dasar:

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

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

Ini sangat mirip dengan pendekatanmax-width dan oleh @Danield, hanya saja lebih fleksibel.max-height

Paul
sumber
7

Pertanyaan asli saya untuk ini adalah bagaimana keduanya memiliki elemen dari aspek tetap, tetapi untuk menyesuaikannya dalam wadah yang ditentukan dengan tepat, yang membuatnya sedikit rumit. Jika Anda hanya ingin elemen individu mempertahankan rasio aspeknya, itu jauh lebih mudah.

Metode terbaik yang pernah saya temukan adalah dengan memberikan elemen ketinggian nol dan kemudian menggunakan persentase padding-bottom untuk memberikan ketinggian. Persentase pengisi selalu proporsional dengan lebar elemen, dan bukan tingginya, bahkan jika bantalan atas atau bawahnya.

Properti Padding W3C

Jadi memanfaatkannya Anda dapat memberi elemen persentase lebar untuk ditempatkan di dalam wadah, dan kemudian padding untuk menentukan rasio aspek, atau dalam istilah lain, hubungan antara lebar dan tingginya.

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

Jadi dalam contoh di atas objek mengambil 80% dari lebar wadah, dan kemudian tingginya 56,25% dari nilai itu. Jika lebarnya 160px maka padding bawahnya, dan dengan demikian tingginya akan menjadi 90px - aspek 16: 9.

Masalah kecil di sini, yang mungkin tidak menjadi masalah bagi Anda, adalah tidak ada ruang alami di dalam objek baru Anda. Jika Anda perlu memasukkan beberapa teks misalnya dan teks itu perlu mengambil nilai paddingnya sendiri, Anda perlu menambahkan wadah di dalamnya dan menentukan properti di sana.

Unit vw dan vh juga tidak didukung pada beberapa browser lama, jadi jawaban yang diterima untuk pertanyaan saya mungkin tidak memungkinkan untuk Anda dan Anda mungkin harus menggunakan sesuatu yang lebih lo-fi.

Henry Gibson
sumber
Untuk menghindari penggunaan markup ekstra di sini, Anda dapat menambahkan elemen menggunakan :: before atau :: after dan memberikan elemen itu tinggi 0 dan padding-bottom relatif terhadap lebar objek dan 0 lebarnya sendiri. Itu akan memiliki efek yang sama tetapi dengan lebih sedikit markup, dan akan membuat area objek penuh dapat digunakan untuk memasukkan konten tanpa membutuhkan wadah.
Henry Gibson
6

Saya memahami bahwa Anda meminta agar Anda menginginkan CSSsolusi tertentu. Untuk mempertahankan rasio aspek, Anda harus membagi tinggi dengan rasio aspek yang diinginkan. 16: 9 = 1,777777777778.

Untuk mendapatkan tinggi yang benar untuk wadah, Anda perlu membagi lebar saat ini dengan 1,777777777778. Karena Anda tidak dapat memeriksa widthwadah dengan hanya CSSatau membagi dengan persentase CSS, ini tidak mungkin tanpa JavaScript(sepengetahuan saya).

Saya telah menulis skrip kerja yang akan menjaga rasio aspek yang diinginkan.

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

Anda dapat menggunakan functionlagi jika Anda ingin menampilkan sesuatu yang lain dengan rasio yang berbeda dengan menggunakan

keepAspectRatio(id, width, height);
MCSharp
sumber
Terima kasih atas jawaban anda. Melakukan ini dalam skrip relatif mudah, tetapi saya sangat menginginkan solusi berbasis CSS. Saya menginginkan solusi responsif yang benar-benar andal dan cepat, dan saya menemukan terkadang acara pengubahan ukuran di browser yang berbeda agak lamban. Anda akhirnya melakukan transformasi sedikit setelah pengubahan ukuran browser terjadi, sehingga tidak memberikan pengalaman pengguna terbaik. Ingin sesuatu yang membuat saat browser mengubah ukuran, maka CSS murni.
Henry Gibson
1
@Henry Gibson Script telah diuji di Chrome, FF, IE, Opera, dan Safari. Semua bekerja dengan cepat dan dapat diandalkan bahkan di browser versi lama. Setiap browser mengaktifkan kejadiannya sendiri. window.onresizediaktifkan saat browser mengubah ukuran.
MCSharp
ingat pengguna akhir tidak mengubah ukuran browser sebanyak yang dilakukan pengembang
Simon_Weaver
1

Jawaban Danield bagus, tapi hanya bisa digunakan saat div memenuhi seluruh viewport, atau dengan menggunakan sedikit calc, bisa digunakan jika lebar dan tinggi konten lain di viewport diketahui.

Namun, dengan menggabungkan margin-bottomtrik dengan metode pada jawaban di atas, masalahnya bisa direduksi menjadi hanya mengetahui ketinggian konten lainnya. Ini berguna jika Anda memiliki header dengan tinggi tetap, tetapi lebar sidebar, misalnya, tidak diketahui.

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>

Ini dia di jsfiddle menggunakan scss , yang membuatnya lebih jelas dari mana asalnya.

Cameron Martin
sumber
0

Berdasarkan jawaban Daniel , saya menulis mixin SASS ini dengan interpolasi ( #{}) untuk iframe video youtube yang ada di dalam dialog modal 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}
  }
}

Pemakaian:

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

Ini akan selalu menampilkan video tanpa bagian hitam yang ditambahkan youtube ke iframe ketika lebar atau tingginya tidak proporsional dengan video. Catatan:

  • $sides-adjustment adalah sedikit penyesuaian untuk mengimbangi sebagian kecil dari bagian hitam ini yang muncul di samping;
  • pada perangkat dengan tinggi sangat rendah (<480px), modal standar memakan banyak ruang, jadi saya memutuskan untuk:
    1. sembunyikan .modal-footer;
    2. sesuaikan posisi .modal-dialog;
    3. kurangi ukuran file .modal-header.

Setelah banyak pengujian, ini bekerja dengan sempurna untuk saya sekarang.

CPHPython
sumber
-1

di sini solusi berdasarkan solusi @Danield, yang berfungsi bahkan dalam div.

Dalam contoh itu, saya menggunakan Angular tetapi dapat dengan cepat berpindah ke vanilla JS atau kerangka kerja lain.

Ide utamanya hanyalah memindahkan solusi @Danield dalam iframe kosong dan menyalin dimensi setelah ukuran jendela iframe berubah.

Iframe itu harus menyesuaikan dimensi dengan elemen ayahnya.

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

Berikut beberapa screenshotnya:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Angel Fraga Parodi
sumber