我们在日常开发页面的过程中,对于固定背景图的容器的宽高的处理,往往是按照设计稿写死,很少会出现需要固定宽高比例,而宽度自适应的场景。
但是这个情况,在低代码平台的基础组件中会比较常见,比如布局组件:
用户上传一张背景图,需要用图片的宽度撑满容器,高度采用图片的宽高比进行自适应,然后内容区域由用户继续拖拽其他组件完成设计。
此时,由于不确定是否在其他容器内,所以宽度只能设置为 100%,但是高度就比较难设置。
有几个方案,方案一是通过 getComputedStyle 获取当前元素的实际宽度,然后 JS 根据图片的宽高比直接计算出高度来设置给元素。
方案二则是父元素采用 relative 定位,子元素通过 img 元素来作为 children, 再增加一个 绝对定位撑满整个父元素的 div 包裹其他子元素,借助 img 的默认的高度自适应特性完成宽高比的保留,伪代码如下:
const Layout = () => (
<div className="w-full relative">
<img src="xxxx" width="xxx" height="xxx" />
<div className="absolute top-0 bottom-0 left-0 right-0">
{/* 其他拖拽进来的子元素 */}
</div>
</div>
)
方案三和方案二类似,只不过原本依赖 img 标签默认的高度自适应,调整为利用 padding top 设置为百分比时参照的是宽度来完成宽高比例的设置,伪代码如下:
//aspectRatio = width / height,假设是 16 / 9,那么 paddingTop = (height / width) * 100%,所以 paddingTop 设置为 1 / (16 / 9) * 100%,也就是 1 / aspectRatio * 100%
const Layout = (aspectRatio) => (
<div className="w-full relative" style={{ paddingTop: `${1 / aspectRatio * 100}%` }}>
<div className="absolute top-0 bottom-0 left-0 right-0">
{/* 其他拖拽进来的子元素 */}
</div>
</div>
)
我一开始采用的便是方案三,毕竟简单。
但是很快就出现了问题,子元素本来配置出来,比如两行文案是可以正常展示的,但是当文案被多语言翻译后,可能就会超出这个背景图高度,这一点在移动端更为明显。
这个问题,方案二和方案三都不能满足,方案一倒是可以把计算出来的高度设置为最小高度来解决这个问题,但是还是略显麻烦。
此时就是本文要介绍的核心 CSS 属性了:aspect-ratio。
aspect-ratio 在 2021 年提出,如今已是基础能力,现代浏览器广泛支持。
不过也正是由于是 2021年提出的,我自己对于 CSS 的学习,其实是停留在了工作前,后来新增的一些特性并没有进行新的学习,反而还是比较古老的思维。这次也是在和一个同事的探讨下才进行了新的尝试。
这个属性,其实本质上就是来解决这个问题的。
.card {
width: 100%;
aspect-ratio: 16 / 9;
}
在父容器设置后,当内容元素高度不足,则会保持同样的宽高比例,当超出时,使用子元素的高度来撑开。
可以说是非常适合我当下的场景了。
最终的伪代码如下:
const Layout = (aspectRatio) => (
<div className="w-full" style={{
aspectRatio,
backgroundImage: `url('your-image.jpg')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}}>
{/* 其他拖拽进来的子元素 */}
</div>
)
通过设置 aspect-ratio 来完成固定宽高比的实现,背景图通过 cover 来保证铺满,在超出场景下,借助 aspect-ratio 的特性来完成父容器的撑开,图片由于是 cover,并不会显得突兀。
关于新旧两种方案,两者实现对比如下:
| 特性 | 旧的 “Padding Hack” 方式 | 新的 aspect-ratio |
|---|---|---|
| 原理 | 利用 padding-bottom: 56.25%(9/16)来撑开高度 |
直接声明 aspect-ratio: 16 / 9 |
| 代码量 | 冗长,需要额外的容器或伪元素 | 一行代码 |
| 内容溢出 | 内容容易被切断,必须绝对定位内部元素 | 智能处理:如果内容太多,盒子会自动长高 |
| 直观性 | 需要拿计算器算百分比(100 * 9 / 16) |
直接写比例,所见即所得 |
可以说是非常方便的一个属性了。
也给我自己提了个醒,现在早已不是只有 CSS3 的天下了,新特性往往好用也方便,得持续保持学习才行。
如果你也在做低代码画布组件,推荐先从这个属性开始替换旧方案。