当前内容所在位置
- 第一章 层叠、优先级与继承
- 第二章 相对单位
- 2.1 相对单位的威力
- 2.2 em 与 rem
- 2.3 告别像素思维
- 2.4 视口的相对单位 ✔️
- 2.5 无单位的数值与行高
- 2.6 自定义属性
- 2.7 本章小结
2.4 视口的相对单位
前面介绍过的 em
和 rem
是相对于 font-size
定义的,但相对单位并非只有这一类情况;还有一类 视口相对单位(viewport-relative units),用于定义相对于浏览器视口的长度。
视口的定义
视口(viewport) 是浏览器窗口中网页可见部分的边框区域。它不包括浏览器的地址栏、工具栏和(可能显示的)状态栏。
最近 CSS 语言对于视口相对单位又增加了一些新内容。目前 CSS 中总共有 24 个视口相对单元。这听起来可能让人难以接受(overwhelming),但它们都是由一些相对简单的概念通过多种方式的混搭而衍生出来的。本节将从最基本的知识点开始介绍。以下是最初添加到 CSS 语言中的四个基础视口相对单位:
vh
—— 视口高度的 1%;vw
—— 视口宽度的 1%;vmin
—— 视口宽、高中较小的一方的 1%;vmax
—— 视口宽、高中较大的一方的 1%。
例如,50vw
等于视口宽度的一半,25vh
等于视口高度的 25%。vmin
取决于宽或高中尺寸较小的一方。这样就保证了元素始终都能适应屏幕尺寸,而与设备方向无关:横屏时 vmin
取决于视口高度;竖屏时则取决于视口宽度。
图 2.10 展示了一个正方形元素在不同屏幕尺寸的视口中的显示效果。它的宽高均为 90vmin
,也就是两个维度中较小一方的 90%,即横屏时高度的 90%,或竖屏时高度的 90%。
图 2.10 宽高均为 90vmin 的元素,无论视口大小或方向如何,都会显示成一个稍小于视口的正方形
代码清单 2.14 为该元素的样式代码。无论浏览器如何缩放,最终都能得到一个适应视口的大正方形。在页面中添加一段 <div class= "square">
就能看到效果。
代码清单 2.14 用 vmin
定义正方形元素的尺寸
.square {width: 90vmin;height: 90vmin;background-color: #369;
}
视口相对长度非常适合展示一个填满屏幕的大图。也可以将图片放在一个长容器内,给定高度为 100vh
,理论上就能使其与视口高度完全一致。
当这些相对单位开始用于往视口填充大图后,人们发现一个问题:在移动设备上,视口的尺寸会动态变化。为了让屏幕可用尺寸最大化,移动端浏览器设计了这样一个功能:当用户向下滑动页面时,一些与用户体验相关的控件,如屏幕顶部的地址栏以及底部的导航按钮,会滑出视窗;而当用户向上滑动页面时,这些控件又会恢复显示。
这些动态变化会导致视口大小的缩放,进而改变页面上使用 vh
作单位的元素尺寸,并最终导致下方内容在屏幕上跳动。想象一下这样的场景:在用户向下滑过多个 100vh
的方盒后,又稍微向上翻了一下。这将导致视图中的所有内容猛地上窜几百个像素,带来极度不协调的阅读体验;况且还有性能问题——因为浏览器必须重新计算页面布局(通常称之为 布局抖动(layout thrashing))。
最终,大多数移动端浏览器都根据视口能够达到的最大尺寸重新解释了 vh
,并忽略地址栏对视口尺寸的影响,从而阻止了该抖动行为。但有时开发人员可能并不想这样处理,因此 CSS 规范中又补充了几个相对单位。
2.4.1 从新的视口单位遴选
为了解决布局抖动的问题,CSS 引入了大视口(large viewports)和小视口(small viewports)的概念。大视口是在浏览器隐藏所有用户体验元素时的最大视口;而小视口则恰恰相反,是这些元素都正常显示时的浏览器最小视口(如图 2.11 所示)。
图 2.11 移动设备大小视口对比
与普通的视口单位一样,大视口单位也可用于设置宽度、高度、最小值和最大值。使用大视口单位,需在前面加上字母 l
(即 large 的首字母小写):lvw
、lvh
、lvmin
、lvmax
;同理,前面加上字母 s
(即 small 的首字母小写)则表示使用小视口单位:svw
、svh
、svmin
、svmax
。
有了这些新单元,开发人员就能根据当前场景遴选出更重要的行为模式:满屏高度的报头(masthead)是否必须占满整个屏幕,即便超出浏览器底部一点点也无所谓吗?如果是的话,就用 height: 100lvh
;还是说,整个报头保持可见更重要,就算报头下方、屏幕底部会空出一点空间也无伤大雅?如果是的话,就用 height: 100svh
。
此外,以下注意事项也要牢牢记住:
- 视口单位制没有考虑滚动条:也就是说,当存在垂直滚动条时,宽度为
100svw
的元素会产生水平滚动。 - 当存在屏幕键盘时,关于小视口单位是否应该缩小屏幕尺寸,CSS 规范尚未明确规定。目前一些安卓浏览器是这样处理的,但 iOS 浏览器不是;未来可能还会变动。
- 在绝大多数浏览器中,原始视口单位往往表现为大视口单位,但事无绝对。
这就又留下一个悬而未决的问题:某些情况下可能需要视口单位的原始行为,即当浏览器的用户体验元素出现或隐藏时,视口单位会动态切换。对于这种行为,CSS 提供了第三类视口相对单元:动态视口(dynamic viewport)。启用它们,只需在视口单位前加上字母 d
(即 dynamic 的首字母小写)即可:dvw
、dvh
、dvmin
、dvmax
。当视口较小时,其行为类似小视口单位;而当视口较大时,又类似大视口单元。
使用动态视口单位时务必慎之又慎——当用户在移动设备上下滚动页面时,一旦元素高度发生动态变化,很可能会出现布局抖动。
表 2.1 列出了所有可用的视口相对单位。为了完整起见,还包括一组额外的单位类型:内联(inline)与块(block)。这两种类型被称为“逻辑属性”,其行为分别类似于宽度和高度,但对于日语等垂直书写的语言,则需要对调一下。本书第 3 章还将深入介绍逻辑属性。
表 2.1 视口相对单位一览表
未指定视口(原始单位) | 大视口 | 小视口 | 动态视口 | |
---|---|---|---|---|
宽 / 高 | vw / vh | lvw / lvh | svw / svh | dvw / dvh |
最小 / 最大 | vmin / vmax | lvmin / lvmax | svmin / svmax | dvmin / dvmax |
内联 / 块 | vi / vb | lvi / lvb | svi / svb | dvi / dvb |
除了上面提过的报头及其他类似的全屏或半屏元素之外,应该并没有充分的理由断言某种视口单位一定优于另一种视口单位。但由于可供选择的视口相对单元如此之多,可能还会让人有些无从下手。这种情况下,我会选择小视口单位。
2.4.2 使用视口单位定义字号
视口相对单位有一个不起眼的用途,就是设置字号。但我发现它比用 vh
和 vw
设置元素的宽度和高度还要实用。
试想,给一个元素加上 font-size: 2svw
会发生什么?在一个 1200px
的台式机显示器上,字号的计算值为 24px
(即 1200 的 2%);而在一个 768px
的平板设备上,计算值则为 15px
(即 768 的 2%)。这么做的好处在于,该元素在两种尺寸之间可以平滑地过渡。这意味着字号不会在某个断点发生突变,而是随着视口尺寸的变化而逐渐过渡。
唯一美中不足的是,24px
的字号对台式屏幕太大了;更有甚者换到 iPhone SE 上,字号又会一直缩到仅有 7.5px
左右。如果能让字号保留这种缩放的能力,同时又能避免走极端就好了。为此,CSS 的 calc()
或 clamp()
函数可以助您一臂之力。
1 利用 CALC() 函数实现响应式
calc()
函数可以对两个及以上的值进行基本四则运算。当涉及不同单位的值时,calc()
特别实用。其支持的运算包括:加(+
)、减(-
)、乘(*
)、除(/
)。其中加号和减号两边必须留有空白,因此建议大家养成在每个操作符前后都加上一个空格的习惯,比如 calc(1em + 10px)
。
以下代码利用 calc()
函数将 em
与 svw
单位相结合。从样式表中删除之前的基本字号,以及相关的媒体查询。换成以下样式代码:
:root {font-size: calc(0.5em + 1svw);
}
现在打开页面,慢慢缩放浏览器,字体也会平滑地缩放。 0.5em
保证了最小字号,而 1svw
则引入了一个响应式的标量。这样,基础字号就可以从 iPhone SE 上的 11.75px
一直过渡到 1200px
的浏览器窗口中的 20px
。
警告
在使用视口单位来计算字号时,一定要确保算式中包含
em
或rem
单位值,以提高页面的可访问性。这样一来,最终渲染出的字号就能兼顾用户自定义的字体设置。
您可以根据自己的喜好调整这些值,但要找到一个在小视口中不会太小、并在大视口中不会太大的理想值,可能还是有点困难。
2 利用 CLAMP() 函数加以完善
上面介绍的这种响应式字号很有用,但一个更新的 CSS 函数 clamp()
可以提供更精细的控制。clamp()
接受三个参数:最小值、以表达式形式给出的首选值、以及最大值。再次按以下样式更新页面:
:root {font-size: clamp(0.9rem, 0.6rem + 0.5svw, 1.5rem);
}
此时指定的字号为 0.6rem + 0.5svw
,但 clamp()
函数确保了最终结果不会小于 0.9rem
,也不会大于 1.5rem
。这样,即便遇到非常大或非常小的视口,也不会出现字号越界的情况。
既然字号是响应式的,那么页面上使用 em
或 rem
定义的其他尺寸也可以响应式地同步缩放。这样,无需使用任何媒体查询,就完成了响应式策略的绝大部分工作。页面上的所有内容都将根据视口进行流畅缩放,而无需设置三四个硬编码的断点。响应式设计的内容远不止这些,后续章节还会进行更深入的探讨,但掌握这些知识必将为后续的设计工作开一个好头。
提示
min()
和max()
这两个相关函数可能时常也会用到。min()
函数表示给定值中的最小值(如width: min(200px, 20svw);
);max()
则表示给定值中的最大值(如min-height: max(200px, 20svw);
)。