CSS 奇技淫巧 —— 绝对居中布局

2023-07-10 · 1,104 chars · 6 min read

本文来聊一个 flex 布局的小技巧 —— 绝对居中布局。这名字是我瞎起的,可能不是特别恰当,不过我讲完背景,大家就应该都清楚了。

背景#

APP 顶部由上到下,一般是「状态栏」和「导航栏」。状态栏通常就是系统默认的样式,最多根据底色的深浅,把文字和图标切换为反色,保证用户可以清晰地看到时间等信息。但是导航栏,经常需要适配页面的视觉风格,特别是在一些「全屏 H5」中。比如下面这张图片:

前端在实现这个组件的时候,考虑到要兼容各种场景,通常会分为三个部分:

  • 左边:返回按钮,可能要适配 Android 和 iOS 的不同风格,看 APP 的精细程度
  • 中间:内容区。可以是文字,也可以是组件,比如上图的 <Tab />
  • 右边:自定义区域。常见的内容有「确认」,「搜索」,「帮助」等等...

那么问题来了,当左边和右边都是正常按钮时,两边宽度是一致的,中间文字默认居中即可。

但是,当右边的自定义内容,宽度与左边不同,或者干脆没有右边自定义区域。这种情况下,中间文字就不居中了:

那么,如何让中间的文字部分,相对于整个导航栏绝对居中呢?

绝对居中布局#

可能很多人首先想到的是,让左右两部分绝对定位在两侧。但是这样布局的问题是,中间区域是无法「感知」到左右两部分的宽度,很容易出现内容覆盖的情况:

所以,绝对定位的方案,样式上不够「安全」。而使用 JS 的话,由于设备和字体的差异,需要在运行时计算宽度,非常不利于 SSR 和 SSG 直接输出顶部内容。最好方案还是能继续使用 flex 三列布局,中间部分自动撑满余下全部区域,不产生重叠。

但是,使用 flex 布局要解决一个问题,由于左右两部分,存在不等宽的可能。那么在不等宽的情况下,如何保证中间区域仍然居中呢?

从问题到结论的过程中,经过了大量尝试和思考,这部分比较难用文字记录下来。此处直接说结论,中间区域居中的核心,是要保证左右两部分始终等宽。而让左右两部分始终等宽的方式,就是左右两边同时渲染左右两个组件,通过 overflow: hidden 裁掉多余的部分。看个示意图就一目了然了:

因为导航栏的高度,在一台设备上通常是固定的。所以左右两个组件,左边按照「左上右下」,右边按照「右上左下」的布局排列好,外部容器裁掉高出的部分,即可保证左右等宽。

HTML 代码:

<div class="container">
  <div class="left-wrapper">
    <div class="back">后退</div>
    <div class="report">反馈问题</div>
  </div>
  <div class="center">这里是绝对居中的标题</div>
  <div class="right-wrapper">
    <div class="report">反馈问题</div>
    <div class="back">后退</div>
  </div>
</div>

CSS 代码:

.container {
  width: 100%;
  height: 60px;
  line-height: 60px;
  display: flex;
  background-color: #ddd;
  border: 2px solid #000;
}

.left-wrapper,
.right-wrapper {
  width: auto;
  height: 60px;
  background-color: #d6e8d5;
  flex-direction: column;
}

.back {
  width: 60px;
  height: 60px;
  text-align: center;
  background-color: #ccc;
}

.report {
  width: 100px;
  height: 60px;
  text-align: center;
  background-color: #999999;
}

.center {
  flex: 1;
  text-align: center;
  background-color: #fff2cc;
}

这个方案比较 hack,最初这么做的原因是不想使用 JS 计算,纯 CSS 的方案性能更好,也比较适配我们的 SSR、SSG 架构。但毕竟会重复渲染两次,所以如果有一些特别复杂的组件,最好能在外层包一个定宽的容器,动态讲复杂组件渲染到容器内。

除了重复渲染的问题,此方案还存在一些其他的短板,比如加了 overflow: hidden 后,右边如果有下拉菜单的话,可能要特殊处理下。但总的来说,还是比较稳的,在线上使用很久了,没发现什么明显的问题。

PS:如果你有其他更好的方案,欢迎评论区留言讨论!

赞赏

微信