现代 CSS

图解CSS: CSS中的函数

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

一直以来大家都不把CSS当作一门编程语言,是因为他不像其他和程序语言一样具有程序语言的特性,比如变量、函数、逻辑运算等。但随着CSS不断的变革,CSS也越来越像一门程序语言,比如CSS也有变量、逻辑操作和函数等特性。虽然说这些特性不像其他程序语言那么强大,但他实实在在的在变化,而且有了一定的进步。今天主要和大家来聊CSS中的函数。

简介

CSS中的函数不像其他功能模块有自己独立的规范,但是他真的存在于CSS中,而且大家在平时编写CSS的时候已经用到过了CSS函数。那么,CSS中的函数是什么呢?为了解开这个迷,我们先从其他的程序语言开始,比如我们前端最熟悉不过的JavaScript语言。

在一些编程语言中,函数可以用来执行特定任务的,而这些执行特定任务的代码都放置在一个花括号{}块中。就拿JavaScript来说吧,我们有一个helloWorld()函数:

function helloWorld() {
    alert('Hello, CSS and JavaScript')
}

helloWorld()

执行helloWorld()函数后会弹出一个对话框:

前端开发者都知道,JavaScript中还可以给函数传递一些参数,这些参数是一些数据或文本,并且可以在函数的逻辑中输出。比如上面的函数,我们给其传递一个参数name

function helloWorld (name) {
    alert(`Hi, ${name}, welcome!`)
}

helloWorld('大漠')

这个时候大漠就是传递给函数helloWorldname

在JavaScript中编写函数,主要是帮助我们做一些重复性的工作。

在CSS中,也有函数,但他的函数构建和JavaScript这样的编程语言有所不同。CSS的函数同样会返回一个值,但这些值主要是赋值给CSS属性的,最终将是CSS属性的值。换句话说,函数返回的结果赋值给不同的CSS属性时,所起的作用也将不同。就拿CSS中最早的函数之一calc()来说吧,如果赋值给width那么是用来指定宽度的值,如果给border-width,那么是用来指定边框粗细的。

另外,CSS中的函数相对于程序语言中的函数来说要简单的多,因为CSS函数中不会带有复杂逻辑处理,最多也就是一个表达式,比如calc(10vw + 10px)。这样一来,CSS中的函数要简单地多。

虽然CSS的函数简单,但根据不同的功能特性,其类型也多。接下来,我们就来和大家聊聊CSS中常见的一些函数。

CSS函数的基本特征

CSS的函数有着其自身的特征,简单地说,CSS中带有小括()的一般会被认为是CSS的函数。在CSS中使用()的地方主要有CSS选择器CSS属性值。比如:

虽然CSS不能创建自己的函数(自定义函数),也不能具备复杂的逻辑能力,但CSS函数也具备一些简单的逻辑处理能力。比如上图中的:not()选择器(也称为函数),就可以过滤掉.active的类名。

另外,CSS函数不像其他程序语言的函数,它的输出本质上是可视的。这个输出主要用于控制CSS属性的值,从而影响元素的样式效果。例如:

a:hover {
    filter: drop-shadow(.25rem 0 .75em #f36)
}

上面示例使用的是CSS滤镜中的阴影(drop-shadow()),当鼠标悬浮到元素上是会有一个发光效果。

如果要用一句话来描述CSS函数的话,那么可以是:

CSS函数主要服务于CSS的选择器和属性,它有着一个最基本的特性,那就是带有一个小括号()标记符,在括号内可以放置值(参数)

CSS函数类型

时至今日,W3C规范中并没有一份独立的规范来描述CSS函数,但这些函数的确存在,并且散落在不同的功能模块中

我根据功能类型,对CSS函数做了一个简单的类型划分,如下图所示:

上图中的函数大部分都有档可查,W3C规范对其做出详细的描述。其中有很多是我们熟悉的,比如伪类选择器函数渐变函数颜色函数滤镜函数等,当然也有我们不太熟悉(甚至从未见过的)函数,比如数学函数其他函数所列的。

接下来,我们会简单的介绍这些函数,以及其出处,但不会详细的介绍每个函数的作用和使用。有关于它们更详细的描述,建议查阅规范中对应的描述。

常见的CSS函数

我们从最常见的几个函数开始。这几个函数是特意上图中提出来的,因为在还没有提CSS函数概念的时候,他们就出现在我们的代码中,而且使用频率也较高。这几个函数就是url()attr()calc():lang():dir()

url()

url()函数来自CSS Images Module

我想最早接触到url()应该是在给元素添加背景图片的时候,换句话说,就是通过url()函数一加载图像资源:

.element {
    background-image: url(wavy.png);
}

但在今天的CSS中,url()不再局限于background-image属性中,还可以用于:

background-image: url("https://mdn.mozillademos.org/files/16761/star.gif");
list-style-image: url('../images/bullet.jpg');
content: url("pdficon.jpg");
cursor: url(mycursor.cur);
border-image-source: url(/media/diamonds.png);
src: url('fantasticfont.woff');
offset-path: url(#path);
mask-image: url("masks.svg#mask1");
@font-face {
    font-family: "Open Sans";
    src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
        url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
}

简单地说,在CSS中可以使用url()函数来引用相应的资源,有点类似于HTML中的srchref属性。

attr()

attr()函数来自CSS Values and Units Module

原则上说,attr()能运用于所有的CSS属性,但目前仅能服务于CSS的伪元素::before::aftercontent属性。该函数可以用来获取到HTML元素的属性的值(比如data-*属性)。最为常见的示例就是构建Tooltips

[tooltip]::after {
    content: attr(tooltip); 
}

在一些场景之中,我们可以让Web的体验更佳,比如图片加载失败,让UI上更美:

另外有一个场景,attr()函数非常有用,即“在打印页面的时候,希望链接地址能打印出来”。实现该效果我们就可以借助attr()函数来完成:

@media print {
    a[href]:after {
        content: " (" attr(href) ")";
    }
}

另外还可以将相对链接,锚点做一些处理:

@media print {
    a[href^="http"]:not([href*="mywebsite.com"]):after {
        content: " (" attr(href) ")";
    }
}

你可以尝试打开上例,然后按下command + p快捷键(MacOS系统),你会看到非打印状态和打印状态下的不同:

calc()

calc()函数来自CSS Values and Units Module

calc()到来之前,CSS不能像其他程序语言去做一些计算操作。那么自calc()来到我们的身边之后,他具备一些简单的数学四则运算,即可以在calc()函数中进行加(+减(-乘(*除(/的运算,而且也可以添加小括号()来控制其运算的顺序。

有了calc()之后,在一些动态计算的场景就变得容易地多。比如对安全区域的计算,calc()让事情变得简单地多

@supports (padding-top: constant(safe-area-inset-top)) {
    .main {
        padding-top: calc(var(--safe-area-inset-top) + 88px);
    }
    .detail {
        background-position: 0 calc(var(--safe-area-inset-top) + 395px), 0 0;
    }
}

@supports (padding-top: env(safe-area-inset-top)) {
    .main {
        padding-top: calc(var(--safe-area-inset-top) + 88px);
    }
    .detail {
        background-position: 0 calc(var(--safe-area-inset-top) + 395px), 0 0;
    }
}

时至今日,calc()的使用场景很多,比如我们构建响应式设计时,font-size大小加把锁的时候calc()就起到在关键性的作用:

@media (min-width: 320px) and (max-width: 959px) { 
    h1 { 
        font-size: calc( 1.5rem + 16 * (100vw - 320px) / (960 - 320) ); 
        /* 对于负的斜率,需要对断点求倒数 */ 
        line-height: calc( 1.2em + 3.2 * (100vw - 960px) / (320 - 960) ); 
    } 
}

特别是随着CSS自定义属性的出现,calc()的作用就更大了,给开发者带来的便利性就更强了,比如在《图解CSS: CSS 颜色》一文中提到的,可以使用calc()和CSS自定义属性,灵活的实现颜色反色效果:

:root {
    --red: 28;
    --green: 150;
    --blue: 130;

    --accessible-color: calc(
        (
        (
            (
            (var(--red) * 299) +
            (var(--green) * 587) +
            (var(--blue) * 114)
            ) / 1000
        ) - 128
        ) * -1000
    );
}

:lang()

:lang()来自于Selectors Level 4

如果你平时的业务有涉及到国际业务,那么对于语言的使用应该有较高的要求。

作为Web开发者,都知道,可以在HTML的元素中通过lang属性来指定文档语言的类型,比如:

<html lang="en"></html>
<html lang="zh-CN"></html>

也可以在除<html>元素上的其他元素上显式指定lang属性:

<div lang="en"><q>This English quote has a <q>nested</q> quote inside.</q></div>
<div lang="fr"><q>This French quote has a <q>nested</q> quote inside.</q></div>
<div lang="de"><q>This German quote has a <q>nested</q> quote inside.</q></div>

在CSS中,我们可以通过:lang()函数(常称为伪类选择器),根据不同语言类型,设置不同的样式效果:

:lang(en) > q { 
    quotes: '\201C' '\201D' '\2018' '\2019'; 
}

:lang(fr) > q { 
    quotes: '« ' ' »'; 
}

:lang(de) > q { 
    quotes: '»' '«' '\2039' '\203A'; 
}

:lang(ko) > q {
    quotes: "\300C" "\300D" "\300E" "\300F" "\300C" "\300D" "\300E" "\300F";
}

:dir()

:dir()来自Selectors Level 4

从《Web中向左向右》一文中我们可以得知,不同的语言类型有关不同的布局方式,特别是方向性有明确的要求,这样做主要是便于用户的阅读。比如我们熟悉的英文,它的阅读方式都是从左往右(ltr;对于阿拉伯语系,它的阅读方式都是从右往左(rtl

在HTML中,我们可以显式地使用dir属性指定其阅读方式(ltrrtl)。

<div dir="rtl">
    <span>test1</span>
    <div dir="ltr">test2
        <div dir="auto">עִבְרִית</div>
    </div>
</div>

本例中 :dir(rtl) 会匹配最外层的div,内容为test1span,和有希伯来字符的div:dir(ltr) 会匹配到内容为test2div

同样的,在CSS中,我们可以使用:dir()函数来为不同的阅读方式指定不同的样式。

:dir(rtl) { 
    color: red; 
}

:dir(ltr) { 
    color: blue; 
}

请使用Firefox打开上面的Demo,你将看到:dir()函数对应的样式效果:

url()calc():lang():dir()四个基础函数中可以得知,CSS函数可以返回一定的值,返回的值主要服务于CSS的选择器和CSS属性。就目前为止,伪类选择器函数主要用于CSS的选择器,用来过滤元素;除伪类选择器之外的其他函数主要用于CSS的属性,作为属性的值。不管哪类函数,都可以在()传递一定的参数值(参数可以是单一的,也可以是多个的,还可以是表达式)。

自定义属性函数

自定义属性函数又称为CSS的变量函数,来自于CSS Custom Properties for Cascading Variables Module Level 1

熟悉CSS的前端开发者都知道,早期的CSS不像其他程序语言有变量的概念。自CSS Custom Properties for Cascading Variables Module Level 1出现,在CSS中也有了变量的概念。

在现代CSS中,可以使用双破折线--在选择器中声明自定义属性:

:root {
    --color: #f36;
}

selector {
    --color: #f36;
}

正如上面示例中所示--color,被称为CSS自定义属性。不过,这个时候--color仅仅是声明了,并不能产生任何效果,也没有运用于任何属性上。只有使用var()函数将已声明的自定义属性运用于CSS属性时,自定义属性才有效,而且这个运用于var()函数中的自定义属性此时也被称为CSS变量

var()函数的语法规则如下:

var() = var( <custom-property-name> [, <declaration-value> ]? )

其中<custom-property-name>是声明的自定义属性,<declaration-value>是个可选值,如果提供了,这个值就是一个回退值,当<custom-property-name>未生效时,<declaration-value>就会起作用。var()函数的使用很简单,比如:

:root {
    --color: #f36;
}

button {
    color: var(--color)
}

button:hover {
    color: var(--color, #fff)
}

var()函数还可以这么简单的理解,*var()函数将CSS自定义属性作为参数引入,并且返回的值可以运用于CSS的属性上,即用var()函数替换值的任何部分。但要注意,var()函数不能用于属性名,选择器或属性值以外的任何东西。

var()函数还可以相互嵌套,比如color: var(var(--color, #fff), #000),但不建议这样使用,因为这样会让代码难于阅读,也不利于维护。

var()函数还可以和其他的函数相互嵌套使用,比如calc()min()max()clamp()函数等,而且它们的结合,功能会变得更强大。就拿和calc()函数的结合使用为例吧。

:root {
    --delay: 0;
    --duration: 2;
    --stagger-step: 0;
    --coefficient: 1;
    --offset: 0;
}

.block{
    animation: party calc(var(--duration, 1) * 1s) linear infinite;
    animation-delay: calc((((var(--delay, 0) + (var(--index) * var(--stagger-step))) + var(--offset)) * var(--coefficient)) * 1s);
}

.reversed .block {
    animation-delay: calc((((var(--delay, 0) + ((5 - var(--index)) * var(--stagger-step))) + var(--offset)) * var(--coefficient)) * 1s);
}

安全区域函数

env()函数来自于CSS Environment Variables Module Level 1

env()函数最早是上苹果公司的研发团队提出来的,主要用来处理像iPhone X、XR、Xmax等设备安全区域2018年纳入到W3C规范中

env()函数以类似于 var() 函数和自定义属性的方式将用户代理定义的环境变量值插入你的 CSS 中。区别在于,环境变量除了由用户代理定义而不是由用户定义外,还被全局作用在文档中,而自定义属性则限定在声明它们的元素中。

为了告诉浏览器使用屏幕上所有的可用空间,并以此使用env()变量,我们需要添加一个新的视口元值:

body {
    padding:
        env(safe-area-inset-top, 20px)
        env(safe-area-inset-right, 20px)
        env(safe-area-inset-bottom, 20px)
        env(safe-area-inset-left, 20px);
}

另外,与自定义属性不同,自定义属性不能在声明之外使用,而env()函数可以代替属性值或描述符的任何部分(例如,在 媒体查询的规则 中)。 随着规范的发展,它也可能在像是 选择器 等其他地方使用。

另外,evn()函数还允许你创建条件逻辑,如果与设备的用户代理相匹配,就会触发该逻辑。典型的案例就是处理移动设备安全区域(Notch)的方法。换句话说,采用env()函数来做设备嗅探并不是一个科学的方法(也有人认为这是一个错误的事情)—— 不应该认为env()是欺骗它的方法。相反,按照预期使用它,确保你的设计适用于那些在视口上施加唯一硬件约束的设备。

@supports (padding-top: constant(safe-area-inset-top)) {
    .header {
        padding-top: var(--safe-area-inset-top);
    }
}

@supports (padding-top: env(safe-area-inset-top)) {
    .header {
        padding-top: var(--safe-area-inset-top);
    }
}

网格函数

fit-content()minmax()repeat()函数来自于CSS Grid Layout Module,其中fit-content()函数也在CSS Intrinsic & Extrinsic Sizing Module

fit-content()minmax()repeat()几个函数用于CSS Grid布局中,可以让布局更为灵活,同时也从另一面体现CSS Grid布局的强大之处。

fit-content()

如果显式指定了内联轴,使用了fit-content()函数,可以用指定的参数替换可用空间,即 min(max-content, max(min-content, <length-percentage>));否则将表现为属性的 初始值。对于内在尺寸,fit-content(<length>)表现长度值(length)。如果fit-content()使用了百分比值的话,将会作为min-content当作最小内容,max-content作为最大内容。

这个函数可以在CSS Grid布局中用作轨迹尺寸比如grid-template-columns,其中最大尺寸由max-content定义,最小尺寸由auto定义,计算方式类似于auto(即minmax(auto, max-content)),但如果参数的轨道尺寸大于autoauto minmum),则在参数处对轨道尺寸进行限制。

我们来看一个简单的示例:

.grid {
    display: grid;
    grid-template-columns: fit-content(250px) 1fr;
}

在这个示例中,给fit-content()函数传了一个250px的参数(是一个<length>值),它将会告诉浏览器限制网格第一列的宽度为250px(最大宽度),网格的内容会到容器边缘会自动换行。

我们来再看另一个案例:

.grid {
    display: grid;
    grid-template-columns: fit-content(400px) fit-content(400px) 1fr;
    gap: 10px;
}

我们虽然在网格的第一列和第二列使用fit-content()函数指定列宽为400px,但第一列的内容自动计算(auto)的宽度并未达到这个值(400px),这个时候第一列的宽度即是内容自动填充的宽度(auto),而第二列内容自动宽度超出400px,那么内容到列的边缘会自动换行。你可以尝试着拖动浏览器视窗大小,当拖动到足够小时,第一列和第二列将会以max-content来计算。

minmax()

minmax()函数定义了一个大小范围的闭区间,该范围大于或等于最小值,小于或等于最大值。minmax()函数接受两个参数:minmax(min, max),如果max < min,则忽略max,将minmax(min, max)视为min<flex>值(fr)作为最大值时设置网络轨道的弹性系数,作为最小值时无效。

minmax()函数中的minmax两个参数值都可以接受下面几个方式的值:

min = <length> | <percentage> | min-content | max-content | auto
max =  <length> | <percentage> | <flex> | min-content | max-content | auto

其中<length><percentage>比较好理解,就是我们熟悉的固定长度值和百分比值,另外几个是指:

  • <flex>网格布局独有的单位分值fr,它是一个系数值,每个fr的轨道都以其系数值比例均分剩余空间,换句话说,1fr代表网格容器中可用空间的一等份。
  • max-content:表示网格的轨道长度自适应内容最大的那个单元格
  • min-content:表示网格的轨道长度自适应内容最小的那个单元格
  • auto:作为最大值时,等价于max-content;最为最小值时,它表示轨道中单元格最小长宽(由min-width、min-height)的最大值

特别声明,CSS的min-contentmax-contentminmax()fit-content()auto以及min-width/heightmax-width/height构建一个复杂的尺寸体系,如果你对这方面感兴趣的话,可以阅读《图解CSS: 元素尺寸的设置》一文

在网格布局中,minmax()函数和min-contentmax-contentauto可以让网格布局的能力更大,灵活性更强。

.grid {
    display: grid;
    grid-template-columns: minmax(100px, 200px) minmax(min-content, 30%) 1fr minmax(min-content, 300px) minmax(min-content, max-content);
}

上面的示例:

  • 第一列minmax(100px, 200px): 最小宽度是100px,最大宽度是200px
  • 第二列minmax(min-content, 30%): 最小宽度是min-content,内容的最小单元格宽度,最大宽度是容器宽度的30%
  • 第三列1fr: 网格容器剩余空间的1等份
  • 第四列minmax(min-content, 300px): 最小宽度是min-content,内容的最小单元格宽度
  • 第五列minmax(min-content, max-content):最小宽度是min-content,内容的最小单元格宽度(即img的宽度),最大宽度是max-content,内容最大的那个单元格宽度(即img的宽度)

repeat()

在CSS网格布局中,很多时候会用到重复尺寸的网格轨道,比如:

.grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr
}

上面的示例代码告诉浏览器将网格容器分成了四等份,其中每一列的宽度都是1fr。针对这样的场景,我们就可以使用repeat()函数来构建:

.grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr)
}

这是一个简单地示例。

CSS的repeat()函数的语法规则:

repeat( [ <positive-integer> | auto-fill | auto-fit ] , <track-list> )

repeat()函数接受两个参数,第一个参数用来指定重复的次数,它的值可以是具体的数值,也可以是关键词auto-fitauto-fill;第二个在数是指网格轨道列表值(<track-list>)。组合在一起就是用来告诉浏览器,相同的网格轨道重复的次数。

repeat()函数也有一定的限制:

  • repeat()函数不能相互嵌套
  • 自动重复(auto-fitauto-fill)不能与内部尺寸(min-contentmax-contentautofit-content())或弹性的尺寸(比如fr)相结合

因此,repeat()函数语法可以用更精确的方式来描述:

<track-repeat> = repeat( [ <positive-integer> ] , [ <line-names>? <track-size> ]+ <line-names>? )
<auto-repeat>  = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
<fixed-repeat> = repeat( [ <positive-integer> ] , [ <line-names>? <fixed-size> ]+ <line-names>? )

上面一些参数来自于轨道列表,具体的语法规则如下:

<track-list>          = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?

<auto-track-list>     = [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>? <auto-repeat>
                        [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?

<explicit-track-list> = [ <line-names>? <track-size> ]+ <line-names>?

<track-size> = <track-breadth> | minmax( <inflexible-breadth> , <track-breadth> ) | fit-content( <length-percentage> )

<fixed-size> = <fixed-breadth> | minmax( <fixed-breadth> , <track-breadth> ) | minmax( <inflexible-breadth> , <fixed-breadth> )

<track-breadth> = <length-percentage> | <flex> | min-content | max-content | auto

<inflexible-breadth>  = <length-percentage> | min-content | max-content | auto

<fixed-breadth>  = <length-percentage>

<line-names> = '[' <custom-ident>* ']'

在使用的时候,有一定的细节需要注意:

  • <track-repeat>表示任何<track-size>的重复,但仅局限于固定的重复次数
  • <auto-repeat>可以自动重复填充空间,但是需要明确的轨道尺寸(<fixed-size>),以便可以计算重复的数量。它只能在轨道列表中出现一次,但是同一个轨道列表还可以包含多个<fixed-size>

对于auto-fitauto-fill简单的描述

auto-fit会让网格容器剩余空间自动分配到网格项目,让网格项目占用更多可用空间;auto-fill让网格容器尽可能多的放置网格列,但不会将剩余的网格容器空间分配到网格项目。

用一张图来描述,会更形象一点:

有了repeat()minmax()之后,我们可以在不依赖任何媒体查询就可以实现一些简单的响应式布局效果:

网格中的每个列的最小宽度为200px。根据浏览器视窗大小,网格数量会根据共最合理的宽度进行变化。这里使用了CSS的minmax()函数。只需要使用两行CSS代码就可以实现:

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

来看一个具体的案例:

动画函数

cubic-bezier()steps()函数来自CSS Easing Functionspath()ray()函数来自Motion Path Module

在CSS中,我们可以通过animationtransition给Web添加动画效果,这也是Web应用程序或Web页面中一个重要的部分。在创建动画效果时我们需要随着时间的推移控制事物(元素)的状态。因此需要用到时间函数(transition-timing-functionanimation-timing-function),时间函数除了提供easelinearease-inease-outease-in-out关键词之外,还可以使用cubic-bezier()steps()函数。

cubic-bezier()

cubic-bezier()函数是一种缓动函数,由四个实数定义,指定三次贝塞尔曲线的两个控制点P1P2,其终点P0P3分别固定在(0,0)(1,1)处。P1P2x坐标被限制在[0,1]范围内。

不是所有的三次贝塞尔曲线都适合作为缓动函数(Easing Function),因为不是所有的都是数学函数。根据CSS的定义,P0P3固定后,三次贝塞尔曲线是一个函数,因此,当且仅当P1P2的横坐标都在[0,1]范围内时,三次贝塞尔曲线是有效的。

三次贝塞尔曲线与P1P2纵坐标在[0,1]范围外可能会产生跳跃效果。当指定无效的贝塞尔曲线时,CSS将忽略整个属性。

cubic-bezier()语法规则如下:

cubic-bezier(x1, y1, x2, y2)

其中x1y1x2y2<number>,表示定义三次贝塞尔曲线的横坐标,以及P1P2点的纵坐标。x1x2必须在[0,1]范围内,否则无效。

@Lea Verou提供了一个在线的三次贝塞尔曲线在线编辑工具

steps()

steps()函数是<step-easing-function>(还有step-startstep-end)中的一种,也是一种缓动函数,它将输入时间分割成一定数量的长度相等的间隔。它由若干步(<integer>)和步的位置(<step-position>)来定义。其中<step-position>接受四个值:

  • jump-start:对应之前的start,表示左连续函数,因此第一步或跳转发生在动画开始时 (告诉动画跳过起始位置);
  • jump-end:对应之前的end,表示右连续函数,因此最后一步或跳转发生在动画结束时(告诉动画跳过结束位置);
  • jump-both:表示一个右连续函数和一个在连续函数,包括在0%100%标记处的暂停,有效地在动画迭代期间添加一个步骤(起始和结束的两个状态都跳过);
  • jump-none:都没有跳,相反,持有0%100%的标记,每个持续时间为1/n同时保留动画的起始状态和结束状态

用图来描述会更形象一点:

@Dan Wilson的Demo比上来还能更形象的阐述steps()函数取值不同时的效果

使用steps()函数可以将一张雪碧图合成一个帧动画:

具体效果如下:

而且用于时钟的旋转效果也非常方便:

ray()path()

路径动画(offset-path中可以通过ray()函数和path()函数给动画元素按指定的路径运动。

从几何的角度来说,直线上的一点和它一旁的部分所组成的图形称为射线(这是欧几里德几何学中描述)。而offset-path中的ray()也称为射线,不同的是,它以容器的中心为圆心,在容器中嵌入一个能够嵌入的最大圆形,结合自定义的夹角,将圆心与边上的点相连,形成的路径。

ray()函数接受三个参数:

ray() = ray( [ <angle> && <size> && contain? ] )

<angle>和渐变中的<angle>一样,都是轴承角,0度向上,正值代表顺时针旋转。使用<angle>定义的路径,元素盒子使用的极坐标定位。

ray()函数中除了<angle>值,还显式的设置了closest-side值,该值对应的就是ray()函数中的<size>。该值可以用来决定路径的长短,它主要支持以下属性值:

<size> = [ closest-side | closest-corner | farthest-side | farthest-corner | sides ]

具体的含义是:

<size>属性值 描述
closest-side 初始位置到最近一边的距离
closest-corner 初始位置到最近一角的距离
farthest-side 初始位置到最远一边的距离
farthest-corner 初始位置到最远一角的距离
sides 初始位置到交线的距离

ray()中设置了contain参数时,会有两种情况发生:

  • 当在元素上显式的设置了offset-distance,那么该元素会完全包含在路径中
  • 当元素未显式设置offset-distance会导致元素被路径包围,那么路径大小就会最小程度地增加,这样就存在偏移距离

我们来看一个简单地示例:

.plane {
    animation: plane 5s infinite linear;
    offset-distance: 250px;
    offset-rotate: auto  140deg;
    offset-path: ray(0deg farthest-side);
}

@keyframes plane {
    to {
        offset-path: ray(360deg farthest-side);
    }
}

效果如下:

如果你熟悉SVG的path或者曾经在别的地方使用过path()函数(比如clip-path)或者CSS Shapes中使用path()绘制的图形(也可以是路径),那么你对path就不会感到陌生。

.car{
    offset-path: path("M288.4,129.9l-26.6,4.3c-0.8,0.2-1.5,0.6-1.9,1.3l-18.2,26.6c-0.6,0.9-1.6,1.4-2.6,1.3l-78.2-4.9c-1.1-0.1-2.1-0.7-2.5-1.8l-25.6-60.9c-0.6-1.3-2-2-3.4-1.7l-24.7,6.5c-0.5,0.1-1,0.4-1.4,0.9l-56.1,63c-1.1,1.2-3.1,3.9-1.2,6l77.9,68.3c2.4,1.4,1.8,4.9,1.3,6.1l-26.6,59.7c-0.4,0.8-0.3,1.7,0.1,2.5l17.8,35.8l13.4,29.4c0.4,0.5,5.9,3.5,6.5,3.6l31.8,9l42,9.5c0.3-0.7,0.4-3.3,0.2-4.1l-20.2-56.6c-0.4-1.2-4.2-8.8-0.7-10.3l46.8-32.6l44.3-36.4c-0.2-3.1-1.5-6.8-1.9-8l-3-8.4c-0.6-2,1.1-4,3.2-3.7l109,14.4c1.3,0.2,2.6-0.5,3.1-1.7l7.8-18c0.5-1.2,0.2-2.6-0.8-3.4l-66.8-57.3l-39.9-37.7C290.2,130,289.3,129.8,288.4,129.9z");
    animation: move 15s linear infinite;
}

@keyframes move{
    100%{
        offset-distance: 100%;
    }
}

伪类选择器函数

伪类选择器函数主要出自于两个规范中,其中:nth-child():nth-last-child():nth-of-type():nth-last-of-type()出自于Selectors Level 3;而:is():not():where():has()出于Selectors Level 4

这些选择器可以帮助我们快速选择到需要的目标元素,比如说选择第n个,比如说选择最后一个等。@Chris Coyier写了一个:nth-*相关函数的测试器,你可以操作这个测试器,亲自体验:nth-*等伪类选择器函数的作用:

这几个函数有一个共性,那就是可以给他传一个表达式参数,比如n+1-n+22n+4等,除了表达式还可以添加像oddeven这样的关键词。注意n可以理解为从 1开始的自然序列(比如0123等)。

它们的主要区别是:

:nth-child()是从前面开始按指定序号匹配;:nth-last-child()是从后面开始按指定序号匹配;:nth-of-type()从前面开始匹配指定索引当前标签类型元素;:nth-last-of-type()是从后面开始匹配指定索引当前标签类型元素

另外,:nth-child():nth-last-child():nth-of-type():nth-last-oft-type()组合在一起,可以让选择器更强大,比如起到取模(Mod)或数量查询的功能:

li:nth-last-child(n + 4):nth-last-child(-n+6):first-child, 
li:nth-last-child(n + 4):nth-last-child(-n+6):first-child ~ li{ 
    background-position: 0 0; 
}

如果你对组合伪类选择器函数去做数量查询话题感兴趣的话,还可以阅读:

有关于:nth-*相关伪类选择器函数更详细的介绍还可以阅读《伪类选择器》一文。

:is():not():where():has()是CSS选择器最新模块中的特性。简单的来看一下这几个选择器。

:is()函数将选择器列表作为参数,并选择该列表中任意一个选择器可以选择的元素,这对于以更紧凑的形式编写大型选择器非常有用:

/* 选择header, main, footer里的任意一个悬浮状态的段落(p标签) */
:is(header, main, footer) p:hover {
    color: red;
    cursor: pointer;
}

/* 以上内容相当于以下内容 */
header p:hover,
main p:hover,
footer p:hover {
    color: red;
    cursor: pointer;
}

注意,:is()前身是:matches()

:where()函数接受选择器列表作为它的参数,将会选择所有能被该选择器列表中任何一条规则选中的元素。

:where():is() 的不同之处在于,:where() 的优先级总是为 0 ,但是 :is() 的优先级是由它的选择器列表中优先级最高的选择器决定的。比如下面这个示例:

a:not(:hover) {
    text-decoration: none;
}

nav a {
    /* 没有任何影响 */
    text-decoration: underline;
}

使用:where()可以用来改变选择器权重,从而达到想要的意图:

a:where(:not(:hover)) {
    text-decoration: none;
}

nav a {
    /* 可以正常工作 */
    text-decoration: underline;
}

:has()函数代表一个元素,其给定的选择器参数(相对于该元素的 :scope)至少匹配一个元素。:has()接受一个选择器组作为参数。在当前规范中 :has 并未列为实时选择器配置的一部分,意味着其不能用于样式表中,只能用于如 document.querySelector() 的函数中。比如:

a:has(> img) {
    border: 2px solid lime
}

上面的选择器只会匹配直接包含 <img> 子元素的 <a> 元素:

:not()函数以选择器列表做为参数,它表示的元素不是由其参数表示的。:not()选择器可以用来做为判断的一个选择器,好比JavaScript中的非。其主要作用就是将符合规则的元素剔除,将样式规则应用于其他元素上。事实上,在CSS Selector Level 3就有:not()的身影,只不过当初的功能比较弱,比如:not(p)用来选择不是<p>的元素。但在新版本的中,其功能变得更为强大,可以应用更为复杂的规则,但是同样地不允许嵌套使用,比如:not(:not(...))

我们平时开发项目的时候,时常会碰到列表这样的效果,列表项之间有一个margin-bottom,而往往想在最后一项中不设置margin-bottom。比如像下图这样的效果:

往往我们借助伪类选择器:last-child来帮我实现,比如:

li{
    margin-bottom: 20px;
}

li:last-child {
    margin-bottom: 0;
}

如果我们使用:not()选择器,会变得更容易:

li:not(:last-child) {
    margin-bottom: 20px
}

上面的代码表示的意思就是:选中除最后一项li的所有li,并给其设置margin-bottom: 20px;

另外,:not()选择器还有一个提高CSS权重的小作用,比如div:not(span)div是同一个概念,但是明显的前者的优先级要更高。

而且:not()还可以和其他的伪类选择器函数嵌套使用,比如:

// 匹配不含有h1、h2、h3、h4、h5和h6的section元素 
section:not(:has(h1, h2, h3, h4, h5, h6)) {} 

// 匹配含有的不是h1、h2、h3、h4、h5、h6子元素的元素 
section:has(:not(h1, h2, h3, h4, h5, h6)) {}

:not():has()组合在一起使用的,但两者组合在一起所达到的意思却完全不一样。其中:not(:has(selector))匹配不含有selector选择元素的元素(有点绕,对应看上面示例描述),而:has(:not(selector))匹配含有的不是selector子元素的元素。两都主要区别在于,:has(:not(selector))写法必须要含有一个子元素,而:not(:has())可以不含有元素也会被匹配。

不过,使用:not()时有些细节需要特别注意:

  • :not() 不能被嵌套,这意味着 :not(:not(...)) 是无效的
  • 由于伪元素不是简单的选择器,他们不能被当作 :not() 中的参数,形如 :not(p::before) 这样的选择器将不会工作
  • 可以利用这个伪类写一个完全没有用处的选择器。例如, :not(*) 匹配任何非元素的元素,因此,这个规则将永远不会被应用
  • 可以利用这个伪类提高规则的优先级。例如, #foo:not(#bar)#foo 会匹配相同的元素,但是前者的优先级更高
  • :not(.foo) 将匹配任何非 .foo 的元素,包括 <html><body>
  • 这个选择器只会应用在一个元素上,无法用它来排除所有父元素。比如, body :not(table) a 依旧会应用到表格元素 <table> 内部的 <a> 上, 因为 <tr>将会被 :not(table) 这部分选择器匹配

如果你对CSS Selector Level4规范中提到的选择器感兴趣的话,建议你花点时间阅读《初探CSS 选择器Level 4》一文。有关于选择器更多的教程可以点击这里查阅

渐变函数

渐变函数最早出于CSS Images Module Level 3,最新规范来自CSS Image Values and Replaced Content Module

到今天为止,渐变函数主要有linear-gradient()radial-gradient()conic-gradient()repeating-linear-gradient()repeating-radial-gradient()repeating-conic-gradient()几个函数。这几个函数可以构建一种颜色平滑过渡到另一种颜色的图像(渐变其实生成的就是图像,用于background-image),当然它也可以是多种颜色之间的一种平滑过渡或是间性(看上去有分隔线)过渡。

渐变函数的语法如下:

<gradient> = [
    <linear-gradient()> | <repeating-linear-gradient()> |
    <radial-gradient()> | <repeating-radial-gradient()> |
    <conic-gradient()>  | <repeating-conic-gradient()> 
]

具体的语法规则:

linear-gradient() = linear-gradient([ <angle> | to <side-or-corner> ]? ,<color-stop-list>)

radial-gradient() = radial-gradient([ <ending-shape> || <size> ]? [ at <position> ]? ,<color-stop-list>)
radial-gradient() = radial-gradient([ [ circle || <length> ] [ ellipse || <length-percentage>{2} [ at <position> ]? , | [ [ circle | ellipse ] || <extent-keyword> ]  [ at <position> ]? , |at <position> , ]? <color-stop> [ , <color-stop> ]+)

conic-gradient() = conic-gradient([ from <angle> ]? [ at <position> ]?,<angular-color-stop-list>)

<extent-keyword> = closest-corner | closest-side | farthest-corner | farthest-side
<side-or-corner> = [left | right] || [top | bottom]
<color-stop-list> = <linear-color-stop> , [ <linear-color-hint>? , <linear-color-stop> ]#
<linear-color-stop> = <color> && <length-percentage>?
<linear-color-hint> = <length-percentage>
<color-stop-list> =[ <linear-color-stop> [, <linear-color-hint>]? ]# , <linear-color-stop>
<color-stop-length> = <length-percentage>{1,2}
<angular-color-stop-list> = [ <angular-color-stop> [, <angular-color-hint>]? ]# , <angular-color-stop>
<angular-color-stop> = <color> && <color-stop-angle>?
<angular-color-hint> = <angle-percentage>
<color-stop-angle> = <angle-percentage>{1,2}
<color-stop> = <color-stop-length> | <color-stop-angle>

另外,repeating-linear-gradient()repeating-radial-gradient()repeating-conic-gradient()函数和linear-gradient()radial-gradient()conic-gradient()等同。

另外,这几个渐变函数可以帮助大家实现很多意想不到的效果,比如这个Demo(建议PC端打开)

CSS渐变函数是很有意思的东西,如果你对这方面感兴趣,还可以阅读:

背景图片函数

背景图片函数来自CSS Image Values and Replaced Content Module

背景图片函数除了前面介绍的url()函数之外,还有几个非常有意思的函数。

cross-fade()

在CSS中,可以给容器设置多个背景,使用CSS混合模式可以达到一些图层混合的效果。cross-fade()函数可以通过改变背景图像的透明度,让它们叠加在一起,即多个图像混合在一起的效果。

.element {
    background-image: cross-fade(
        url("https://images.unsplash.com/photo-1566738780863-f9608f88f3a9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2378&q=80"),
        url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/221808/sky.jpg"),
        75%
    );
}

比如上面的示例中,可以尝试着改变图像的透明度,你将看到的效果如下:

image-set()

Web开发者都知道,在视网膜屏幕(高清屏)对图像资源要求非常的高,如果达不到一定的尺寸,那么图像清晰度就会有着直接的影响。早一点的解决方案,可能会考虑用媒体查询来改变背景图像资源来达到设备的要求:

后面随着imgsrcsetsize特性的出现以及<picture>元素的到来,开发者可以根据设备来加载不同的图像资源,实现在高清屏加载高清晰的图像:

同样的,背景图像函数中的image-set()也可以达到同等的功能:

element()

element()函数定义了一个从任意的 HTML 元素中生成的图像 <image> 值。该图像值是实时的,这意味着如果被指定的 HTML 元素被更改,使用结果值的CSS属性将自动更新。简单地说,element()可以把HTML中的任意元素,当作另一个元素的背景图像,而且神奇的是,只要元素修改了(样式或内容),那么对应的背景也会改变。比如下面这个示例:

<div id="css-source"> 
    <p>Lorem ipsum</p> 
    <img src="" alt="" /> 
</div> 
<div id="css-result"></div> 

#css-result { 
    background: element(#css-source); 
    background-size: 50% 50%; 
}

上例中,div#css-source元素,其包含了图片和文本,而且将div#css-source元素当作div#css-result元素的背景图像来渲染。

效果如下(请使用Firefox查看下面的Demo):

image()

image()函数允许开发者:

  • 使用媒体片段做为图像
  • 使用纯色作为图像
  • 当指定url的图像无法下载或解码时,会降级为纯色图像
  • 自动尊重图像元数据中指定的图像方向

换句话说,image()函数可以插入静态图像(<url>引入的图像),也可以通过渐变和element()函数动态绘制图像。其具体语法规则如下:

image() = image( <image-tags>? [ <image-src>? , <color>? ]! )
<image-tags> = [ ltr | rtl ]
<image-src> = [ <url> | <string> ]

来看一个示例:

body { 
    color: black; 
    background: white; 
}

.special { 
    color: white; 
    background: image("dark.png", black); 
}

上面的代码会告诉浏览器,当图片加载失败时,会以纯色来替代,确保文本仍然是可读的。

颜色函数

颜色函数主要来自CSS Color Module Level 4CSS Color Module Level 5

颜色不管是在Web系统中还是在设计学中都是一个复杂的体系,但对于Web开发者来说,更多的是关注于颜色的使用。换句话说,就是如何给Web元素添加元素。

在Level 4中除了提供了新的描述颜色的函数之外,而且其语法规则也有所改变:

虽然在语法规则上有所改变,但老的语法规则也是兼容的。随着新语法的出现,那么以前的rgba()hsla()这样带透明通道函数就可以放弃使用,并且在函数名和使用方式趋于统一。

rgb()

语法规则:

rgb() = rgb( <percentage>{3} [ / <alpha-value> ]? ) | rgb( <number>{3} [ / <alpha-value> ]? ) || rgb( <percentage>#{3} , <alpha-value>? ) | rgb( <number>#{3} , <alpha-value>? ) 

rgb()Rred)、G(green)和B(blue)是必传的三个参数,采用的是RGB色彩空间,如果未显式传递第四个参数值A,那么表示颜色透明通道的值为1,相当之前我们熟悉的rgb()函数,如果显式传递了第四个参数值,表示设置了颜色的透明度,相当之前我们熟悉的rgba()函数。

函数中参数之间可以用空格(最新语法)或逗号来分隔,如果采用空格分隔符,那么传递透明度参数值时需要用/来分隔。

hsl()

语法规则:

hsl() = hsl( <hue> <percentage> <percentage> [ / <alpha-value> ]? ) || hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )

hsl()函数和rgb()函数类似,只不过参数所表达的含义不同,在hsl()函数中传的参数依次是h(色相Hue)、s(饱和度saturation)、l(亮度Lightness)和a(透明度)。

hwb()

语法规则:

hwb() = hwb( <hue> <percentage> <percentage> [ / <alpha-value> ]? )

HWB是 Hue Whiteness Blackness的简写,是RGB颜色空间的一种基于颜色的表示。HWB模型通常比HSL模型更直观(HSL模型本身被广泛认为比RGB更直观)。这是因为,HWB模型允许你选择一种色相盘上的一种颜色,然后根据自己需要将其与白色和黑色混合。

  • H:和HSLHSV中的H相同,指的都是色相
  • W:指的是白色的程度,范围从0% ~ 100%(或0 ~ 1
  • B:批的是黑色的程度,范围从0% ~ 100%(或0 ~ 1

lab()lch()

语法规则:

lab() = lab( <percentage> <number> <number> [ / <alpha-value> ]? )
lch() = lch( <percentage> <number> <hue> [ / <alpha-value> ]? )

在CSS中,我们可以使用lab()函数描述Lab颜色空间,使用lch()函数描述LCH颜色空间。

lab()函数的第一个参数指定了CIE的亮度。这是一个典型的介于0%(代表黑色)和100%(代表白色)之间的数字。第二个和第三个参数是Lab颜色空间中沿着ab轴的距离。这些值是有符号的(允许正负值),理论上是无界的(但实际上不超过±160)。第四个参数是可选值,和rgb()函数一样,用来描述颜色的透明通道,默认值为100%,表示完全不透明。

lch()lab()函数类似,第一个参数指定了CIE的亮度。这是一个典型的介于0%(代表黑色)和100%(代表白色)之间的数字。第二个和第三个参数是Lab颜色空间中沿着ab轴的距离。这些值是有符号的(允许正负值),理论上是无界的(但实际上不超过±160)。第四个参数是可选值,和rgb()函数一样,用来描述颜色的透明通道,默认值为100%,表示完全不透明。

gray()

语法规则:

gray() = gray( <number>  [ / <alpha-value> ]? )

第一个参数指定灰度,等于CIE亮度,而第二个可选参数指定灰度的透明通道。

color()

语法规则:

color() = color( [ <ident>? [ <number>+ | <string> ] [ / <alpha-value> ]? ]# , <color>? )

在CSS中,我们可以通过引用颜色配置文件来指定颜色。这可以是一个校准的CMYK打印机,或RGB色彩空间,或任何其他颜色或单色输出设备的特点。此外,为了方便起见,CSS提供了几个预定义的RGB颜色空间,比如srgbdisplay-p3a98-rgbprophoto-rgbrec-2020等。

color()函数接受一个或多个逗号分隔的参数,每个参数指定一种颜色,如果之前的颜色无法显示,则稍后的颜色将充当回退(替代前者)。

device-cmyk()

语法规则:

device-cmyk() = device-cmyk( <cmyk-component>{4} [ / <alpha-value> ]? , <color>? ) <cmyk-component> = <number> | <percentage>

device-cmyk()函数的参数按顺序将青色、品红、黄色和黑色组合在一起,它们的值可以是0 ~ 1之间的<number>0% ~ 100%<percentage>。这两种取值方式是等同的,并且是线性地相互映射的。值小于0(或0%),或大于1(或100%),同样会有效,只不会小于0的值为计算为0,大于1(或100%)的会计算为1(或100%)。

第五个参数是指定颜色的透明通道,它的解释和rgb()函数中的第四个参数相同。第六个参数是用来指定回退颜色,在用户代理不知道如何准确地将CMYK颜色转换为RGB时使用。如果省略,它默认为CMYK颜色转换为RGBA。

其他计算颜色的函数

在未来的CSS中,会有很多修改颜色的函数,比如color-mix()color-contrast()color-adjust()rgb()hsl()hwb()lab()lch()函数。

但其中rgb()hsl()hwb()lab()lch()函数和前面用来描述颜色值的<rgb><hsl><hwb><lab><lch>不同,比如:

rgb(from  indianred 255 g b)
hsl(from var(--accent) calc(h+180) s l)
lab(from var(--mycolor) l 0 0)

可能大家会感到困惑。不过这部分功能在W3C的CSS Color Module Level 5规范中有详细的描述。

在CSS的函数中,我们可以结合calc()var()函数,可以轻易构建颜色系统:

:root {
    --hue: 0;
    --lightness: 50;
    --saturation: 80;
}

* {
    --s: calc(var(--saturation) * calc(1 - var(--desaturate, 0)));
    --h: calc(var(--hue) + var(--hue-rotate, 0));
    --l: calc(var(--lightness) * calc(1 - var(--darken, 0)));
    --color: hsl(var(--h) calc(var(--s) * 1%) calc(var(--l) * 1%));
}

body {
    --darken: -.25;
    --desaturate: .2;
    --hue-rotate: 100;
    background-color: var(--color)
}

有关于颜色函数更详细的介绍,还可以移步阅读《图解CSS: CSS 颜色》一文

图形函数

图形函数主要来自于CSS Shapes Module Level 1

CSS中的图形函数主要是用来绘制图形的,在W3C规范中常被称为<basic-shape>,主要包含inset()circle()ellipse()polygon()

inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
circle() = circle( [<shape-radius>]? [at <position>]? )
ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )

<shape-arg> = <length> | <percentage>
<shape-radius> = <length> | <percentage> | closest-side | farthest-side

目前这几个函数主要服务于clip-pathshape-outsideoffset-path几个属性。运用于不同的属性中所起作用不同:

比如下面这个Demo,内容在一个圆内排版:

上面的示例在shape-outside属性中用polygon()函数描绘了一个类似半圆的图形:

滤镜函数

滤镜函数主要来自Filter Effects Module Level 1

CSS的滤镜函数的出现,可以帮助Web开发者实现很多特殊效果,以前这些效果都需要依赖图像编辑软件(比如PhotoShop)来实现。CSS滤镜函数主要包括:

<filter-function> = <blur()> | <brightness()> | <contrast()> | <drop-shadow()> | <grayscale()> | <hue-rotate()> | <invert()> | <opacity()> | <sepia()> | <saturate()>

具体的函数如下:

blur() = blur( <length>? )
brightness() = brightness( <number-percentage>? )
contrast() = contrast( <number-percentage>? )
drop-shadow() = drop-shadow( <color>? && <length>{2,3} )
grayscale() = grayscale( <number-percentage>? )
hue-rotate() = hue-rotate( [ <angle> | <zero> ]? )
invert() = invert( <number-percentage>? )
opacity() = opacity( <number-percentage>? )
saturate() = saturate( <number-percentage>? )
sepia() = sepia( <number-percentage>? )

这些函数可以单个或多个运用于filterfilter()backdrop-filter。具体每个函数所起的作用,大家可以通过下面这个Demo来体验:

采用CSS的background-blend-modemix-blend-modefilter我们可以实现一些意想不到的效果,甚至可以说堪称是艺术效果

变换函数

变换函数主要来自CSS Transforms Module Level 2

CSS变换函数主要是运用于transform的属性值,它们分为2D变换函数和3D变换函数。其中2D变换函数包括:

matrix() = matrix( <number> [, <number> ]{5,5} )
translate() = translate( <length-percentage> [, <length-percentage> ]? )
translateX() = translateX( <length-percentage> )
translateY() = translateY( <length-percentage> )
scale() = scale( <number> [, <number> ]? )
scaleX() = scaleX( <number> )
scaleY() = scaleY( <number> )
rotate() = rotate( [ <angle> | <zero> ] )
skew() = skew( [ <angle> | <zero> ] [, [ <angle> | <zero> ] ]? )
skewX() = skewX( [ <angle> | <zero> ] )
skewY() = skewY( [ <angle> | <zero> ] )

其中:

  • translate()translateX()translateY()的简写
  • rotate()rotateX()rotateY(0)的简写
  • scale()scaleX()scaleY()的简写
  • skew()skewX()skewY()的简写

3D的变换函数包括:

matrix3d() = matrix3d( <number>#{16} )
translate3d() = translate3d( <length-percentage> , <length-percentage> , <length> )
translateZ() = translateZ( <length> )
scale3d() = scale3d( <number> , <number>, <number> )
scaleZ() = scaleZ( <number> )
rotate3d() = rotate3d( <number> , <number> , <number> , [ <angle> | <zero> ] )
perspective() = perspective( <length [0,∞]> )

其中:

  • translate3d()translateX()translateY()translateZ()的简写
  • rotate3d()rotateX()rotateY()rotateZ()的简写
  • scale3d()scaleX()scaleY()scaleZ()的简写

这些函数可以用来对元素进行位移旋转扭曲缩放等,而且其中matrix()matrix3d()可以用来描述任一变换函数。

@Jorge Moreno制作了一个很棒的工具,叫做CSS变换函数可视化工具。它允许你实时调整相应参数,以便更好地理解所有变换函数:

CSS的变换函数可以单一或多个运用于同一个元素,比如:

.element {
    transform: 
        rotate(10deg)
        rotateX(10deg)
        rotateY(10deg)
        rotateZ(10deg)
        perspective(700px);
}

比较函数

比较函数主要来自CSS Values and Units Module Level 4

CSS的min()max()clamp()函数常称为比较函数,但它们和calc()常常也被称为数学函数。但它们和calc()函数最大的区别是用来在几个参数值之间做比较,然后将最合适的值赋值给对应的CSS属性。

CSS的比较函数的语法规则和calc()类似,允许使用带有加法(+)、减法(-)、乘法(*)和除法(/)的数学表达式作为分量值(Component Values)。min()max()函数分别表示其包含的最小或最大的逗号分隔计算;clamp()函数表示其中心计算,返回一个区间范围的值(MIN,VAL,MAX),如果值(VAL)在最小(MIN)和最大值(MAX)区间内,则使用该值(VAL),如果值(VAL)大于最大值(MAX),则使用最大值(MAX),如果值(VAL)小于最小值(MIN),则使用最小值(MIN)。

它们的具体语法规则如下:

<min()>   = min( <calc-sum># )
<max()>   = max( <calc-sum># )
<clamp()> = clamp( <calc-sum>#{3} )

<calc-sum> = <calc-product> [ [ '+' | '-' ] <calc-product> ]*
<calc-product> = <calc-value> [ [ '*' | '/' ] <calc-value> ]*
<calc-value> = <number> | <dimension> | <percentage> | ( <calc-sum> )

有关于这几个函数更详细的介绍可以阅读《聊聊min()max()clamp()函数》一文。

计数函数

计数函数来自CSS Lists Module Level 3

在Web的构建中,HTML的ulol会自动生成序列号(可能是符号也可能是数字),而它们都是由浏览器生成的列表内容,这些内容被称为计数器

在CSS中,我们可以使用计数函数counter()counters()CSS的content以及::marker伪元素选择器来控制非列表计数器:

也就是说,它们的组合可以构建个性化的计数器效果:

除此之外,还能构建一些简单游戏:

数学函数

这里要说的是数学中的三角函数。众所周知,在CSS中到目前为止是还没有三角函数(比如sin()cos()tan()acos()atan()atan2()sqrt()pow()等)相关的规范,但社区中有开始有人在往规范中提这方面的特性。

到目前为止,虽然CSS中还没有三角函数,但在一些CSS的处理器中早就有三角函数的身影,比如Sass中

@function asin($z) {
    $sum: 0;

    @if abs($z) > sin(pi()/4) {
        $z: sqrt(1 - pow($z, 2));
    }

    @return $sum;
}

@Ana Tudor就用这方面的特性构建了一个3D旋转的动画效果:

不过,随着CSS自定义属性的到来,也可以用来模拟一些三角函数的功能:

.sin {
    --sin-term1: var(--angle);
    --sin-term2: calc((var(--angle) * var(--angle) * var(--angle)) / 6);
    --sin-term3: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 120);
    --sin-term4: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 5040);
    --sin-term5: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 362880);
    --sin: calc(var(--sin-term1) - var(--sin-term2) + var(--sin-term3) - var(--sin-term4) + var(--sin-term5));
}

比如下面的动画效果,就是用CSS自定义属性模拟三角函数实现的动画效果:

我想在不久的未来,我们就能在CSS的规范中看到这些三角函数相关的规范,而且可以用这些函数来实现我们想要的效果,特别是在动画的场景中。

其他函数

在CSS中除了上述提到的CSS函数之外,还有很多我们从未接触到或未看到的函数,比如symbols()annotation()format()local()toggle()target-counter()target-counters()target-text()等。在这些函数中,我想只有format()local()@font-face属性中有接触过:

有关于这些函数这里不做更多阐述,待这些函数得到更多主流浏览器支持时,我们将再和大家一起探讨。

小结

这里提到的CSS函数大部分已得到主流浏览器的支持,有些仅得到个别浏览器的支持,甚至有些仅是概念。但驱动Web技术不断的革新和进步往往就是从概念上出发的。

虽然文章中每有对每个函数做详细的介绍,但我希望这是一种引导,让喜欢CSS的同学能自己根据自己的需要进一步获取更详尽的知识。最后,希望这篇文章对大家有所帮助,如果你在这方面有更好的建议或相关经验,欢迎在下面的评论中与我们共享。

返回顶部