响应式技巧指南

本文正在参加「金石计划」

今天看到了一个网站:react.gg/ ,一个学习 React 的网站,当你打开它有点花里胡哨,但好像也没啥特别的。

响应式技巧指南

不过有职业习惯的我,顺道打开 devtools 开了,发现了些门道,就是今天的主角—— fontsize clamp属性。

响应式技巧指南

clamp 也不是啥陌生人了,它配合 min()/max() 这 3 个数学函数在 2018 年底开始支持,至今五个年头了,比我从业时间都长。但是平时遇到这三个参数往往都是在响应式设计布局中,今天神了奇了头一次在 fontsize 中遇到。

接下来讲在 fontsize 中使用 clamp 的好处。

响应式字体的解决方案

现代浏览器的默认字体大小一般是 16px,如果不挑剔,16px 在大多数设备上显示都没问题,可能唯一的缺点就是对于使用超大显示屏的人不友好,比如屏幕尺寸 2700+ 的显示屏,16px 字体的字就会非常小,一般需要手动放大才能看清。

大屏也好解决直接加个媒体查询就好了:

@ media(min-width: 2000px) {
    .title { 
        font-size: 3rem; 
    }
}

作为网站维护者,友好的体验的还是必要的,以无码科技的官网:nocode.com/ 为例,它需要支持三端运行——手机 + iPad + PC,网页默认字体大小为 16px,网页是恒定不变的,但三端的窗口是不一样的,一定程度上会导致内容主题不突出,讲人话就是内容不直观看不清。

根据习惯,小的屏幕字体就应该小点,用来显示更多的内容,大的屏幕字体就应该大点,内容看得清楚。手机、iPad、PC 屏幕从小到大,字体也应该从小到大,不应该一律默认为 16px。

短平快解决

为了解决这个问题,以前大家的套路很简单,凡是遇到响应式,一律媒体查询解决,比如通过媒体查询设置根元素的 fontsize,然后用 rem 引用:

html {
     font-size: 16px;
}
 @media screen and (min-width: 320px) {
     html {
         font-size: calc(16px + 6 * ((100vw - 320px) / 680));
    }
}
 @media screen and (min-width: 1000px) {
     html {
         font-size: 22px;
    }
}

有啥缺点呢,就是体验不够丝滑,因为屏幕大大小小太多了,比如大点手机屏幕直接比肩常规的 iPad,大点 iPad 直接和电脑一样大,至于超大显示器更多了。

想要丝滑还有另一种解决方案。

视口法

想要自动调节,那就是使用视口宽度单位 vw 作为字体的单位。比如:

h1 {
  font-size: 5.9vw;
}

这种方法太过没节操,可以无限大,也可无限小,字体完全跟随着屏幕大小变化。

响应式技巧指南

事实上,当屏幕小/大到一定程度,为了良好的体验,字体就不应该再改变了。

甚至在代码实践中,为了最佳显示效果会手动设置屏幕的最大宽度 Max-width 和最小宽度 min-width,比如携程移动端:

响应式技巧指南

因为没有设置最小宽度,特小得屏幕上显示就崩溃了:

响应式技巧指南

clamp 方法

在一定范围内设置字体大小,这简直就是为 clamp 定制的需求。CSS 的 clamp(minimum, preferred, maximum) 函数有三个参数:最小值、首选值(推荐值)和最大值,它将限制一个值的取值范围,确保它不会小于最小值或大于最大值,并且在最小值和最大值之间以首选值为中心进行变化。

比如我们常常会这样设置宽度,让其在 300px ~ 600px 之间变化。

width: clamp(300px, 50%, 600px);

比如设置网页常规字体大小如下:

.title {
  font-size: clamp(16px, 5vw, 50px);
}

这会设置浏览器窗口在 (16 * 20 = )320px(50 * 20 = )1000px 的范围内字体从 16px ~ 50px 进行变化。

响应式技巧指南

可能看到这里你就要关闭文章了,但事情还没结束。

首先,如何工程化的应用 clamp 就是一个难题,比如下面截图的一篇博客:

响应式技巧指南

标题和内容肯定要设置不同的 fontsize,更何况 HTML 中有六个标题 p 和 a 等标签字体大小都是不同的。

其次,大家看我们确定 clamp 最大值和最小值的方式,完全人脑计算很容易出错,比如:

font-size: clamp(1.2rem, 5vw, 2.1rem);

要想知道浏览器窗口在多大范围内触发 fontsize 的变化范围,不免颇费周折的计算一番,这就导致我们想要给固定窗口大小的浏览器指定变化范围很难。

最后,clamp 的推荐值 5vw 这种书写方式,有一个很大的体验问题,会遇到浏览器放大缩小字体无效的情况:

给出一个标题和一旦文字,标题和文字都没有设置字体的大小,全部采用默认的样式:

响应式技巧指南

我们使用浏览器的缩放功能把页面放大 2 倍,字体也被放大两倍,看下对比:

响应式技巧指南

等比缩放没有任何问题。

现在调整标题 h1 的 fontsize 为 font-size: clamp(16px, 5vw, 50px); 看下对比效果:

响应式技巧指南

缩放两倍的 h1 好像变化不明显,打开控制台我们就明白了,fontsize 被改变了:

响应式技巧指南

测量下视口大小就明白了,宽 649px 小于 1000px,原因很清楚了,放大动作导致了视口变小,clamp 正常运作了

响应式技巧指南

如何计算 clamp 的推荐值

确定 body 作为基准,然后定义标题 h1 ~ h6 的最大/小值。

Font Element h6 body h5 h4 h3 h2 h1
Minimum 0.90 1.00 1.20 1.40 1.60 1.80 2.00
Maximum 1.00 1.10 1.45 1.80 2.10 2.45 2.75

上表用 Excel 生成折线图更加直观展示:

响应式技巧指南

我们不改变浏览器根元素字体大小,默认为 16px,同时以 h1 为例它的 fontsize 最小 2rem 最大 2.75rem:

clamp(2rem, y, 2.75rem);

待会为了计算,fontsize 单位需要由 rem 转成 px,最小值换算结果为 (2 * 16px = )23px,最大为 (2.75 * 16px = )44px,

clamp(32px, y, 44px);

现在设定最小屏幕尺寸为 380px,最大屏幕尺寸为 1800px。

接下来就是怎么求取 y 了。

解法一

我们要求解的 y 是随着视口(x)变化,得到不同的字体大小(y),比如 380 => 32,1800 => 44。(380, 32) 和 (1800, 44) 作为坐标绘制到折线图上如下图:

响应式技巧指南

绘图工具 rapidtables

观察图我们知道这是一个一次方程,而且两点的区间就是字体大小随视口的变化范围,这范围内的所有点都会落在 y = kx + b 方程上,已知两点带入可求得斜率 k 和截距 b 的值:

  1. 斜率 k,约等于 0.00845px 截距 b 约等于 28.78px

响应式技巧指南

也就是讲 clamp 的推荐值为:

clamp(32px, 0.008450704225x px + 28.78px, 44px);

其中 x 表示浏览器的窗口大小,这里就可以替换为 100vw,在最小屏幕 380px 显示的时候 100vw 就等于 380px,在最大屏幕 1800px 显示的时候 100vw 就等于 1800px。

此时的 clamp 的推荐值为:

// clamp(32px, 0.00845 * 100vw + 28.78px, 44px);
clamp(32px, 0.845vw + 28.78 px, 44px);

最后单位由 px 换算成 rem 且全部保留三位小数:

// clamp(32px / 16px, 0.845vw + 28.78px / 16px, 44px / 16px);
clamp(2rem, 0.845vw + 1.799rem, 2.75rem);

知道公式的思路和换算逻辑,我们就可以做一个类似求推荐值的工具,事实上这种工具已经很多了,一搜一大把:

响应式技巧指南

点击体验:chrisburnell.com/clamp-calcu…

解法二

我认为上面的思路最简单,但还有一个解法二思路来源为:enzedonline.com/en/tech-blo…

同时,最小值 2rem 在 380px 屏幕下出现,此时页面有 (380px / 16px = 23.75) 个 rem,最大值 2.75 在 1800px 屏幕下出现,此时页面有(1800px / 16px = 112.5)个 rem。

我们把计算结果放到图上,会得到一个一次函数 y = kx + b 知道两点坐标带入求解,就能得到方程 y = 0.0085x + 1.7993。

响应式技巧指南

因为视口 vw 是以百分比来描述的,所以我们需要将斜率乘以 100 来实现我们的 clamp 函数:

y = 0.85vw + 1.7993rem

此时的 h1 为:

h1 {
  font-size: clamp(2rem, 1.799rem + 0.85vw, 2.75rem);
}

结论

1.799rem + 0.85vw 完美解决了上面提到直接写 4w 引发的三个问题。

现在得到公式了,就可以写个简单的小工具,指定浏览器大小,clamp 的最大值和最小值,推荐值就能自己计算:min-max-calculator.9elements.com/

响应式技巧指南

工程化设计

借助 min-max-calculator.9elements.com/ 工具我们就可以自己设计网站的 fontsize 大小和空间布局大小。

比如下面这份 CSS 清单就是 9elements.com/blog/ 使用的模板:

:root {
    --space-3xs: clamp(0.25rem, 0.23rem + 0.08vw, 0.31rem);
    --space-2xs: clamp(0.5rem, 0.48rem + 0.08vw, 0.56rem);
    --space-xs: clamp(0.75rem, 0.72rem + 0.16vw, 0.88rem);
    --space-s: clamp(1rem, 0.97rem + 0.16vw, 1.13rem);
    --space-m: clamp(1.5rem, 1.45rem + 0.25vw, 1.69rem);
    --space-l: clamp(2rem, 1.93rem + 0.33vw, 2.25rem);
    --space-xl: clamp(3rem, 2.9rem + 0.49vw, 3.38rem);
    --space-2xl: clamp(4rem, 3.87rem + 0.66vw, 4.5rem);
    --space-3xl: clamp(7rem, 6.77rem + 1.15vw, 7.88rem);
    --space-4xl: clamp(12rem, 11.61rem + 1.97vw, 13.5rem);
    --space-3xs-2xs: clamp(0.25rem, 0.17rem + 0.41vw, 0.56rem);
    --space-2xs-xs: clamp(0.5rem, 0.4rem + 0.49vw, 0.88rem);
    --space-xs-s: clamp(0.75rem, 0.65rem + 0.49vw, 1.13rem);
    --space-s-m: clamp(1rem, 0.82rem + 0.9vw, 1.69rem);
    --space-m-l: clamp(1.5rem, 1.3rem + 0.99vw, 2.25rem);
    --space-l-xl: clamp(2rem, 1.64rem + 1.81vw, 3.38rem);
    --space-xl-2xl: clamp(3rem, 2.61rem + 1.97vw, 4.5rem);
    --space-2xl-3xl: clamp(4rem, 2.98rem + 5.1vw, 7.88rem);
    --space-3xl-4xl: clamp(7rem, 5.29rem + 8.55vw, 13.5rem);
    
    --text-step--2: 0.875rem;
    --text-step--1: clamp(0.781rem, 0.757rem + 0.12vw, 0.875rem);
    --text-step-0: clamp(1rem, 0.967rem + 0.16vw, 1.125rem);
    --text-step-1: clamp(1.125rem, 1.092rem + 0.16vw, 1.25rem);
    --text-step-2: clamp(1.25rem, 1.184rem + 0.33vw, 1.5rem);
    --text-step-3: clamp(1.438rem, 1.257rem + 0.9vw, 2.125rem);
    --text-step-4: clamp(2rem, 1.638rem + 1.81vw, 3.375rem);
    --text-step-5: clamp(2.5rem, 1.513rem + 4.93vw, 6.25rem);
    --text-step-6: clamp(2.938rem, 1.079rem + 9.29vw, 9.5rem);
}

使用起来就非常的友好:

响应式技巧指南

对于空间尺寸 9elements 使用的是原子化 CSS 类名的命名规范,对于 fontsize 采用的则是传统 HTML 六个标题和正文按照字号大小排序,当然你也可以重写为符合原子 CSS 的变量:

text-xs   
text-sm   
text-base 
text-lg
text-xl   
text-2xl  
text-3xl  
text-4xl  
text-5xl  
text-6xl  
text-7xl  
text-8xl  
text-9xl  

编程随想

我不知道你是否熟悉这个网络 ID,21 年就消失了,最近出现了,却不是一个好的结局,因为发现社会中存在一些 bug,提了些 issues 开了几个 PR,直接 emoji 了。

希望他和他的家人都健健康康,都好好的活着,一起见证没有谎言修饰的明天。

参考

原文链接:https://juejin.cn/post/7216624116336689212 作者:蜡笔小伟

(0)
上一篇 2023年4月3日 下午4:16
下一篇 2023年4月3日 下午4:27

相关推荐

发表回复

登录后才能评论