现代CSS解决方案:CSS数学函数

如题所述

第1个回答  2024-09-04

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

Transformfunctions

Mathfunctions

Filterfunctions

Colorfunctions

Imagefunctions

Counterfunctions

Fontfunctions

Shapefunctions

Referencefunctions

CSSgridfunctions

本文,将具体介绍其中的CSS数学函数(Mathfunctions)中,已经被浏览器大规模支持的4个:

calc()

min()

max()

clamp()

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

Calc()

calc()此CSS函数允许在声明CSS属性值时执行一些计算。

语法类似于

{width:calc(100%-80px);}

一些需要注意的点:

+和-运算符的两边必须要有空白字符。比如,calc(50%-8px)会被解析成为一个无效的表达式,必须写成calc(8px+-50%)

*和/这两个运算符前后不需要空白字符,但如果考虑到统一性,仍然推荐加上空白符

用0作除数会使HTML解析器抛出异常

涉及自动布局和固定布局的表格中的表列、表列组、表行、表行组和表单元格的宽度和高度百分比的数学表达式,auto可视为已指定。

calc()函数支持嵌套,但支持的方式是:把被嵌套的calc()函数全当成普通的括号。(所以,函数内直接用括号就好了。)

calc()支持与CSS变量混合使用

看一个最常见的例子,页面结构如下:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>

页面的g-footer高为80px,我们希望不管页面多长,g-content部分都可以占满剩余空间,像是这样:

这种布局使用flex的弹性布局可以轻松实现,当然,也可以使用calc()实现:

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}

下面罗列一些Calc()的进阶技巧。

Calc中的加减法与乘除法的差异

注意,calc()中的加减法与乘除法的差异:

{font-size:calc(1rem+10px);width:calc(100px+10%);}

可以看到,加减法两边的操作数都是需要单位的,而乘除法,需要一个无单位数,仅仅表示一个倍率:

{width:calc(100%/7);animation-delay:calc(1s*3);}

Calc的嵌套

calc()函数是可以嵌套使用的,像是这样:

{width:calc(100vw-calc(100%-64px));}

此时,内部的calc()函数可以退化写成一个括号即可(),所以上述代码等价于:

{width:calc(100vw-(100%-64px));}

也就是嵌套内的calc(),calc几个函数字符可以省略。

Calc内不同单位的混合运算

calc()支持不同单位的混合运算,对于长度,只要是属于长度相关的单位都可以进行混合运算,包含这些:

px

%

em

rem

in

mm

cm

pt

pc

ex

ch

vh

vw

vmin

vmax

这里有一个有意思的点,运算肯定是消耗性能的,早年间,有这样一段CSS代码,可以直接让Chrome浏览器崩溃Crash:

<div></div>

CSS样式如下:

div{--initial-level-0:calc(1vh+1%+1px+1em+1vw+1cm);--level-1:calc(var(--initial-level-0)+var(--initial-level-0));--level-2:calc(var(--level-1)+var(--level-1));--level-3:calc(var(--level-2)+var(--level-2));--level-4:calc(var(--level-3)+var(--level-3));--level-5:calc(var(--level-4)+var(--level-4));--level-6:calc(var(--level-5)+var(--level-5));--level-7:calc(var(--level-6)+var(--level-6));--level-8:calc(var(--level-7)+var(--level-7));--level-9:calc(var(--level-8)+var(--level-8));--level-10:calc(var(--level-9)+var(--level-9));--level-11:calc(var(--level-10)+var(--level-10));--level-12:calc(var(--level-11)+var(--level-11));--level-13:calc(var(--level-12)+var(--level-12));--level-14:calc(var(--level-13)+var(--level-13));--level-15:calc(var(--level-14)+var(--level-14));--level-16:calc(var(--level-15)+var(--level-15));--level-17:calc(var(--level-16)+var(--level-16));--level-18:calc(var(--level-17)+var(--level-17));--level-19:calc(var(--level-18)+var(--level-18));--level-20:calc(var(--level-19)+var(--level-19));--level-21:calc(var(--level-20)+var(--level-20));--level-22:calc(var(--level-21)+var(--level-21));--level-23:calc(var(--level-22)+var(--level-22));--level-24:calc(var(--level-23)+var(--level-23));--level-25:calc(var(--level-24)+var(--level-24));--level-26:calc(var(--level-25)+var(--level-25));--level-27:calc(var(--level-26)+var(--level-26));--level-28:calc(var(--level-27)+var(--level-27));--level-29:calc(var(--level-28)+var(--level-28));--level-30:calc(var(--level-29)+var(--level-29));--level-final:calc(var(--level-30)+1px);border-width:var(--level-final);border-style:solid;}

可以看到,从--level-1到--level-30,每次的运算量都是成倍的增长,最终到--level-final变量,展开将有2^30=1073741824个--initial-level-0表达式的内容。

并且,每个--initial-level-0表达式的内容--calc(1vh+1%+1px+1em+1vw+1cm),在浏览器解析的时候,也已经足够复杂。

混合在一起,就导致了浏览器的BOOM(Chrome70之前的版本),为了能看到效果,我们将上述样式赋给某个元素被hover的时候,得到如下效果:

当然,这个BUG目前已经被修复了,我们也可以通过这个小DEMO了解到,一是calc是可以进行不同单位的混合运算的,另外一个就是注意具体使用的时候如果计算量巨大,可能会导致性能上较大的消耗。

当然,不要将长度单位和非长度单位混合使用,像是这样:

{animation-delay:calc(1s+1px);}

Calc搭配CSS自定义变量使用

calc()函数非常重要的一个特性就是能够搭配CSS自定义以及CSS@property变量一起使用。

最简单的一个DEMO:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>0

当然,这样看上去,根本看不出这样的写法的作用,好像没有什么意义。实际应用场景中,会比上述的DEMO要稍微复杂一些。

假设我们要实现这样一个loading动画效果,一开始只有3个球:

可能的写法是这样,我们给3个球都添加同一个旋转动画,然后分别控制他们的animation-delay:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>1

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>2

如果有一天,这个动画需要扩展成5个球的话,像是这样:

我们就不得已,得去既添加HTML,又修改CSS。而如果借助Calc和CSS变量,这个场景就可以稍微简化一下。

假设只有3个球:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>3

我们通过HTML的Style标签,传入--delay变量,在CSS中直接使用它们:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>4

而当动画修改成5个球时,我们就不需要修改CSS,直接修改HTML即可,像是这样:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>5

核心的CSS还是这一句,不需要做任何修改:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>6

完整的DEMO,你可以戳这里:CodePenDemo--Calc&CSSVariableDemo

calc搭配自定义变量时候的默认值

还是上述的Loading动画效果,如果我的HTML标签中,有一个标签忘记填充--delay的值了,那会发生什么?

像是这样:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>7

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>6

由于HTML标签没有传入--delay的值,并且在CSS中向上查找也没找到对应的值,此时,animation-delay:calc(var(--delay)*-1s)这一句其实是无效的,相当于animation-delay:0,效果也就是少了个球的效果:

所以,基于这种情况,可以利用CSS自定义变量var()的fallback机制:

<divclass="g-container"><divclass="g-content">Content</div><divclass="g-footer">Footer</div></div>9

此时,如果没有读取到任何--delay值,就会使用默认的1与-1s进行运算。

Calc字符串拼接

很多人在使用CSS的时候,会尝试字符串的拼接,像是这样:

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}0

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}1

这里想利用calc(var(--urlA)+var(--url)+var(--urlB))拼出完整的在background-image中可使用的URLurl(https://s1.ax1x.com/2022/03/07/bsBD1I.png)。

然而,这是不被允许的(无法实现的)。calc的没有字符串拼接的能力。

唯一可能完成字符串拼接的是在元素的伪元素的?content属性中。但是也不是利用calc。

来看这样一个例子,这是错误的:

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}2

此时,不需要calc,直接使用自定义变量相加即可。

因此,正确的写法:

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}3

此时,内容可以正常展示:

再强调一下,calc的没有字符串拼接的能力,如下的使用方式都是无法被识别的错误语法:

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}4

min()、max()、clamp()

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

max():从一个逗号分隔的表达式列表中选择最大(正方向)的值作为属性的值

min():从一个逗号分隔的表达式列表中选择最小的值作为属性的值

clamp():把一个值限制在一个上限和下限之间,当这个值超过最小值和最大值的范围时,在最小值和最大值之间选择一个值使用

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

譬如这样一个布局:

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}5

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}6

效果如下,.container块它会随着屏幕的增大而增大,始终占据整个屏幕:

对于一个响应式的项目,我们肯定不希望它的宽度会一直变大,而是当达到一定的阈值时,宽度从相对单位变成了绝对单位,这种情况就适用于min(),简单改造下代码:

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}7

容器的宽度值会在width:100%与width:500px之间做选择,选取相对小的那个。

在屏幕宽度不足500px时候,也就表现为width:100%,反之,则表现为width:500px:

同理,在类似的场景,我们也可以使用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一起使用。

譬如:

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}8

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

.g-container{height:100vh;}.g-content{height:calc(100vh-80px);}.g-footer{height:80px;}9

基于max、min模拟clamp

现在,有这样一种场景,如果,我们又需要限制最大值,也需要限制最小值,怎么办呢?

像是这样一个场景,**字体的大小,最小是12px,随着屏幕的变大,逐渐变大,但是为了避免老人机现象(随着屏幕变大,无限制变大),我们还需要限制一个最大值20px。

我们可以利用vw来实现给字体赋动态值,假设在移动端,设备宽度的CSS像素为320px时,页面的字体宽度最小为12px,换算成vw即是320/100=3.2,也就是1vw在屏幕宽度为320px时候,表现为3.2px,12px约等于3.75vw。

同时,我们需要限制最大字体值为20px,对应的CSS如下:

{font-size:calc(1rem+10px);width:calc(100px+10%);}0

看看效果:

通过max()、min()的配合使用,以及搭配一个相对单位vw,我们成功的给字体设置了上下限,而在这个上下限之间实现了动态变化。

当然,上面核心的这一段max(12px,min(3.75vw,20px))看上去有点绕,因此,CSS推出了clamp()简化这个语法,下面两个写法是等价的:

{font-size:calc(1rem+10px);width:calc(100px+10%);}1

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,随着屏幕的不断变大或者缩小,内容元素将会一直变大变小下去,这也导致了在大屏幕下,许多元素看着实在太大了!

因此,我们需要一种能够控制最大、最小阈值的方式,像是这样:

此时,clamp就能非常好的派上用场,还是我们上述的例子,这一段代码font-size:clamp(12px,3.75vw,20px),就能将字体限制在12px-20px的范围内。

因此,对于移动端页面而言,所有涉及长度的单位,我们都可以使用vw进行设置。而诸如字体、内外边距、宽度等不应该完全等比例缩放的,采用clamp()控制最大最小阈值。

在ModernFl

logo设计

创造品牌价值

¥500元起

APP开发

量身定制,源码交付

¥2000元起

商标注册

一个好品牌从商标开始

¥1480元起

公司注册

注册公司全程代办

¥0元起

    官方电话官方服务
      官方网站八戒财税知识产权八戒服务商企业需求数字市场

相关了解……

你可能感兴趣的内容

大家正在搜

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 非常风气网