【问题标题】:Maintain aspect ratio of div but fill screen width and height in CSS?保持div的纵横比但在CSS中填充屏幕宽度和高度?
【发布时间】:2014-01-02 14:57:07
【问题描述】:

我有一个固定纵横比约为16:9 横向的网站,例如视频。

我想让它居中并扩展以填充可用宽度和可用高度,但永远不要在任一侧变大。

例如:

  1. 又高又薄的页面会使内容拉伸整个宽度,同时保持成比例的高度。
  2. 短而宽的页面的内容会以成比例的宽度延伸到整个高度。

我一直在研究两种方法:

  1. 使用具有正确纵横比的图像来扩展容器div,但我无法让它在主要浏览器中以相同的方式运行。
  2. 设置比例底部填充,但这仅适用于相对于宽度并忽略高度。它只是随着宽度不断变大并显示垂直滚动条。

我知道你可以很容易地用 JS 做到这一点,但我想要一个纯 CSS 解决方案。

有什么想法吗?

【问题讨论】:

标签: html css responsive-design aspect-ratio


【解决方案1】:

我了解到,您曾询问过您想要 CSS 特定的解决方案。要保持纵横比,您需要将高度除以所需的纵横比。 16:9 = 1.777777777778。

要获得容器的正确高度,您需要将当前宽度除以 1.777777777778。由于您无法仅使用CSS 检查容器的width 或除以百分比为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 该脚本已经在 Chrome、FF、IE、Opera 和 Safari 中进行了测试。即使在旧版本的浏览器中,所有工作都可以快速运行并且可靠。每个浏览器都会触发它自己的事件。 window.onresize 在浏览器改变大小时被触发。
  • 记住最终用户不会像开发人员那样频繁地调整浏览器大小
【解决方案2】:

使用新的CSS viewport unitsvwvh(视口宽度/视口高度)

FIDDLE

垂直和水平调整大小,您会看到元素将始终填充最大视口大小而不会破坏比例并且没有滚动条!

(纯)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 */
}
&lt;div&gt;&lt;/div&gt;

如果您想使用最多 90% 的视口宽度和高度: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;
}
&lt;div&gt;&lt;/div&gt;

另外,浏览器支持也不错:IE9+、FF、Chrome、Safari-caniuse

【讨论】:

  • 这是一个非常干净的解决方案。理想情况下,我可以在更早的时候使用浏览器支持。我猜在较旧的浏览器上,您只需设置最小宽度/高度并将其设置为 - 如果它有滚动条,那就这样吧
  • 不幸的是,最近的 iOS 版本don't support,或者视口单元的实现有问题(vhvwvminvmax)。但是,有一个 workaround,它使用媒体查询来定位 iOS 设备。
  • 我正在尝试实现您那里的 90% 解决方案,但我的图像不是 16:9 并且它正在垂直压缩我的图像。我的图像有各种尺寸。有解决办法吗?
  • @J82 如果您的图像具有不同的宽度/高度比率,那么您需要为每个比率编写代码(例如通过单独的类),或者您需要裁剪图像以适应特定的比率。如果您查看我发布的小提琴中的 cmets,您可以轻松修改代码以适应您需要的比例
  • @NaturalBornCamper - 在我的仅 webgl 画布应用程序(需要保持 16:9 AR 的地方)中,我偶尔也会出现滚动条的问题,但通过将 overflow: hidden; 添加到body .. 所以我的 body css decl 现在看起来像这样:body { width: 100%; height: 100%; overflow: hidden;}
【解决方案3】:

只需将 Danield 的答案重新表述在 LESS 混合中,以供进一步使用:

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

【讨论】:

    【解决方案4】:

    我最初的问题是如何同时拥有一个固定方面的元素,但又如何将其精确地放入指定的容器中,这使得它有点繁琐。如果您只是想让单个元素保持其纵横比,那就容易多了。

    我遇到的最佳方法是给元素零高度,然后使用百分比 padding-bottom 给它高度。百分比填充总是与元素的宽度成正比,而不是它的高度,即使它的顶部或底部填充也是如此。

    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%。如果它的宽度是 160 像素,那么底部填充,因此高度将是 90 像素 - 16:9 纵横比。

    这里的小问题可能对您来说不是问题,是您的新对象内部没有自然空间。例如,如果您需要放入一些文本并且该文本需要采用它自己的填充值,则您需要在其中添加一个容器并在其中指定属性。

    还有一些旧版浏览器不支持 vw 和 vh 单位,因此您可能无法接受我的问题的答案,您可能需要使用更低保真的东西。

    【讨论】:

    • 为了避免在此处使用额外的标记,您可以使用 ::before 或 ::after 添加一个元素,并为该元素指定一个高度为 0 和一个相对于对象宽度和 0 宽度的填充底部自己的。这将具有相同的效果,但标记更少,并且将使整个对象区域可用于放入内容而无需容器。
    【解决方案5】:

    现在指定了一个新的 CSS 属性来解决这个问题:object-fit

    https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit

    目前已广泛支持该功能 (http://caniuse.com/#feat=object-fit)。

    【讨论】:

      【解决方案6】:

      CSS media query @mediaCSS viewport units vwvh 一起使用以适应视口的纵横比。这有利于更新其他属性,例如 font-size

      This fiddle 演示了 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);
        }
      }
      

      这与@Danield 的max-widthmax-height approach 非常相似,只是更灵活。

      【讨论】:

        【解决方案7】:

        Danield's answer很好,但是只能在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>

        Here it is in a jsfiddle using scss,这使得值的来源更加明显。

        【讨论】:

          【解决方案8】:

          基于 Daniel's answer,我为 Bootstrap 模态对话框内的 youtube 视频 iframe 编写了这个带有插值的 SASS mixin (#{}):

          @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 是一个轻微的调整,以补偿这些黑色部分出现在侧面的一小部分;
          • 在非常低高度的设备(
          • 隐藏.modal-footer;
          • 调整.modal-dialog的定位;
          • 减小.modal-header 的大小。

          经过大量测试,这对我来说现在完美无缺。

          【讨论】:

            【解决方案9】:

            这里是基于@Danield 解决方案的解决方案,即使在 div 中也可以使用。

            在该示例中,我使用的是 Angular,但它可以快速迁移到 vanilla JS 或其他框架。

            主要思想只是将@Danield 解决方案移动到一个空的iframe 中,并在iframe 窗口大小更改后复制尺寸。

            该 iframe 必须与其父元素匹配尺寸。

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

            这里有几张截图:

            【讨论】:

              猜你喜欢
              • 2012-06-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-07-01
              • 2018-08-09
              • 1970-01-01
              • 1970-01-01
              • 2011-04-14
              相关资源
              最近更新 更多