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