รักษาอัตราส่วนของ div แต่เติมความกว้างและความสูงของหน้าจอใน CSS?


122

ฉันมีไซต์ที่จะรวบรวมซึ่งมีอัตราส่วนภาพคงที่ประมาณ16:9แนวนอนเช่นวิดีโอ

ฉันต้องการให้มันอยู่ตรงกลางและขยายเพื่อเติมเต็มความกว้างที่มีอยู่และความสูงที่มี แต่อย่าให้ใหญ่ขึ้นจากด้านใดด้านหนึ่ง

ตัวอย่างเช่น:

  1. หน้าสูงและบางจะมีเนื้อหายืดเต็มความกว้างในขณะที่รักษาความสูงตามสัดส่วน
  2. หน้ากว้างสั้น ๆ จะมีเนื้อหายืดเต็มความสูงโดยมีความกว้างตามสัดส่วน

มีสองวิธีที่ฉันกำลังดู:

  1. ใช้รูปภาพที่มีอัตราส่วนกว้างยาวเพื่อขยายคอนเทนเนอร์divแต่ฉันไม่สามารถทำให้มันทำงานในลักษณะเดียวกันกับเบราว์เซอร์หลัก ๆ
  2. การตั้งค่าช่องว่างด้านล่างตามสัดส่วน แต่ใช้ได้กับความกว้างเท่านั้นและไม่สนใจความสูง มันจะใหญ่ขึ้นเรื่อย ๆ ตามความกว้างและแสดงแถบเลื่อนแนวตั้ง

ฉันรู้ว่าคุณสามารถทำสิ่งนี้กับ JS ได้ค่อนข้างง่าย แต่ฉันต้องการโซลูชัน CSS ที่บริสุทธิ์

ความคิดใด ๆ ?


sitepoint.com/…ตั้งคอนเทนเนอร์ div เป็นposition: relative (default) height: 0 padding-<top/bottom>: H/W*100%
Ujjwal Singh

คำตอบ:


280

ใช้หน่วยวิวพอร์ต CSS ใหม่vwและvh(ความกว้างของวิวพอร์ต / ความสูงของวิวพอร์ต)

ซอ

ปรับขนาดในแนวตั้งและแนวนอนแล้วคุณจะเห็นว่าองค์ประกอบจะเติมขนาดวิวพอร์ตสูงสุดโดยไม่ทำลายอัตราส่วนและไม่มีแถบเลื่อน!

(เพียว) 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 */
}

หากคุณต้องการใช้ความกว้างและความสูงสูงสุด 90% ของวิวพอร์ต: FIDDLE

นอกจากนี้การรองรับเบราว์เซอร์ก็ค่อนข้างดีเช่นกัน: IE9 +, FF, Chrome, Safari -caniuse


1
นี่เป็นวิธีการแก้ปัญหาที่ดีจริงๆ ฉันสามารถทำได้ด้วยการสนับสนุนเบราว์เซอร์ย้อนกลับไปเล็กน้อย ฉันเดาว่าในเบราว์เซอร์รุ่นเก่าคุณจะต้องมีความกว้าง / ความสูงขั้นต่ำและตั้งค่าเป็นแบบนั้น - ถ้ามันมีแถบเลื่อนให้เป็นเช่นนั้น
Henry Gibson

8
อืม .. ขอบคุณที่ช่วยฉันทำงานได้ 2 วันและทำให้ผู้ใช้ผิดหวังไม่สิ้นสุด 1000+ ถ้าฉันทำได้
Schoening

2
คุณช่วยฉันประหยัดเวลาได้มาก ขอบคุณอย่างล้นหลาม
Dylan Gattey

1
แต่น่าเสียดายที่ iOS เวอร์ชันล่าสุดไม่สนับสนุนหรือมีการดำเนินงานของหน่วยงานเสียวิวพอร์ต ( vh, vw, vmin, vmax) อย่างไรก็ตามมีวิธีแก้ปัญหาชั่วคราวซึ่งใช้แบบสอบถามสื่อเพื่อกำหนดเป้าหมายอุปกรณ์ iOS
ทิม

1
ผมรักคุณ. ทางออกที่ดี!
Guttemberg

20

เพียงแค่ปรับเปลี่ยนDanieldคำตอบในมิกซ์อินน้อยสำหรับการใช้งานเพิ่มเติม:

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

ใช้คิวรีสื่อ CSS @mediaร่วมกับหน่วยวิวพอร์ต CSS vwและvhปรับให้เข้ากับอัตราส่วนกว้างยาวของวิวพอร์ต สิ่งนี้มีประโยชน์ในการอัปเดตคุณสมบัติอื่น ๆ เช่นfont-sizeกัน

ซอนี้แสดงให้เห็นถึงอัตราส่วนคงที่สำหรับ div และข้อความที่ตรงกับขนาดของมันทุกประการ

ขนาดเริ่มต้นขึ้นอยู่กับความกว้างเต็ม:

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

จากนั้นเมื่อวิวพอร์ตกว้างกว่าอัตราส่วนภาพที่ต้องการให้เปลี่ยนเป็นความสูงเป็นการวัดฐาน:

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

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

สิ่งนี้คล้ายกันมากในหลอดเลือดดำmax-widthและmax-height แนวทางของ @Danield มีความยืดหยุ่นมากขึ้น


7

คำถามเดิมของฉันสำหรับเรื่องนี้คือทำอย่างไรให้ทั้งสองมีองค์ประกอบของแง่มุมคงที่ แต่เพื่อให้พอดีกับสิ่งนั้นภายในคอนเทนเนอร์ที่ระบุอย่างแน่นอนซึ่งทำให้ยุ่งเล็กน้อย หากคุณเพียงแค่ต้องการให้องค์ประกอบแต่ละอย่างคงอัตราส่วนไว้มันจะง่ายกว่ามาก

วิธีที่ดีที่สุดที่ฉันเจอคือการให้ความสูงขององค์ประกอบเป็นศูนย์จากนั้นใช้เปอร์เซ็นต์ช่องว่างด้านล่างเพื่อให้ความสูง การเว้นช่องว่างตามเปอร์เซ็นต์จะเป็นสัดส่วนกับความกว้างขององค์ประกอบเสมอไม่ใช่ความสูงแม้ว่าจะมีช่องว่างด้านบนหรือด้านล่างก็ตาม

W3C Padding Properties

ดังนั้นการใช้เพื่อให้องค์ประกอบมีความกว้างเป็นเปอร์เซ็นต์เพื่อนั่งภายในคอนเทนเนอร์จากนั้นจึงเว้นระยะห่างเพื่อระบุอัตราส่วนภาพหรือในอีกแง่หนึ่งคือความสัมพันธ์ระหว่างความกว้างและความสูง

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

ดังนั้นในตัวอย่างด้านบนวัตถุรับ 80% ของความกว้างของคอนเทนเนอร์และความสูงคือ 56.25% ของค่านั้น ถ้าความกว้างคือ 160px ช่องว่างด้านล่างดังนั้นความสูงจะเป็น 90px - ขนาด 16: 9

ปัญหาเล็กน้อยที่นี่ซึ่งอาจไม่ใช่ปัญหาสำหรับคุณคือไม่มีพื้นที่ธรรมชาติภายในวัตถุใหม่ของคุณ ตัวอย่างเช่นหากคุณต้องการใส่ข้อความบางส่วนและข้อความนั้นจำเป็นต้องใช้ค่า padding ของตัวเองคุณต้องเพิ่มคอนเทนเนอร์ภายในและระบุคุณสมบัติในนั้น

นอกจากนี้หน่วย vw และ vh ยังไม่รองรับในเบราว์เซอร์รุ่นเก่าบางรุ่นดังนั้นคำตอบที่เป็นที่ยอมรับสำหรับคำถามของฉันอาจไม่สามารถทำได้สำหรับคุณและคุณอาจต้องใช้ lo-fi อะไรสักอย่างเพิ่มเติม


เพื่อหลีกเลี่ยงการใช้มาร์กอัปพิเศษที่นี่คุณสามารถเพิ่มองค์ประกอบโดยใช้ :: before หรือ :: after และกำหนดให้องค์ประกอบนั้นมีความสูงเป็น 0 และช่องว่างภายในที่สัมพันธ์กับความกว้างของวัตถุและความกว้าง 0 ของตัวมันเอง ซึ่งจะมีผลเหมือนกัน แต่มีมาร์กอัปน้อยกว่าและจะทำให้พื้นที่วัตถุเต็มสามารถใช้เพื่อใส่เนื้อหาได้โดยไม่ต้องใช้คอนเทนเนอร์
Henry Gibson

6

ฉันเข้าใจว่าคุณถามว่าคุณต้องการCSSวิธีแก้ปัญหาเฉพาะ ในการรักษาอัตราส่วนคุณจะต้องหารความสูงด้วยอัตราส่วนภาพที่ต้องการ 16: 9 = 1.777777777778.

เพื่อให้ได้ความสูงที่ถูกต้องสำหรับคอนเทนเนอร์คุณจะต้องหารความกว้างปัจจุบันด้วย 1.777777777778 เนื่องจากคุณไม่สามารถตรวจสอบwidthคอนเทนเนอร์ด้วยเพียงแค่CSSหรือหารด้วยเปอร์เซ็นต์จึงCSSเป็นไปไม่ได้หากไม่มีJavaScript(ความรู้ของฉัน)

ฉันได้เขียนสคริปต์การทำงานที่จะคงอัตราส่วนที่ต้องการไว้

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

คุณสามารถใช้functionอีกครั้งหากคุณต้องการแสดงอย่างอื่นด้วยอัตราส่วนที่แตกต่างกันโดยใช้

keepAspectRatio(id, width, height);

ขอบคุณสำหรับคำตอบ. การทำเช่นนี้ในสคริปต์นั้นค่อนข้างตรงไปตรงมา แต่ฉันต้องการโซลูชันที่ใช้ CSS จริงๆ ฉันต้องการโซลูชันการตอบสนองที่รวดเร็วและเชื่อถือได้และบางครั้งฉันพบว่าการปรับขนาดเหตุการณ์ในเบราว์เซอร์ที่แตกต่างกันนั้นค่อนข้างล่าช้าเล็กน้อย คุณต้องทำการเปลี่ยนแปลงเล็กน้อยหลังจากเกิดการปรับขนาดเบราว์เซอร์ดังนั้นจึงไม่ได้ให้ประสบการณ์ผู้ใช้ที่ดีที่สุด ต้องการสิ่งที่แสดงผลทันทีที่เบราว์เซอร์เปลี่ยนขนาดดังนั้น CSS ที่แท้จริง
Henry Gibson

1
@Henry Gibson สคริปต์ได้รับการทดสอบใน Chrome, FF, IE, Opera และ Safari ทำงานได้เร็วและเชื่อถือได้แม้ในเบราว์เซอร์เวอร์ชันเก่า เบราว์เซอร์ทุกตัวเริ่มการทำงานของเหตุการณ์นั้นเอง window.onresizeจะเริ่มทำงานทันทีที่เบราว์เซอร์เปลี่ยนขนาด
MCSharp

จำไว้ว่าผู้ใช้ไม่ได้ปรับขนาดเบราว์เซอร์เกือบเท่าที่นักพัฒนาทำ
Simon_Weaver

3

ขณะนี้มีคุณสมบัติใหม่ CSS object-fitระบุที่อยู่นี้:

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

การสนับสนุนเบราว์เซอร์ยังค่อนข้างขาด ( http://caniuse.com/#feat=object-fit ) - ปัจจุบันใช้งานได้ในระดับหนึ่งในเบราว์เซอร์ส่วนใหญ่ยกเว้น Microsoft - แต่เมื่อพิจารณาถึงเวลาที่จำเป็นสำหรับคำถามนี้


1

คำตอบของ Danieldนั้นดี แต่จะใช้ได้ก็ต่อเมื่อ div เติมวิวพอร์ตทั้งหมดหรือโดยใช้บิตของcalcสามารถใช้ได้หากทราบความกว้างและความสูงของเนื้อหาอื่นในวิวพอร์ต

อย่างไรก็ตามเมื่อรวมmargin-bottomเคล็ดลับกับวิธีการในคำตอบข้างต้นปัญหาจะลดลงเหลือเพียงแค่ต้องทราบความสูงของเนื้อหาอื่น ๆ สิ่งนี้มีประโยชน์หากคุณมีส่วนหัวที่มีความสูงคงที่ แต่ไม่ทราบความกว้างของแถบด้านข้างเช่น

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>

นี่คือใน jsfiddle โดยใช้ scssซึ่งทำให้ชัดเจนมากขึ้นว่าค่ามาจากไหน


0

จากคำตอบของ Danielฉันเขียน SASS mixin นี้ด้วย interpolation ( #{}) สำหรับ iframe ของวิดีโอ youtube ที่อยู่ในกล่องโต้ตอบโมดอล 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}
  }
}

การใช้งาน:

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

สิ่งนี้จะแสดงวิดีโอโดยไม่มีส่วนสีดำที่ youtube เพิ่มลงใน iframe เสมอเมื่อความกว้างหรือความสูงไม่ได้สัดส่วนกับวิดีโอ หมายเหตุ:

  • $sides-adjustment เป็นการปรับเล็กน้อยเพื่อชดเชยส่วนเล็ก ๆ สีดำเหล่านี้ที่ปรากฏขึ้นที่ด้านข้าง
  • ในอุปกรณ์ที่มีความสูงต่ำมาก (<480px) โมดอลมาตรฐานใช้พื้นที่มากดังนั้นฉันจึงตัดสินใจที่จะ:
    1. ซ่อน.modal-footer;
    2. ปรับตำแหน่งของ.modal-dialog;
    3. ลดขนาดของไฟล์.modal-header.

หลังจากการทดสอบหลายครั้งตอนนี้ก็ใช้งานได้อย่างไม่มีที่ติสำหรับฉัน


-1

นี่คือโซลูชันที่ใช้โซลูชัน @Danield ซึ่งใช้งานได้แม้ใน div

ในตัวอย่างนั้นฉันใช้ Angular แต่สามารถย้ายไปยัง vanilla JS หรือเฟรมเวิร์กอื่นได้อย่างรวดเร็ว

แนวคิดหลักคือเพียงแค่ย้ายโซลูชัน @Danield ภายใน iframe ว่างและคัดลอกขนาดหลังจากเปลี่ยนขนาดหน้าต่าง iframe

iframe นั้นต้องพอดีกับขนาดกับองค์ประกอบของพ่อ

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

นี่คือภาพหน้าจอบางส่วน:

ใส่คำอธิบายภาพที่นี่

ใส่คำอธิบายภาพที่นี่

ใส่คำอธิบายภาพที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.