请列举四种「等比例自适应矩形」实现方案?

引言

本文是由一次面试引发的探讨, 在一次面试过程中被问起「如何实现一个自适应等比例矩形?」, 对于实现方案我依稀记得是有看到过的, 但是一时也想不起来, 所以在面试的时候自然就没有回答出来!!!

面试结束回来也简单 google 了下, 找到实现方案其实基本都大差不差, 无非就是通过 vw 或者 padding 来实现, 所以也就没有怎么总结输出, 直到后面我找到了另外两种方案, 所以就有了这篇文章!!!

下面开始进入主题...

一、相对长度单位「vw」

vw: CSS 中的一个相对长度单位, 1vw 等于视口宽度的 1%

第一个方法, 就是使用 vw 来实现, 这个比较好理解, 当宽度、高度都基于同一个参考标准的一个相对长度, 那么自然就能够实现等比例矩形了

css 复制代码
.box {
  width: 50vw; 
  height: 25vw;
}

这里有个比较大的缺点就是, 宽度、长度只能相对于视口进行自适应; 无法相对于父元素进行自适应

二、使用「padding」属性

第二个方案, 也是早期比较推荐的一种做法; 该方法主要依据是: CSS 在默认情况下 marginpadding 属性的 垂直方向百分比值 都是 相对于父元素的宽度 来计算的

有代码如下: .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>

最后效果如下:

补充: 你是否好奇, 为啥 marginpadding 在垂直方向上的百分百是相对父元素宽度的, 而不是高度呢? 我们都知道父元素的高度往往由子元素来决定, 试想下如果 marginpadding 垂直方向的百分百比是相对于父元素高度的, 那么当它们设置了一个百分比, 相应的, 父元素高度会进行适应性增加; 此时父元素高度增加的同时, marginpadding 若以父元素高度为基准, 则其实际数值又会发生适应性变化, 双向因果会造成循环, 所以 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」更多补充

  1. 兼容性: 到目前(2023.10)为止除了 IE, 其他浏览器基本都已支持
  1. 当容器内容足够多的情况下 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, 高度为 110aspect-ratio 设置的比例并不匹配

那么这是为什么呢? 主要原因是 aspect-ratio 对容器尺寸的影响其实是最低的, 我们都知道影响容器尺寸的属性有很多包括min-width/min-heightmax-width/max-heightwidth/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; 来解决

  1. width/heightmin/max-width/height 的相互影响:
  • width/height 属性的关系:

    • widthheight 都设置了, 那么就没 aspect-ratio 任何事咯
    • widthheight 都没设置, 那么宽度自适应, 高度则按照 aspect-ratio 以及容器的宽度来计算高度
    • widthheight 设置其一, 则另一个尺寸按 aspect-ratio 来计算
  • min/max-width/height 属性的关系:

    • 实际上是 aspect-ratiowidth/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; 定义了一个可查询容器, 个人理解就是将容器标记为可查询容器
  • cqwCSS 单位中的一种, 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: cqicqb 中较小的值
  • cqmax: cqicqb 中较大的值

五、参考

相关推荐
乐闻x9 分钟前
Pinia 实战教程:构建高效的 Vue 3 状态管理系统
前端·javascript·vue.js
weixin_4314496822 分钟前
web组态软件
前端·物联网·低代码·编辑器·组态
橘子味小白菜28 分钟前
el-table的树形结构后端返回的id没有唯一键怎么办
前端·vue.js
前端Hardy1 小时前
HTML&CSS:比赛记分卡
前端·javascript·css·3d·html
疯狂的沙粒1 小时前
Vue项目开发 element-UI 前端实现 1到10排列选择的按钮
前端·vue.js·ui
刺客-Andy1 小时前
React第六节 组件属性prop的propTypes类型使用介绍
前端·javascript·react.js·typescript
Justinc.2 小时前
CSS3_媒体查询(十一)
css·css3·媒体
Mr.Liu62 小时前
小程序24-滚动效果:scroll-view组件详解
前端·微信小程序·小程序
是萝卜干呀2 小时前
Frontend - 防止多次请求,避免重复请求
javascript·ajax·jquery·防抖·节流·disabled属性
三金121382 小时前
局部使用Vue
前端·javascript·vue.js