Duy trì tỷ lệ co của div nhưng lấp đầy chiều rộng và chiều cao của màn hình trong CSS?


122

Tôi có một trang web để kết hợp với nhau có tỷ lệ khung hình cố định xấp xỉ ngang 16:9, như video.

Tôi muốn đặt nó ở giữa và mở rộng để lấp đầy chiều rộng có sẵn và chiều cao có sẵn, nhưng không bao giờ lớn hơn ở hai bên.

Ví dụ:

  1. Một trang cao và mỏng sẽ có nội dung kéo dài toàn bộ chiều rộng trong khi vẫn duy trì chiều cao tương xứng.
  2. Một trang rộng ngắn sẽ có nội dung kéo dài toàn bộ chiều cao, với chiều rộng tương ứng.

Có hai phương pháp tôi đã xem xét:

  1. Sử dụng hình ảnh có tỷ lệ khung hình phù hợp để mở rộng vùng chứa div, nhưng tôi không thể làm cho nó hoạt động theo cùng một cách trên các trình duyệt chính.
  2. Đặt đệm dưới cùng tỷ lệ, nhưng điều đó chỉ hoạt động tương đối với chiều rộng và bỏ qua chiều cao. Nó chỉ tiếp tục lớn hơn với chiều rộng và hiển thị các thanh cuộn dọc.

Tôi biết bạn có thể làm điều này với JS khá dễ dàng, nhưng tôi muốn một giải pháp CSS thuần túy.

Có ý kiến ​​gì không?


sitepoint.com/… Đặt container div thànhposition: relative (default) height: 0 padding-<top/bottom>: H/W*100%
Ujjwal Singh

Câu trả lời:


280

Sử dụng các đơn vị khung nhìn CSS mới vwvh(chiều rộng khung nhìn / chiều cao khung nhìn)

VĨ CẦM

Thay đổi kích thước theo chiều dọc và chiều ngang và bạn sẽ thấy rằng phần tử sẽ luôn lấp đầy kích thước khung nhìn tối đa mà không phá vỡ tỷ lệ và không có thanh cuộn!

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

Nếu bạn muốn sử dụng tối đa 90% chiều rộng và chiều cao của chế độ xem: FIDDLE

Ngoài ra, hỗ trợ trình duyệt cũng khá tốt: IE9 +, FF, Chrome, Safari- caniuse


1
Đây là một giải pháp sạch thực sự tốt đẹp. Lý tưởng nhất là tôi có thể làm với sự hỗ trợ của trình duyệt xa hơn một chút. Tôi đoán trên một trình duyệt cũ hơn, bạn muốn chỉ có tối thiểu chiều rộng / chiều cao và đặt nó vào đó - nếu nó có thanh cuộn như vậy đi
Henry Gibson

8
Uhm .. cảm ơn vì đã tiết kiệm cho tôi 2 ngày làm việc và vô số sự thất vọng của người dùng. Hơn 1000 nếu tôi có thể.
Schoening vào

2
Bạn vừa tiết kiệm cho tôi rất nhiều thời gian. Cảm ơn bạn vô cùng.
Dylan Gattey,

1
Thật không may, iOS gần đây phiên bản không hỗ trợ , hoặc có một thực hiện của các đơn vị bị phá vỡ khung nhìn ( vh, vw, vmin, vmax). Tuy nhiên, có một cách giải quyết là sử dụng các truy vấn phương tiện để nhắm mục tiêu các thiết bị iOS.
Tim

1
Tôi mến bạn. giải pháp tuyệt vời!
Guttemberg

20

Chỉ cần định dạng lại Danieldcâu trả lời của câu trả lời trong LESS mixin, để sử dụng thêm:

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

Sử dụng truy vấn phương tiện CSS @media cùng với các đơn vị chế độ xem CSS vwvhđể thích ứng với tỷ lệ khung hình của chế độ xem. Điều này cũng có lợi cho việc cập nhật các thuộc tính khác font-size.

Fiddle này thể hiện tỷ lệ co cố định cho div và văn bản khớp chính xác với tỷ lệ của nó.

Kích thước ban đầu dựa trên chiều rộng đầy đủ:

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

Sau đó, khi khung nhìn rộng hơn tỷ lệ khung hình mong muốn, hãy chuyển sang chiều cao làm thước đo cơ sở:

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

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

Điều này rất giống với phương phápmax-widthmax-height cách tiếp cận của @Danield, chỉ là linh hoạt hơn.


7

Câu hỏi ban đầu của tôi cho điều này là làm thế nào để vừa có một phần tử của một khía cạnh cố định, nhưng lại vừa khớp chính xác phần tử đó trong một vùng chứa được chỉ định, điều này khiến nó hơi rắc rối. Nếu bạn chỉ muốn một phần tử riêng lẻ duy trì tỷ lệ khung hình của nó thì sẽ dễ dàng hơn rất nhiều.

Phương pháp tốt nhất mà tôi đã thử là đưa ra chiều cao phần tử bằng không và sau đó sử dụng phần trăm padding-bottom để tạo chiều cao cho phần tử. Phần trăm đệm luôn tỷ lệ với chiều rộng của một phần tử chứ không phải chiều cao của nó, ngay cả khi phần đệm trên cùng hay dưới cùng của nó.

Thuộc tính đệm W3C

Vì vậy, bằng cách sử dụng bạn có thể cung cấp cho một phần tử chiều rộng phần trăm để nằm trong một vùng chứa, sau đó đệm để chỉ định tỷ lệ khung hình hoặc nói cách khác, mối quan hệ giữa chiều rộng và chiều cao của nó.

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

Vì vậy, trong ví dụ trên, đối tượng chiếm 80% chiều rộng vùng chứa, và sau đó chiều cao của nó là 56,25% giá trị đó. Nếu chiều rộng của nó là 160px thì phần đệm dưới cùng và do đó chiều cao sẽ là 90px - tỷ lệ 16: 9.

Vấn đề nhỏ ở đây, có thể không phải là vấn đề đối với bạn, là không có không gian tự nhiên bên trong đối tượng mới của bạn. Ví dụ, nếu bạn cần đưa một số văn bản vào và văn bản đó cần nhận các giá trị đệm của riêng nó, bạn cần thêm một vùng chứa bên trong và chỉ định các thuộc tính trong đó.

Ngoài ra, các đơn vị vw và vh không được hỗ trợ trên một số trình duyệt cũ hơn, vì vậy câu trả lời được chấp nhận cho câu hỏi của tôi có thể không thực hiện được với bạn và bạn có thể phải sử dụng thứ gì đó lo-fi hơn.


Để tránh sử dụng thêm đánh dấu ở đây, bạn có thể thêm một phần tử bằng cách sử dụng :: before hoặc :: after và cung cấp cho phần tử đó chiều cao là 0 và padding-bottom so với chiều rộng đối tượng và chiều rộng 0 của chính nó. Điều đó sẽ có tác dụng tương tự nhưng với ít đánh dấu hơn và sẽ làm cho toàn bộ vùng đối tượng có thể sử dụng được để đưa nội dung vào mà không cần vùng chứa.
Henry Gibson

6

Tôi hiểu rằng bạn đã hỏi rằng bạn muốn một CSSgiải pháp cụ thể. Để giữ nguyên tỷ lệ khung hình, bạn cần chia chiều cao cho tỷ lệ khung hình mong muốn. 16: 9 = 1,777777777778.

Để có được chiều cao chính xác cho vùng chứa, bạn cần chia chiều rộng hiện tại cho 1,777777777778. Vì bạn không thể kiểm tra vùng widthchứa bằng chỉ CSShoặc chia cho phần trăm là CSS, nên điều này là không thể nếu không có JavaScript(theo hiểu biết của tôi).

Tôi đã viết một kịch bản làm việc sẽ giữ nguyên tỷ lệ khung hình mong muốn.

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

Bạn có thể sử dụng functionlại nếu bạn muốn hiển thị thứ gì đó khác với tỷ lệ khác bằng cách sử dụng

keepAspectRatio(id, width, height);

Cảm ơn câu trả lời của bạn. Thực hiện điều này trong script tương đối đơn giản, nhưng tôi thực sự muốn có một giải pháp dựa trên CSS. Tôi muốn có một giải pháp đáp ứng đáng tin cậy thực sự linh hoạt và đôi khi tôi thấy các sự kiện thay đổi kích thước trong các trình duyệt khác nhau hơi chậm. Bạn sẽ thực hiện chuyển đổi một chút sau khi thay đổi kích thước trình duyệt xảy ra, vì vậy nó không mang lại trải nghiệm người dùng tốt nhất. Muốn thứ gì đó hiển thị ngay khi trình duyệt thay đổi kích thước, do đó, CSS thuần túy.
Henry Gibson

1
@Henry Gibson Tập lệnh đã được thử nghiệm trong Chrome, FF, IE, Opera và Safari. Tất cả đều hoạt động linh hoạt và đáng tin cậy ngay cả trong các phiên bản trình duyệt cũ hơn. Mỗi trình duyệt kích hoạt các sự kiện của riêng nó. window.onresizeđược kích hoạt tại thời điểm trình duyệt thay đổi kích thước.
MCSharp

nhớ người dùng cuối không thay đổi kích thước trình duyệt gần càng nhiều càng tốt các nhà phát triển làm
Simon_Weaver


1

Câu trả lời của Danield là tốt, nhưng nó chỉ có thể được sử dụng khi div lấp đầy toàn bộ khung nhìn hoặc bằng cách sử dụng một chút calc, có thể được sử dụng nếu chiều rộng và chiều cao của nội dung khác trong khung nhìn được biết.

Tuy nhiên, bằng cách kết hợp margin-bottomthủ thuật với phương pháp trong câu trả lời đã nói ở trên, vấn đề có thể được giảm xuống chỉ cần biết chiều cao của nội dung khác. Điều này hữu ích nếu bạn có tiêu đề chiều cao cố định, nhưng chiều rộng của thanh bên chẳng hạn, không được biết.

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>

Đây là một jsfiddle sử dụng scss , điều này làm cho nó rõ ràng hơn các giá trị đến từ đâu.


0

Dựa trên câu trả lời của Daniel , tôi đã viết kết hợp SASS này với phép nội suy ( #{}) cho iframe của video youtube bên trong hộp thoại phương thức 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}
  }
}

Sử dụng:

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

Điều này sẽ luôn hiển thị video mà không có những phần màu đen mà youtube thêm vào iframe khi chiều rộng hoặc chiều cao không tỷ lệ với video. Ghi chú:

  • $sides-adjustment là một sự điều chỉnh nhỏ để bù đắp một phần nhỏ của những phần màu đen này hiển thị ở hai bên;
  • trong các thiết bị có chiều cao rất thấp (<480px), phương thức tiêu chuẩn chiếm nhiều dung lượng, vì vậy tôi quyết định:
    1. ẩn .modal-footer;
    2. điều chỉnh vị trí của .modal-dialog;
    3. giảm kích thước của .modal-header.

Sau rất nhiều thử nghiệm, điều này đang hoạt động hoàn hảo đối với tôi.


-1

đây là giải pháp dựa trên giải pháp @Danield, hoạt động ngay cả trong một div.

Trong ví dụ đó, tôi đang sử dụng Angular nhưng nó có thể nhanh chóng chuyển sang vanilla JS hoặc một khuôn khổ khác.

Ý tưởng chính chỉ là di chuyển giải pháp @Danield trong iframe trống và sao chép kích thước sau khi kích thước cửa sổ iframe thay đổi.

Iframe đó phải phù hợp với kích thước với phần tử cha của nó.

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

Dưới đây là một vài ảnh chụp màn hình:

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.