请列举四种「等比例自适应矩形」实现方案?
引言
本文是由一次面试引发的探讨, 在一次面试过程中被问起「如何实现一个自适应等比例矩形?」, 对于实现方案我依稀记得是有看到过的, 但是一时也想不起来, 所以在面试的时候自然就没有回答出来!!!
面试结束回来也简单 google 了下, 找到实现方案其实基本都大差不差, 无非就是通过 vw 或者 padding 来实现, 所以也就没有怎么总结输出, 直到后面我找到了另外两种方案, 所以就有了这篇文章!!!
下面开始进入主题…
一、相对长度单位「vw」
vw:
CSS中的一个相对长度单位,1vw等于视口宽度的1%
第一个方法, 就是使用 vw 来实现, 这个比较好理解, 当宽度、高度都基于同一个参考标准的一个相对长度, 那么自然就能够实现等比例矩形了
.box {width: 50vw; height: 25vw;
}
这里有个比较大的缺点就是, 宽度、长度只能相对于视口进行自适应; 无法相对于父元素进行自适应
二、使用「padding」属性
第二个方案, 也是早期比较推荐的一种做法; 该方法主要依据是: CSS 在默认情况下 margin 和 padding 属性的 垂直方向 的 百分比值 都是 相对于父元素的宽度 来计算的
有代码如下: .container 宽度设置为 500px, .container::before 通过 padding-top: 50%; 把父元素撑开, 实现 2:1 的矩形
<style>.container {width: 500px;background-color: #91caff;}.container::before {content: '';display: block;padding-top: 50%; /* 相对于父元素宽度 */}
</style>
<div class="container"></div>
最后效果如下:

该方法兼容性是没有问题的, 但是由于容器被子元素占满, 我们如果想要在容器内部放置其他内容, 就需要多一层嵌套 div, 然后通过 position 来布局, 如下代码所示: 新增 .content 元素, 然后通过 position 进行定位
<style>.container {width: 500px;position: relative;background-color: #91caff;}.container::before {content: '';display: block;padding-top: 50%; /* 相对于父元素宽度 */}.content {inset: 0;position: absolute;}
</style>
<div class="container"><div class="content">content</div>
</div>
最后效果如下:

补充: 你是否好奇, 为啥
margin和padding在垂直方向上的百分百是相对父元素宽度的, 而不是高度呢? 我们都知道父元素的高度往往由子元素来决定, 试想下如果margin和padding垂直方向的百分百比是相对于父元素高度的, 那么当它们设置了一个百分比, 相应的, 父元素高度会进行适应性增加; 此时父元素高度增加的同时,margin和padding若以父元素高度为基准, 则其实际数值又会发生适应性变化, 双向因果会造成循环, 所以W3C的规范做出了以上规定
三、使用「aspect-ratio」属性
CSS属性aspect-ratio可以为盒子设置一个首选的宽高比例, 这个宽高比可以用于计算auto尺寸以及其他布局函数
第三个方案是通过 aspect-ratio 来实现, aspect-ratio 属性可以为元素设置 宽度和高度 的一个 默认比例, 这样的话我们就很容易实现一个任意比例的矩形了, 如下代码: 通过 aspect-ratio: 2 / 1; 约定默认情况下容器的宽度和高度比例为 2:1, 由于容器只设置了宽度, 那么在 aspect-ratio 的约束下, 高度将自适应性, 从而实现了一个 2:1 的等比例矩形
<style>.container {width: 500px;aspect-ratio: 2 / 1;background-color: #91caff;}</style>
<div class="container"></div>
最后效果如下:

3.1 关于「aspect-ratio」更多补充
- 兼容性: 到目前(2023.10)为止除了IE, 其他浏览器基本都已支持

- 当容器内容足够多的情况下 aspect-ratio的限制将失效, 如下代码所示: 为.container容器添加一段文本, 设置容器宽度为200px, 设置aspect-ratio也就是默认容器宽高比为2:1
<style>.container {width: 200px;aspect-ratio: 2 / 1;background-color: #91caff;}</style>
<div class="container">男子吊环决赛中, 东京奥运会冠亚军刘洋、尤浩为中国队上了 "双保险", 最终以15.233分和14.833分分别摘得金牌和铜牌
</div>
最终效果如下: 由于容器内容很多, 最终会发现容器的宽度为 200, 高度为 110 和 aspect-ratio 设置的比例并不匹配

那么这是为什么呢? 主要原因是 aspect-ratio 对容器尺寸的影响其实是最低的, 我们都知道影响容器尺寸的属性有很多包括min-width/min-height、max-width/max-height、width/height、容器本身内容、aspect-ratio, 那么它们对容器尺寸的影响优先级如下 min-* > max-* > width/height > 内容 > aspect-ratio, 也就是容器本身内容对容器尺寸的影响力大于 aspect-ratio 所以就出现了上面 👆🏻 这个现象
这里解决办法其实也简单, 我们只需要为容器设置一个 min-height: 0; 即可:
<style>.container {width: 200px;aspect-ratio: 2 / 1;background-color: #91caff;
+   min-height: 0;}</style>
<div class="container">男子吊环决赛中, 东京奥运会冠亚军刘洋、尤浩为中国队上了 "双保险", 最终以15.233分和14.833分分别摘得金牌和铜牌
</div>
最终效果如下: 从尺寸上来看 aspect-ratio 成功生效了, 至于文本溢出, 可以设置 overflow: auto; 来解决

- 和 width/height、min/max-width/height的相互影响:
-  和 width/height属性的关系:- width和- height都设置了, 那么就没- aspect-ratio任何事咯
- width和- height都没设置, 那么宽度自适应, 高度则按照- aspect-ratio以及容器的宽度来计算高度
- width和- height设置其一, 则另一个尺寸按- aspect-ratio来计算
 
-  和 min/max-width/height属性的关系:- 实际上是 aspect-ratio和width/height属性关系的延伸
- 如果设置了 min-width或者max-width, 并且影响到容器宽度(可以理解为设置固定的width)
- 如果设置了 min-height或者max-height, 并且影响到容器高度(可以理解为设置固定的height)
- 那么最后的关系还是和上文的 width/height是一致;
 
- 实际上是 
个人理解:
width/height也好min/max-width/height也罢, 只要容器宽度和高度都被影响到(受限), 那么aspect-ratio将失效; 如果其中一个被影响到(受限), 那么aspect-ratio将按照容器最终的尺寸计算另一个尺寸的大小; 如果容器的宽度高度都没受到影响, 那么aspect-ratio将默认按照容器的最终宽度来计算另一个尺寸的大小
四、使用「cqw」单位
第四种, 也是文本介绍的最后一种方法!! 这里其实是使用了 CSS 容器查询的新特性, 容器查询 可以实时获取到指定容器元素的一个尺寸, 并通过媒体查询来监听容器尺寸的变化, 来做一些自适应!!! 当然本文并不需要使用到媒体查询, 但我们可以借用配套的 CSS 单位 cqw 来实现等比例矩形
我们来直接看代码, 基于代码来对 cqw 进行简单讲解, 如下代码:
- container-type: inline-size;定义了一个可查询容器, 个人理解就是将容器标记为可查询容器
- cqw是- CSS单位中的一种,- 1cwq它等于- 最近的- 可查询祖先容器宽度的- 1%
<style>.container {width: 200px;container-type: inline-size;background-color: #91caff;}.content {height: 50cqw; /* 最近可查询容器宽度的 50% */}</style>
<div class="container"><div class="content"></div>
</div>
最后效果如下:

通过 cqw 可以很容器实现任意比例的矩形, 但是它有个缺点就是您可能需要额外嵌套一层!!!
补充: 更多容器查询长度单位
cqw: 查询容器宽度的1%
cqh: 查询容器高度的1%
cqi: 查询容器行向尺寸的1%
cqb: 查询容器块尺寸的1%
cqmin:cqi和cqb中较小的值
cqmax:cqi和cqb中较大的值
五、参考
- CSS实现宽高等比例自适应矩形
- 如何使用 css + div 来实现一个等比例矩形
- CSS高宽不等图片固定比例布局的三重进化
- Chrome 88已经支持aspect-ratio属性了, 学起来
- CSS百分比padding实现比例固定图片自适应布局
- CSS 容器查询

