clamp() 的作用
clamp(MIN, PREFERRED, MAX) 接收三个值,只要中间的值处于另外两个值之间,就返回中间的值。最经典的用法是让标题随屏幕变大,但停在合理的界限上,而且不需要媒体查询:
首选值是 1rem + 3vw:16px 加上宽度的 3%。在 800px 宽的预览中,结果是 16 + 24 = 40px,处于 24px(1.5rem)和 48px(3rem)之间,所以标题是 40px。在 320px 宽的手机上,首选值是 25.6px。超过 1067px 后它会大于 48px,所以停在 48px。调整窗口大小就能看到数字变化;这里的 vw 是预览框宽度的百分比。
不用调整窗口也能看到效果
vw 跟随视口变化,而阅读时你很难改变视口大小。容器查询单位则跟随容器变化:cqi 是容器宽度的 1%。拖动滑块改变盒子的大小,同样写法的 clamp() 就会随之响应:
滑到最左边,标题停在 20px(MIN)。滑到最右边,标题停在 40px(MAX)。在两者之间,它跟随 0.5rem + 6cqi 变化。这和 vw 版本是同一个机制,只是换了一把更短的尺子。
三个参数
font-size: clamp(1rem, 0.8rem + 1vw, 1.5rem);
/* MIN PREFERRED MAX */
| 参数 | 作用 | 常用单位 |
|---|---|---|
| MIN | 这个值能达到的最小值 | rem |
| PREFERRED | 随屏幕变化的值 | rem + vw(或 cqi) |
| MAX | 这个值能达到的最大值 | rem |
每个参数里都可以直接做计算,不用写 calc():1rem + 2vw 原样写就可以。+ 和 - 的两边都要有空格,否则整条声明都无效。
如果 MIN 大于 MAX,MIN 优先:clamp(3rem, 5vw, 2rem) 永远是 3rem。
计算中间的值
要让字号在 320px 的屏幕上是 16px,在 1280px 的屏幕上增长到 24px:
- 斜率:(24 − 16) ÷ (1280 − 320) = 8 ÷ 960 = 0.00833,也就是 0.833vw(vw 以每 100px 为单位)。
- 起点:16px − 0.00833 × 320px = 13.33px,也就是 0.833rem。
- 在两边加上界限:
font-size: clamp(1rem, 0.833rem + 0.833vw, 1.5rem);
宽度为 320px 时结果是 13.33 + 2.67 = 16px,宽度为 1280px 时结果是 13.33 + 10.67 = 24px,超出这个范围就停在界限上。网上的“clamp 计算器”做的正是这个运算。
min() 和 max()
clamp() 是两个更简单的函数的组合:
min(a, b)返回较小的值,所以它设置的是上限。max(a, b)返回较大的值,所以它设置的是下限。clamp(MIN, VAL, MAX)等同于max(MIN, min(VAL, MAX))。
这两个名字一开始会让人觉得是反的:想表达“最多 600px”,要用 min()。
在宽预览中,第一张卡片停在 420px。在 260px 宽的父元素中,100% 是较小的值,所以第二张卡片是 260px。max(12px, 3%) 让第一张卡片得到父元素宽度的 3%(父元素是 body:在 800px 的预览中约为 23.5px),而第二张卡片得到 12px,因为 260px 的 3%(7.8px)低于下限。
width: min(100%, 420px) 用一条声明取代了旧的两条写法 width: 100%; max-width: 420px;。
不只是字号:间距和宽度
clamp() 可以用在任何接受长度的属性中。在中间的值里使用百分比,组件就会相对父元素流动变化,而不是相对视口:
在 300px 宽的父元素中,卡片宽 240px(80%),内边距是 15px(300px 的 5%)。在 760px 宽的父元素中,80% 会是 608px,所以卡片停在最大值 520px;760px 的 5% 是 38px,所以内边距停在 32px。
无障碍:中间的值要保留 rem
只用 vw 写的字号,例如 font-size: 5vw,在读者调大浏览器设置中的默认字号时不会变大,在浏览器缩放时也不会变大,因为缩放会让视口在 CSS 像素上变窄。加入 rem 部分(1rem + 2vw)并用 rem 作界限,文字就会跟随读者的设置。单位页面详细讲解了 rem 和 vw。
常见错误
- 用
min()设置最小值。min()选的是较小的值,也就是上限。要表达“至少 12px”,用max(12px, ...)。 +或-两边没有空格。clamp(1rem, 1rem+2vw, 3rem)是无效的,属性会退回到之前的值。- 中间只用
vw。 这样文字会忽略读者设置的字号。加上一个rem项。 - MIN 大于 MAX。 结果永远是 MIN,这通常不是你想要的。
- 字号的界限用
px。 即使读者需要更大的字,它也会把文字限制住。请用rem。
常见问题
CSS 中的 clamp() 有什么作用?
clamp(MIN, PREFERRED, MAX) 在首选值处于两个界限之间时使用首选值,否则使用离它最近的界限。font-size: clamp(1rem, 4vw, 2rem) 跟随视口宽度的 4% 变化,但永远不会小于 1rem,也不会大于 2rem。
如何用 clamp 实现响应式字号?
中间放一个基于视口的值,两边放固定的界限,并在中间的值里混入 rem,这样文字仍会跟随读者设置的字号:font-size: clamp(1.5rem, 1rem + 2vw, 3rem);。
如果 clamp() 中的最小值大于最大值会怎样?
最小值优先。clamp(3rem, 5vw, 2rem) 的结果永远是 3rem,因为 clamp() 是在应用最大值之后再应用最小值的。
CSS 中 min() 和 max() 有什么区别?
min() 返回其中最小的值,所以它起到上限的作用:width: min(100%, 600px) 永远不会宽于 600px。max() 返回最大的值,所以它起到下限的作用:font-size: max(1rem, 2vw) 永远不会小于 1rem。
所有浏览器都支持 clamp() 吗?
是的,clamp()、min() 和 max() 在当前版本的 Chrome、Edge、Firefox 和 Safari 中都能使用,并且可以用在任何接受长度、数字、百分比或角度的属性中。