媒体查询是一个伟大的概念。在HTML文档中建立一个复杂的结构,并为各种设备进行调整,没有它们往往是不可能的(至少目前是这样)。这里我们要谈的是,流体排版的一个缺点,即大量媒体查询的出现,是可以避免的。或者说,至少可以减少 @媒体 规则中的记录数量。
vw 和vh 相对单位的出现,calc 函数,以及后来 CSS 中的min,max,clamp 函数给了我们很大的权力。Adrian Bece最近发表了一篇关于使用CSS Clamp的现代流体排版的详尽评论。我建议大家都来了解一下。
clamp 函数的优势是显而易见的。我们可以定义一个变化,例如,在一定范围内的视口(屏幕尺寸)的字体大小,同时,通过最大和最小值来限制它。在这样一个简单的案例中,我们自动地(感谢钳制)不需要使用媒体查询来改变断点上的尺寸。
因此,CSS中的以下块:
.block{
font-size: 2rem;
}
@media (max-width: 1200px) {
.block{
font-size: calc(-1rem + 4vw);
}
}
@media (max-width: 800px) {
.block{
font-size: 1rem;
}
}
...可以很容易地用clamp ,只用一行就可以取代:
.block{
font-size: clamp(1rem, -1rem + 4vw, 2rem);
}
但是,如果我们需要设置一个更复杂的行为,而这个行为是由屏幕宽度的不同范围内的各种独特行为决定的,怎么办?请看下面的修改后的代码:
.block{
font-size: calc(-4rem + 8vw);
}
@media (max-width: 1200px) {
.block{
font-size: calc(-1rem + 4vw);
}
}
@media (max-width: 800px) {
.block{
font-size: calc(0.5rem + 0.8vw);
}
}
clamp 能否再次帮助我们?
让我们仔细看看:从简单到复杂。
对数学的简要探讨
我们都知道,画一条通过两点的直线只有一种方法。有几种方法可以写方程来定义直线。下面,我们将方便地把方程写成这样的形式:
$$(1)/;/;/;y=y\_0 + k\*x$$
其中y0是直线与Y-轴的交点(图1),k 参数定义了直线与X-轴的斜率,并代表增长/下降率。使用以下直线方程的典范表示:
$$frac{y - y\_1}{y\_1 - y\_2}=/frac{x - x\_1}{x\_1 - x\_2}$$
很容易将y0和k 参数与属于这条线的两个点的坐标联系起来:
$$(1a)\\;\\;k=\\frac{y\_2 - y\_1}{x\_2 - x\_1}。,y\_0=y\_1 - k\*x\_1$

应该指出的是,在这类问题中,用像素定义输入参数(点坐标)是很方便的。但在输出时,相对的单位rem 是比较好的。我们还应该记住一个事实:视口宽度等于100vw (这是从vw 单位的定义中得出的)。因此,在公式(1)中,我们必须用100vw 来代替x 这个变量。因此,我们会有:
$$(1b)/;/;/; y=y\_0 + k\*100vw$$
现在,像1rem + 2.5vw 这样的表达式作为clamp 或calc 函数的参数之一,其来源变得很清楚。第一个项(1rem)是用相对单位(rem)表示的y0参数,第二个项(2.5vw)是参数k 乘以100vw 因为x=100vw 。选择这样的单位--输出变量值的相对单位(rem)和屏幕尺寸的视口单位(vw)--是分别出于无障碍和响应性的考虑。
所以,现在我们知道如何确定通过两点的直线的公式(1)或(1b)中的参数。这将在下一节中使用。
一般情况下的主要公式推导
现在我们将尝试获得F(x) 函数的方程,它将遵循一般情况下的属性行为(图2)。对于不喜欢纯数学的读者,下面的一些信息可能会被省略。你可以看看最后的方程式(3)和简单的例子来解释如何使用它。
让我们来看看图2a(如下)。正如你从图中看到的,属性的行为是由坐标为*(xi, yi)的N 点决定的(表格)。让 fi(x)是一个定义通过(xi, yi)和(xi+1, yi+1)*点的直线的函数。我们有(N-1) 这样的函数(图2b)。那么,如何得到一个一般的F(x) ,在相应的 [xi, xi+1] 范围内完全等于 fi( x) 函数,而不是完全重复图2中属性的行为?

让我们把函数集合gi(x)定义为:
g i ( x ) = c l a m p y i , f i ( x ) , y i + 1 .
根据clamp 函数的定义。:
g i ( x ) = y i , x < x i ; f i ( x ) , x i ≤ x < x i + 1 ;
让我们把gi(x)函数相加,并把结果表示为G(x) 函数:G x = ∑ i = 1 N - 1 g i x 。
现在我们可以计算这个函数在不同范围的x 变量的值:
G ( x ) = f 1 ( x ) + y 2 + y
或
G ( x ) = f i ( x ) + C o n s t ,
为
x i ≤ x < x i + 1 , i = 1, ... , N - 1 , C o n s t = ∑ j = 2 N - 1 y j
由此可见,在扣除Const 常数项后,G(x) 函数等于每个[xi, xi+1]范围的相应*fi(*x)函数,或者......
F x = G x - C o n s t = ∑ i = 1 N - 1 c l a m p y i , f i ( x ) , y i + 1 - ∑ i = 2 N - 1 y i
方程(3)代表最终结果,是问题的解决方案。
在此应做几点说明:
- 如果我们有yi= yi+1的范围,那么fi(x)= yi,并且clamp(yi, yi, yi) = yi正确。这一事实将使我们在
F(x)函数的最终表达中得到一些简化。 - 如果我们有满足yi>yi+1不等式的范围,那么我们应该把相应的gi(x)函数写成以下形式。

...因为clamp 函数的定义。
- 让我们考虑х<х1和х>хN的范围。从方程(3)可以看出,这些范围内的属性值将是恒定的,相应地等于y1和yN。我们可以什么都不做,让它这样下去。但在这些区间内,我倾向于让数值继续按照*[x1, x2]和[xN-1, xN]区间内的相同规律变化。因此,g1(x)和gN-1(x*)函数不应通过
clamp,而是通过min或max,这取决于给定范围内的数值的增加或减少。如果我们以图2中属性的行为为例,那么我们将有以下两个重新定义。


- 有可能在该属性中设置一个突然的变化。在这种情况下,хi和хi+1之间的最大差异被设置为
1px(图3),或者在实践中更方便,甚至小于1px。 - 很明显,属性的行为越复杂(范围越多),产生的函数就越长,反之亦然。
- 由于方程(3)中的函数可能结构复杂,它必须作为CSS样式表中的CSS
calc函数的参数。

简单的例子
让我们来模拟一下下面这个简单的例子。我们在某个页面上有一个带有标志和菜单项的页眉。对于移动设备,我们需要创建一个汉堡包菜单。在这种情况下,例如,菜单项的字体大小在屏幕尺寸为1920px 时等于18px ,在视口宽度为768px 时减小到12px 。在视口宽度为320px 到767.98px 的范围内,字体大小固定为20px (图4a)。字体大小的这种行为可以用公式(3)来描述。让我们开始吧。

1.我们需要根据方程(1a)计算f1、f2和f3行的参数,以便以(1b)形式表示。对于f1函数,我们有(使用1和2点的协调):

所以,使用方程(1b):

对另两条线采用同样的程序,我们可以得到:

和

和

2.现在我们可以用方程(2)来构建吉函数

3.方程(3)中的最后一项(常数)可以被确定

4.方程(3)的理想形式是...

或者,最后的形式是

5.CSS文件中的最终记录将是(通过注释6)。
.block{
font-size: calc(clamp(0.75rem, 19200.75rem – 40000vw, 1.25rem) + max(0.75rem, 0.5rem + 0.5208333vw) – 0.75rem);
}
...并完全等同于:
.block{
font-size: calc(0.5rem + 0.5208333333vw);
}
@media (max-width: 767px) {
.block {
font-size: 1.25rem;
}
}
对右边缘行为建模的评价(见图4b)给出了以下方程(大家可以验证一下):

你可以在这里查看这个例子。

请看Ruslan的PenQuick CSS例子[forked]。
每个人都可以根据方程(3)编写自己的混合器或函数,或者使用我的实现,作为考虑到上述意见和原始情况的CSS函数。
你也可以在这里找到一个使用flex结构的简单例子。在这个例子中,flex元素的宽度是用方程(3)计算的。这是为了避免媒体查询这种常用的转换方式--最初,四个flex项目的宽度都被设置为25%(当然,转换为px单位),然后,随着视口宽度的减少,它变为50%,然后变为100%。甚至似乎有可能考虑到gap的值,并使其也具有响应性。
但是一旦出现垂直滚动,这个例子就会中断。vw 为了避免例子被破坏,并对滚动条进行补偿,我们可以在表达式中用% 替换柔性元素的宽度单位。但由于CSS的限制不允许我们直接对百分比值使用公式(3),因此最好放弃这种想法。
摘要
我想说的是,这个想法只是为了证明这种方法的可能性,其应用是否合适是你自己的选择。在这种方法的明显缺点中,我必须强调,所产生的代码变得不那么可读。好处是,媒体查询仍然只用于描述变化的逻辑,而适应性的、结构性的变化,不会被改变字体大小、间距、页边距等的技术行所堵塞。
编者注:如果能建立一个小工具,让开发者能在短时间内定义变化并构建这些复杂的查询,那将是非常棒的。然而,维护工作会变得相当麻烦--除非我们使用Sass混合技术,也许?我们衷心感谢Ruslan抽出时间来做这件事,天啊,这是个多么好的旅程啊!"。