在上一期我们学习了新颜色语法和新单位的使用,现在我们对这些单位进一步强化,学习如何通过函数对它们赋予更自由的值。本期包含了一些数学概念,不过放心都是简单的小学数学。
通用函数的应用
在 CSS3 发展的今天,我们拥有了众多可供使用函数,他们分布在任何属性需要地方,比如下列展示了目前通用函数的属性和介绍。
通用函数列表
- attr() 返回所选元素的属性值。
- rgb() 使用红-绿-蓝模型(RGB)定义颜色。
- hsl() 使用色相-饱和度-亮度模型(HSL)定义颜色。
- linear-gradient() 将线性渐变设置为背景图像。定义至少两种颜色(从上到下)。
- radial-gradient() 将径向渐变设置为背景图像。定义至少两种颜色(从中心到边缘)。
- repeating-linear-gradient() 重复线性渐变。
- repeating-radial-gradient() 重复径向渐变。
- cubic-bezier() 定义三次贝塞尔曲线。
- calc() 允许您执行计算来确定 CSS 属性值。
- var() 插入自定义属性的值。
var()
上面很多属性我们都比较熟知,但var()
是如何应用的呢?假设有一个这样的实际生产环境
div {
font-size: 1em;
padding: 1em 0 0 1em;
margin: 1em;
border: .1em solid #aaa;
color: #aaa;
}
// 通过 var()函数,只需要做这样的操作
div {
--size: 1em;
--color: #aaa;
font-size: var(--size);
padding: var(--size) 0 0 var(--size);
margin: var(--size);
border: calc(var(--size) * 0.1) solid var(--color);
color: var(--color);
}
因此,无论是在数值、动画和颜色等任何定义的值,都可以自定义插入。这样做的好处就是你只需要更改一个属性就可以应用到所有调用的属性上。例如我在鼠标飘过后通过:hover
更换一个颜色时,就可以直接使用下方的例子:
div:hover {
--color: #fff;
}
一个显著的作用就是为我们带来简便又简短的代码量,如果配合 js 变化 css 属性,就会实现更多效果。你会发现在上述举例的函数中出现了 calc(),这是一个数学函数,我们接着看下一个章节来了解。
数学函数的应用
CSS 数学函数允许在 CSS 属性值中执行数学表达式,它支持加法(+)、减法(-)、乘法(*)、除法(/)。它能够让我们根据不同情况来自动计算数值,在制作自适应页面时尤为重要。目前在 CSS 中有四个得到很好支持的数学函数。
数学函数列表
- calc():该函数允许在声明 CSS 属性值时执行一些计算;
- min():该函数允许你从逗号分隔符表达式中选择一个最小值作为 CSS 的属性值;
- max():该函数可以从一个逗号分隔的表达式列表中选择最大(正方向)的值作为属性的值;
- clamp():该函数的作用是把一个值限制在一个上限和下限之间,当这个值超过最小值和最大值的范围时,在最小值和最大值之间选择一个值使用。它接收三个参数:最小值、首选值、最大值。
calc
calc() 函数用一个表达式作为它的参数,用这个表达式的结果作为值。这个表达式可以是任何如下操作符的组合(+、-、*、/),采用标准操作符处理法则的简单表达式。在表达式中的运算对象可以使用任意长度的值,因此该函数具有广泛的用途,例如,一个 div 需要占据全屏大小,但又希望高度能够减少 100px,那么就可以如下使用:
div {
height: calc(100vh - 100px)
}
你会发现在这里使用了不同单位,所以可以在一个表达式中混用这类值的不同单位。在需要时,还可以使用小括号来建立计算顺序。因为 calc() 函数支持嵌套,把被嵌套的 calc() 函数全当成普通的括号。所以,函数内直接用括号就好了。例如使用 2 倍的 100px 时,就可以使用calc(100vh - (100px * 2))
。
注意事项
1. + 和 - 运算符的两边必须要有空白字符。 比如,calc(50% -8px) 会被解析成为一个无效的表达式,解析结果是:一个百分比后跟一个负数长度值。而加有空白字符的、有效的表达式 calc(8px + -50%)会被解析成为:一个长度后跟一个加号再跟一个负百分比。
2. * 和 / 这两个运算符前后不需要空白字符,但如果考虑到统一性,仍然推荐加上空白符。
3. 用 0 作除数会使 HTML 解析器抛出异常。
4. 涉及自动布局和固定布局的表格中的表列、表列组、表行、表行组和表单元格的宽度和高度百分比的数学表达式,auto 可视为已指定。
min()
min() 函数方法接受一个或多个用逗号分隔的表达式作为他的参数,数值最小的表达式的值将会作为指定的属性的值。假设有一个这样的实际生产环境:
div {
width: min(1000px, 100vw);
}
例一:已知浏览器窗口宽度是 1920px,那么最后 div 计算出的宽度值是 1000px。
例二:已知浏览器窗口宽度是 800px,那么最后 div 计算出的宽度值是 800px。
通过上述的举例,你可以很好理解 min() 。通俗地讲就是设定一些值,让 min() 函数根据定义的值选取最小的值使用。所以我们在定义的值中选取大值,以便函数选择。
max()
max() 函数方法接受一个或多个用逗号分隔的表达式作为他的参数,数值最大的表达式的值将会作为指定的属性的值。假设还是上述环境:
div {
width: max(1000px, 100vw);
}
例一:已知浏览器窗口宽度是 1920px,那么最后 div 计算出的宽度值是 1920px。
例二:已知浏览器窗口宽度是 800px,那么最后 div 计算出的宽度值是 1000px。
它和 min()的原理相同,只不过选取较大值。
clamp()
clamp(MIN, VAL, MAX) 其实就是表示 max(MIN, min(VAL, MAX))。clamp() 函数接收三个用逗号分隔的表达式作为参数,按最小值、首选值、最大值的顺序排列,这三个值的顺序很重要。
- 当首选值比最小值要小时,则使用最小值。
- 当首选值介于最小值和最大值之间时,用首选值。
- 当首选值比最大值要大时,则使用最大值。
这个表达式可以是数学函数、字面量或其它计算为有效的参数类型表达式,如 attr(),或嵌套的 min 和 max 。作为数学表达式,可以使用加减乘除运算而无需使用 calc() 函数。也可以用括号来确定计算顺序。表达式中的每一个值都可以用不同的单位。假设有一个这样的实际生产环境:
div {
width: clamp(100px, 50vw, 1000px);
}
已知浏览器窗口宽度是 1800px,那么最后 div 计算出的宽度值是 900px。
计算过程如下:
通过 width: max(100px, min(50vw, 1000px));
算出 width: max(100px, min(900px, 1000px));
算出 width: max(100px, 900px);
算出 width: 900px;
这个元素的宽度永远不会低于 100px,首选值是 50vw 并且仅在视口宽度大于 100px 和小于 1000px 时才有效,宽度不会超过 1000px。
数学函数兼容性
新选择器的使用
在编写 css 时,有时会需要使用很长的选择器列表来定位具有相同样式的子元素。比如假设下方生产环境中,旧写法看起来很长,可读性差。此时,可以使用新写法来提高易读性,同时避免使用长选择器。
is()和 where()
// 旧写法
h1 span, h2 span, h3 span, h4 span, h5 span {
display: block;
}
// 新写法
:is(h1, h2, h3, h4, h5) span {
display: block;
}
// 新写法
:where(h1, h2, h3, h4, h5) span {
display: block;
}
你会发现:
的写法相同,它们的区别体现在选择器的权重上。is()
和:where():where()
选择器没有权重,也就是权重是 0,而is()选择器的权重由它的选择器列表中的最高权重决定。虽然,它们有自己的权重规则,但无论:is()
还是:where()
都以 css 规定的权重基础上遵从其他规则。假如有以下选择器:
*:where(h1, h2, h3, h4, h5) span {
color: red !important;
}
:is(h1, h2, h3, h4, h5) span {
color: green;
}
body :is(h1, h2, h3, h4, h5) span {
color: grey;
}
body :where(h1, h2, h3, h4, h5) span {
color: black;
}
那么,请问最后 span 是什么颜色呢?
not()
它的作用是匹配每个元素是不是指定的元素/选择器。假设有一个这样的实际生产环境:
<div class="a b c">100</div>
<nav class="a b">200</nav>
<div class="a c">300</div>
// 例一:有 a 是红色,有 c 不是。
.a:not(.c) {
color: red;
}
// 结果:200 是红色
// 例二:有 a 的 div 是红色,有 b 不是。
div.a:not(.b) {
color: red;
}
// 结果:300 是红色
// 例三:有 a 是红色,有 nav 和 c 不是。
.a:not(nav, .c) {
color: red;
}
// 结果:没有红色
has()
在 :has() 之前,选择器的主体总是在最后。例如,这个选择器的主体是一个列表项:ul > li。伪选择器可以改变选择器,但它们不会改变主体:ul > li:hover 或 ul > li:not(.selected)。
在 :has() 之后,元素列中较高的主体可以保留为主体,同时提供有关子项的查询:ul:has(> li)。很容易理解 :has() 是如何获得“父选择器”的通用名称的,因为在这种情况下,选择器的主体现在是父级。
因为兼容性不良,因此在这里只做普通介绍不过多阐述。
选择器兼容性
结语
愉快的第二期结束了,你会发现 CSS 不断地进步,使其运用趋于理解和易读,这也使得使用者的门槛会越来越低。虽然 CSS 属性不断增加,但也更重要地减少了代码量。文中那道题最后的答案是 red,你答对了吗?
他们的权重按小到大的排序如下:
*:where(h1, h2, h3, h4, h5) span
:is(h1, h2, h3, h4, h5) span
body :where(h1, h2, h3, h4, h5) span
body :is(h1, h2, h3, h4, h5) span
*:where(h1, h2, h3, h4, h5) span // 属性带 !important