在我们开发的过程中,我们很经常有碰到这样的需求,要让某个dom元素始终保持一个固定的宽高比,并且需要随着视窗的宽度 / 高度的变化自适应改变其宽度 / 高度。
直接通过js计算当然能够实现此需求。 我们可以将元素的宽度设置为百分比,在页面resize的时候再通过js获取元素的宽度,并根据宽高比计算出元素的高度。但是,我们先不说此方案的性能问题,但作为一名有追求的切图仔,秉着能用css解决的事情就不要用js的观念,我还是不太能接受这种方式。
如果是纯css方案的话,一般我们会使用媒体查询
或者通过vh/vw + calc
来动态计算样式,但这两种方案都有一定的限制。
媒体查询
媒体查询
只能在屏幕宽高达到指定的阈值时才会触发计算,自适应效果不理想。如下面这个例子只有在屏幕宽度为1365px
、1680px
、1920px
的情况下会触发样式的计算。
@media (max-width: 1365px) {
.video {
width: 400px;
height: 300px;
}
}
@media (min-width: 1366px) and (max-width: 1679px) {
.video {
width: 600px;
height: 450px;
}
}
@media (min-width: 1680px) and (max-width: 1919px) {
.video {
width: 800px;
height: 600px;
}
}
@media (min-width: 1920px) {
.video {
width: 1000px;
height: 750px;
}
}
vh/vw + calc
vh/vw + calc
是一个简单且直观的自适应方案,我们可以将元素的宽度设置为固定vw/vh,并通过calc计算出对应的高度:
.video{
width:40vw;
height:30vw;
}
但是有个比较鸡肋的地方,因为vw/vh是相对于视窗的宽高的,因为当窗口上存在滚动条的话,他会把滚动条的宽高给计算上,那么你计算的时候就要把滚动条的宽高也要考虑上。
通过padding和包含块特性进行实现
实现原理
今天我们来看看一个比较冷门的方式,我们试下利用padding和包含块特性保持DOM元素宽高比。
首先, 我们先来看看包含块
的概念:
-
如果 position 属性是
static
或relative
的话,包含块就是由它的最近的祖先块元素(比如说inline-block, block 或 list-item元素)或格式化上下文BFC(比如说 a table container, flex container, grid container, or the block container itself)的内容区的边缘组成的。 -
如果 position 属性为
absolute
,包含块就是由它的最近的 position 的值不是static
(也就是值为fixed
,absolute
,relative
或sticky
)的祖先元素的内边距区的边缘组成。 -
如果 position 属性是
fixed
,在连续媒体的情况下(continuous media)包含块是 viewport ,在分页媒体(paged media)下的情况下包含块是分页区域(page area)。 -
如果 position 属性是
absolute
或fixed
,包含块也可能是由满足以下条件的最近父级元素的内边距区的边缘组成的:-
transform
或perspective
的值不是none
-
will-change
的值是transform
或perspective
-
filter
的值不是none
或will-change
的值是filter
(只在 Firefox 下生效). -
contain
的值是paint
(例如:contain: paint;
)
-
然后,再来明确两个结论:
- 绝对定位元素的大小是由top、right、 bottom、 left四个属性决定的,当这四个属性的值是一个比例值(如百分比等)的话,那这些属性的计算是基于
包含块
的内容区进行的。 - 垂直方向上的内外边距使用百分比做单位时,是基于
包含块内容区的宽度
来计算的。
实现思路
我们可以在目标元素外层使用一个容器元素进行包裹,并将容器元素的position
设置为relative
,此时容器元素的包含块为他最近的祖先块元素或BFC。由于垂直方向上的内外边距使用百分比做单位时,是基于包含块内容区的宽度
来计算的,所以我们将容器元素width
和padding-bottom
的单位设置为百分比,此时width / padding-bottom
的值就是我们想要得到的宽高比。如果此时width === 100%
,padding-bottom = 想要的高度 / 想要的宽度 * 100%
, 如:
//宽高比为 16 : 9
padding-bottom = 9 / 16 * 100% = 56.25%;
//宽高比为 4 : 3
padding-bottom = 3 / 4 * 100% = 75%;
又因为绝对定位元素的大小是由top、right、 bottom、 left四个属性决定的,当这四个属性的值是一个比例值(如百分比等)的话,那这些属性的计算是基于包含块
的内容区进行的。,我们将目标元素的position
设置为absolute
,此时目标元素的包含块为容器元素,我们再将目标top、right、 bottom、 left都设置为0,此时目标元素的宽高就能够完美贴合容器元素的内容区的宽高了。
代码实现
下面我们通过借助padding-bottom来使元素div.video
保持16 : 9的宽高比:
<div class="video-wrapper">
<div class="video"></div>
</div>
.video-wrapper {
position: relative;
width: 100%;
height: 0;
padding: 0;
padding-bottom: 56.25%;
}
.video{
background-color: yellow;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}