用 CSS 维护 div 的宽高比

问题描述:

I want to create a div that can change its width/height as the window's width changes.

Are there any CSS3 rules that would allow the height to change according to the width, while maintaining its aspect ratio?

I know I can do this via JavaScript, but I would prefer using only CSS.

div keeping aspect ratio according to width of window

转载于:https://stackoverflow.com/questions/1495407/maintain-the-aspect-ratio-of-a-div-with-css

Just create a wrapper <div> with a percentage value for padding-bottom, like this:

div {
  width: 100%;
  padding-bottom: 75%;
  background: gold; /** <-- For the demo **/
}
<div></div>

It will result in a <div> with height equal to 75% of the width of its container (a 4:3 aspect ratio).

This relies on the fact that for padding :

The percentage is calculated with respect to the width of the generated box's containing block [...] (source: w3.org, emphasis mine)

Padding-bottom values for other aspect ratios and 100% width :

aspect ratio  | padding-bottom value
--------------|----------------------
    16:9      |       56.25%
    4:3       |       75%
    3:2       |       66.66%
    8:5       |       62.5%

Placing content in the div :

In order to keep the aspect ratio of the div and prevent its content from stretching it, you need to add an absolutely positioned child and stretch it to the edges of the wrapper with:

div.stretchy-wrapper {
  position: relative;
}

div.stretchy-wrapper > div {
  position: absolute;
  top: 0; bottom: 0; left: 0; right: 0;
}

Here's a demo and another more in depth demo

</div>

If the entire container structure was percentage based, this would be the default behavior, can you provide a more specific example?

Below is an example of what I mean, if your entire parent hierarchy was % based, any browser window adjustment would work without any additional js/css, is this not a possibility with your layout?

<div style="width: 100%;">
   <div style="width: 50%; margin: 0 auto;">Content</div>
</div>

I've found a way to do this using CSS, but you have to be careful as it may change depending on the flow of your own web site. I've done it in order to embed video with a constant aspect ratio within a fluid width portion of my web site.

Say you have an embedded video like this:

<object>
     <param ... /><param ... />...
     <embed src="..." ...</embed>
</object>

You could then place this all inside a div with a "video" class. This video class will probably be the fluid element in your website such that, by itself, it has no direct height constraints, but when you resize the browser it will change in width according to the flow of the web site. This would be the element you are probably trying to get your embedded video in while maintaining a certain aspect ratio of the video.

In order to do this, I put an image before the embedded object within the "video" class div.

!!! The important part is that the image has the correct aspect ratio you wish to maintain. Also, make sure the size of the image is AT LEAST as big as the smallest you expect the video (or whatever you are maintaining the A.R. of) to get based on your layout. This will avoid any potential issues in the resolution of the image when it is percentage-resized. For example, if you wanted to maintain an aspect ratio of 3:2, don't just use a 3px by 2px image. It may work under some circumstances, but I haven't tested it, and it would probably be a good idea to avoid.

You should probably already have a minimum width like this defined for fluid elements of your web site. If not, it is a good idea to do so in order to avoid chopping elements off or having overlap when the browser window gets too small. It is better to have a scroll bar at some point. The smallest in width a web page should get is somewhere around ~600px (including any fixed width columns) because screen resolutions don't come smaller unless you are dealing with phone-friendly sites. !!!

I use a completely transparent png but I don't really think it ends up mattering if you do it right. Like this:

<div class="video">
     <img class="maintainaspectratio" src="maintainaspectratio.png" />
     <object>
          <param ... /><param ... />...
          <embed src="..." ...</embed>
     </object>
</div>

Now you should be able to add CSS similar to the following:

div.video { ...; position: relative; }
div.video img.maintainaspectratio { width: 100%; }
div.video object { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; }
div.video embed {width: 100%; height: 100%; }

Make sure you also remove any explicit height or width declaration within the object and embed tags that usually come with copy/pasted embed code.

The way it works depends on the position properties of the video class element and the item you want have maintain a certain aspect ratio. It takes advantage of the way an image will maintain its proper aspect ratio when resized in an element. It tells whatever else is in video class element to take full-advantage of the real estate provided by the dynamic image by forcing its width/height to 100% of the video class element being adjusted by the image.

Pretty cool, eh?

You might have to play around with it a bit to get it to work with your own design, but this actually works surprisingly well for me. The general concept is there.

To add to Web_Designer's answer, the <div> will have a height (entirely made up of bottom padding) of 75% of the width of it's containing element. Here's a good summary: http://mattsnider.com/css-using-percent-for-margin-and-padding/. I'm not sure why this should be so, but that's how it is.

If you want your div to be a width other than 100%, you need another wrapping div on which to set the width:

div.ar-outer{
    width: 60%; /* container; whatever width you want */
    margin: 0 auto; /* centered if you like */
}
div.ar {
    width:100%; /* 100% of width of container */
    padding-bottom: 75%; /* 75% of width of container */
    position:relative;
}
div.ar-inner {
    position: absolute;
    top: 0; bottom: 0; left: 0; right: 0;
}

I used something similar to Elliot's image trick recently to allow me to use CSS media queries to serve a different logo file depending on device resolution, but still scale proportionally as an <img> would naturally do (I set the logo as background image to a transparent .png with the correct aspect ratio). But Web_Designer's solution would save me an http request.

Elliot inspired me to this solution - thanks:

aspectratio.png is a completely transparent PNG-file with the size of your preferred aspect-ratio, in my case 30x10 pixels.

HTML

<div class="eyecatcher">
  <img src="/img/aspectratio.png"/>
</div>

CSS3

.eyecatcher img {
  width: 100%;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-image: url(../img/autoresized-picture.jpg);
}

Please note: background-size is a css3-feature which might not work with your target-browsers. You may check interoperability (f.e. on caniuse.com).

Basing on your solutions I've made some trick:

When you use it, your HTML will be only

<div data-keep-ratio="75%">
    <div>Main content</div>
</div>

To use it this way make: CSS:

*[data-keep-ratio] {
    display: block;
    width: 100%;
    position: relative;
}
*[data-keep-ratio] > * {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
}

and js (jQuery)

$('*[data-keep-ratio]').each(function(){ 
    var ratio = $(this).data('keep-ratio');
    $(this).css('padding-bottom', ratio);
});

And having this you just set attr data-keep-ratio to height/width and that's it.

vw units:

You can use vw units for both the width and height of the element. This allows the element's aspect ratio to be preserved, based on the viewport width.

vw : 1/100th of the width of the viewport. [MDN]

Alternatively, you can also use vh for viewport height, or even vmin/vmax to use the lesser/greater of the viewport dimensions (discussion here).

Example: 1:1 aspect ratio

div {
  width: 20vw;
  height: 20vw;
  background: gold;
}
<div></div>

For other aspect ratios, you can use the following table to calculate the value for height according to the width of the element :

aspect ratio  |  multiply width by
-----------------------------------
     1:1      |         1
     1:3      |         3
     4:3      |        0.75
    16:9      |       0.5625

Example: 4x4 grid of square divs

body {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}
div {
  width: 23vw;
  height: 23vw;
  margin: 0.5vw auto;
  background: gold;
}
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>

Here is a Fiddle with this demo and here is a solution to make a responsive grid of squares with verticaly and horizontaly centered content.


Browser support for vh/vw units is IE9+ see canIuse for more info

</div>

As stated in here on w3schools.com and somewhat reiterated in this accepted answer, padding values as percentages (emphasis mine):

Specifies the padding in percent of the width of the containing element

Ergo, a correct example of a responsive DIV that keeps a 16:9 aspect ratio is as follows:

CSS

.parent {
    position: relative;
    width: 100%;
}
.child {
    position: relative;
    padding-bottom: calc(100% * 9 / 16);
}
.child > div {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}

HTML

<div class="parent">
    <div class="child">
        <div>Aspect is kept when resizing</div>
    </div>
</div>

Demo on JSFiddle

Just an idea or a hack.

div {
  background-color: blue;
  width: 10%;
  transition: background-color 0.5s, width 0.5s;
  font-size: 0;
}

div:hover {
  width: 20%;
  background-color: red;
}
  
img {
  width: 100%;
  height: auto;
  visibility: hidden;
}
<div>
  <!-- use an image with target aspect ratio. sample is a square -->
  <img src="http://i.imgur.com/9OPnZNk.png" />
</div>

</div>

SCSS is the best solution in my case; using a data attribute :

[data-aspect-ratio] {
    display: block;
    max-width: 100%;
    position: relative;

    &:before {
        content: '';
        display: block;
    }

    > * {
        display: block;
        height: 100%;
        left: 0;
        position: absolute;
        top: 0;
        width: 100%;
    }
}
[data-aspect-ratio="3:1"]:before {
    padding-top: 33.33%;
}
[data-aspect-ratio="2:1"]:before {
    padding-top: 50%;
}
[data-aspect-ratio="16:9"]:before {
    padding-top: 56.25%;
}
[data-aspect-ratio="3:2"]:before {
    padding-top: 66.66%;
}
[data-aspect-ratio="4:3"]:before {
    padding-top: 75%;
}
[data-aspect-ratio="1:1"]:before {
    padding-top: 100%;
}
[data-aspect-ratio="3:4"]:before {
    padding-top: 133.33%;
}
[data-aspect-ratio="2:3"]:before {
    padding-top: 150%;
}
[data-aspect-ratio="9:16"]:before {
    padding-top: 177.77%;
}
[data-aspect-ratio="1:2"]:before {
    padding-top: 200%;
}
[data-aspect-ratio="1:3"]:before {
    padding-top: 300%;
}

For example :

<div data-aspect-ratio="16:9"><iframe ...></iframe></div>

source

I used a new solution.

.squares{
  width: 30vw
  height: 30vw

To main aspect ratio

.aspect-ratio
  width: 10vw
  height: 10vh

However, this is relative to the entire viewport. So, if you need a div that is 30% of the viewport width, you can use 30vw instead, and since you know the width, you reuse them in height using calc and vw unit.

lets say you have 2 divs the outher div is a container and the inner could be any element that you need to keep its ratio (img or an youtube iframe or whatever )

html looks like this :

<div class='container'>
  <div class='element'>
  </div><!-- end of element -->

lets say you need to keep the ratio of the "element"

ratio => 4 to 1 or 2 to 1 ...

css looks like this

.container{
  position: relative;
  height: 0
  padding-bottom : 75% /* for 4 to 3 ratio */ 25% /* for 4 to 1 ratio ..*/

}

.element{
  width : 100%;
  height: 100%;
  position: absolute; 
  top : 0 ;
  bottom : 0 ;
  background : red; /* just for illustration */
}

padding when specified in % it is calculated based on width not height. .. so basically you it doesn't matter what your width it height will always be calculated based of that . which will keep the ratio .

You can use an svg. Make the container/wrapper position relative, put the svg first as staticly positioned and then put absolutely positioned content (top: 0; left:0; right:0; bottom:0;)

Example with 16:9 proportions:

image.svg: (can be inlined in src)

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 9" width="16" height="9"/>

CSS:

.container {
  position: relative;
}
.content {
  position: absolute;
  top:0; left:0; right:0; bottom:0;
}

HTML:

<div class="container">
  <img style="width: 100%" src="image.svg" />
  <div class="content"></div>
</div>

Note that inline svg doesn't seem to work, but you can urlencode the svg and embed it in img src attribute like this:

<img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%209%22%20width%3D%2216%22%20height%3D%229%22%2F%3E" style="width: 100%;" />

While most answers are very cool, most of them require to have an image already sized correctly... Other solutions only work for a width and do not care of the height available, but sometimes you want to fit the content in a certain height too.

I've tried to couple them together to bring a fully portable and re-sizable solution... The trick is to use to auto scaling of an image but use an inline svg element instead of using a pre-rendered image or any form of second HTTP request...

div.holder{
  background-color:red;
  display:inline-block;
  height:100px;
  width:400px;
}
svg, img{
  background-color:blue;
  display:block;
  height:auto;
  width:auto;
  max-width:100%;
  max-height:100%;
}
.content_sizer{
  position:relative;
  display:inline-block;
  height:100%;
}
.content{
  position:absolute;
  top:0;
  bottom:0;
  left:0;
  right:0;
  background-color:rgba(155,255,0,0.5);
}
<div class="holder">
  <div class="content_sizer">
    <svg width=10000 height=5000 />
    <div class="content">
    </div>
  </div>
</div>

Notice that I have used big values in the width and height attributes of the SVG, as it needs to be bigger than the maximum expected size as it can only shrink. The example makes the div's ratio 10:5

</div>

I have run into this issue quite some times, so I made a JS solution for it. This basically adjust the height of the domElement according the width of the element by the ratio you specify. You could use it as followed:

<div ratio="4x3"></div>

Please be aware that since it is setting the height of the element, the element should be either a display:block or display:inline-block.

https://github.com/JeffreyArts/html-ratio-component

If you want to fit a square inside the viewport on either portrait or landscape view (as big as possible, but nothing sticking outside), switch between using vw/vh on orientation portrait/landscape:

@media (orientation:portrait ) {
  .square {
    width :100vw;
    height:100vw;
  }
} 
@media (orientation:landscape) {
  .square {
    width :100vh;
    height:100vh;
  }
} 

As @web-tiki already show a way to use vh/vw, I also need a way to center in the screen, here is a snippet code for 9:16 portrait.

.container {
  width: 100vw;
  height: calc(100vw * 16 / 9);
  transform: translateY(calc((100vw * 16 / 9 - 100vh) / -2));
}

translateY will keep this center in the screen. calc(100vw * 16 / 9) is expected height for 9/16.(100vw * 16 / 9 - 100vh) is overflow height, so, pull up overflow height/2 will keep it center on screen.

For landscape, and keep 16:9, you show use

.container {
  width: 100vw;
  height: calc(100vw * 9 / 16);
  transform: translateY(calc((100vw * 9 / 16 - 100vh) / -2));
}

The ratio 9/16 is ease to change, no need to predefined 100:56.25 or 100:75.If you want to ensure height first, you should switch width and height, e.g. height:100vh;width: calc(100vh * 9 / 16) for 9:16 portrait.

If you want to adapted for different screen size, you may also interest

  • background-size cover/contain
    • Above style is similar to contain, depends on width:height ratio.
  • object-fit
    • cover/contain for img/video tag
  • @media (orientation: portrait)/@media (orientation: landscape)
    • Media query for portrait/landscape to change the ratio.

You can use the flux layout (FWD).

https://en.wikipedia.org/wiki/Adaptive_web_design

It will scale and maintain the layout, its a bit complex but allows for a page to resize without pushing the content like (RWD).

It looks like responsive but it is scaling. https://www.youtube.com/watch?v=xRcBMLI4jbg

You can find the CSS scaling formula here:

http://plugnedit.com/pnew/928-2/

This is an improvement on the accepted answer:

  • Uses pseudo elements instead of wrapper divs
  • The aspect ratio is based on the width of the box instead of its parent
  • The box will stretch vertically when the content becomes taller

.box {
  margin-top: 1em;
  margin-bottom: 1em;
  background-color: #CCC;
}

.fixed-ar::before {
  content: "";
  float: left;
  width: 1px;
  margin-left: -1px;
}
.fixed-ar::after {
  content: "";
  display: table;
  clear: both;
}

.fixed-ar-16-9::before {
  padding-top: 56.25%;
}
.fixed-ar-3-2::before {
  padding-top: 66.66%;
}
.fixed-ar-4-3::before {
  padding-top: 75%;
}
.fixed-ar-1-1::before {
  padding-top: 100%;
}

.width-50 {
  display: inline-block;
  width: 50%;
}
.width-20 {
  display: inline-block;
  width: 20%;
}
<div class="box fixed-ar fixed-ar-16-9">16:9 full width</div>
<hr>
<div class="box fixed-ar fixed-ar-16-9 width-50">16:9</div>
<hr>
<div class="box fixed-ar fixed-ar-16-9 width-20">16:9</div>
<div class="box fixed-ar fixed-ar-3-2 width-20">3:2</div>
<div class="box fixed-ar fixed-ar-4-3 width-20">4:3</div>
<div class="box fixed-ar fixed-ar-1-1 width-20">1:1</div>
<hr>
<div class="box fixed-ar fixed-ar-16-9 width-20">16:9</div>
<div class="box fixed-ar fixed-ar-16-9 width-50">16:9</div>

</div>

Say that you to maintain Width: 100px and Height: 50px (i.e., 2:1) Just do this math:

.pb-2to1 {
  padding-bottom: calc(50 / 100 * 100%); // i.e., 2:1
}