CSS
在 CSS 中,其实存在各种各样的函数。具体分为:

这里具体介绍其中的 CSS 数学函数(Math functions)中,已经被浏览器大规模支持的 4 个:

  • calc()
  • min()
  • max()
  • clamp()

为什么说是被浏览器大规模支持的?因为除了这 4 个目前已经得到大规模支持的数学函数外,其实规范 CSS Values and Units Module Level 4 已经定义了诸如三角函数相关 sin()cos()tan() 等,指数函数相关 pow()sqrt() 等等数学函数,只是目前都处于实验室阶段,还没有浏览器支持它们,需要给时间一点时间。

min()max()clamp()

min()max()clamp() 适合放在一起讲。它们的作用彼此之间有所关联。

  • max():从一个逗号分隔的表达式列表中选择最大(正方向)的值作为属性的值
  • min():从一个逗号分隔的表达式列表中选择最小的值作为属性的值
  • clamp():把一个值限制在一个上限和下限之间,当这个值超过最小值和最大值的范围时,在最小值和最大值之间选择一个值使用

由于在现实中,有非常多元素的的属性不是一成不变的,而是会根据上下文、环境的变化而变化。
譬如这样一个布局:

  1. <div class="container"></div>
  1. .container {
  2. height: 100px;
  3. background: #000;
  4. }

效果如下,.container 块它会随着屏幕的增大而增大,始终占据整个屏幕:
CSS 解决方案:数学函数之 min、max、clamp - 图1
对于一个响应式的项目,肯定不希望它的宽度会一直变大,而是当达到一定的阈值时,宽度从相对单位变成了绝对单位,这种情况就适用于 min(),简单改造下代码:

  1. .container {
  2. width: min(100%, 500px);
  3. height: 100px;
  4. background: #000;
  5. }

容器的宽度值会在 width: 100%width: 500px 之间做选择,选取相对小的那个。
在屏幕宽度不足 500px 时候,也就表现为 width: 100%,反之,则表现为 width: 500px
CSS 解决方案:数学函数之 min、max、clamp - 图2
同理,在类似的场景,也可以使用 max() 从多个值中,选取相对更大的值。

min()max() 支持多个值的列表

min()max() 支持多个值的列表,譬如 width: max(1px, 2px, 3px, 50px)
当然,对于上述表达:
width: max(1px, 2px, 3px, 50px) 其实等于 width: 50px。因此,对于 min()max() 的具体使用而言,最多应该只包含一个具体的绝对单位。否则,这样的像上述这种代码,虽然语法支持,但是任何情况下,计算值都是确定的,其实没有意义。

配合 calc

min()max()clamp() 都可以配合 calc 一起使用。
譬如:

  1. div {
  2. width: max(50vw, calc(300px + 10%));
  3. }

在这种情况下,calc 和相应包裹的括号可以省略,因此,上述代码又可以写成:

  1. div {
  2. width: max(50vw, 300px + 10%);
  3. }

基于 max、min 模拟 clamp

现在,有这样一种场景,如果,又需要限制最大值,也需要限制最小值,怎么办呢?
像是这样一个场景,**字体的大小,最小是 12px,随着屏幕的变大,逐渐变大,但是为了避免老人机现象(随着屏幕变大,无限制变大),还需要限制一个最大值 20px。
可以利用 vw 来实现给字体赋动态值,假设在移动端,设备宽度的 CSS 像素为 320px 时,页面的字体宽度最小为 12px,换算成 vw 即是 320 / 100 = 3.2,也就是 1vw 在 屏幕宽度为 320px 时候,表现为 3.2px,12px 约等于 3.75 vw。
同时,需要限制最大字体值为 20px,对应的 CSS 如下:

  1. p {
  2. font-size: max(12px, min(3.75vw, 20px));
  3. }

看看效果:
CSS 解决方案:数学函数之 min、max、clamp - 图3
通过 max()min() 的配合使用,以及搭配一个相对单位 vw,成功地给字体设置了上下限,而在这个上下限之间实现了动态变化。
当然,上面核心的这一段 max(12px, min(3.75vw, 20px)) 看上去有点绕,因此,CSS 推出了 clamp() 简化这个语法,下面两个写法是等价的:

  1. p {
  2. font-size: max(12px, min(3.75vw, 20px));
  3. // 等价于
  4. font-size: clamp(12px, 3.75vw, 20px);
  5. }

clamp()

clamp() 函数的作用是把一个值限制在一个上限和下限之间,当这个值超过最小值和最大值的范围时,在最小值和最大值之间选择一个值使用。它接收三个参数:最小值、首选值、最大值。
有意思的是,clamp(MIN, VAL, MAX) 其实就是表示 max(MIN, min(VAL, MAX))

使用 vw 配合 clamp 实现响应式布局

继续上面的话题。
在不久的过去,移动端的适配方面,使用更多的 rem 适配方案,可能会借助一些现成的库,类似于 flexible.js、hotcss.js 等库。rem 方案比较大的一个问题在于需要一段 JavaScript 响应视口变化,重设根元素的 font-size,并且,使用 rem 多少有点 hack 的感觉。
在现在,在移动端适配,更为推崇的是 vw 纯 CSS 方案,与 rem 方案类似,它的本质也是页面的等比例缩放。它的一个问题在于,如果仅仅使用 vw,随着屏幕的不断变大或者缩小,内容元素将会一直变大变小下去,这也导致了在大屏幕下,许多元素看着实在太大了!
因此,需要一种能够控制最大、最小阈值的方式,像是这样:
CSS 解决方案:数学函数之 min、max、clamp - 图4
此时,clamp 就能非常好的派上用场,还是上述的例子,这一段代码 font-size: max(12px, min(3.75vw, 20px));,就能将字体限制在 12px - 20px 的范围内。
因此,对于移动端页面而言,所有涉及长度的单位,都可以使用 vw 进行设置。而诸如字体、内外边距、宽度等不应该完全等比例缩放的,采用 clamp() 控制最大最小阈值。
Modern Fluid Typography Using CSS Clamp 一文中,对使用 clamp() 进行流式响应式布局还有更为深入的探讨,感兴趣的可以深入阅读。
总结而言,对于移动端页面,可以以 vw 配合 clamp() 的方式,完成整个移动端布局的适配。它的优势在于

  1. 没有额外 JavaScript 代码的引入,纯 CSS 解决方案
  2. 能够很好地控制边界阈值,合理的进行缩放展示

    反向响应式变化

    还有一个技巧,利用 clamp() 配合负值,也可以反向操作,得到一种屏幕越大,字体越小的反向响应式效果:
    1. p {
    2. font-size: clamp(20px, -5vw + 96px, 60px);
    3. }
    看看效果:
    CSS 解决方案:数学函数之 min、max、clamp - 图5
    这个技巧挺有意思的,由于 -5vw + 96px 的计算值会随着屏幕的变小而增大,实现了一种反向的字体响应式变化。

    总结

    总结一下,合理运用 min()max()clamp(),是构建现代响应式布局的重点,可以告别传统的需要 JavaScript 辅助的一些方案,基于 CSS 这些数学函数即可完成所有的诉求。
    一些进阶阅读非常好的文章: