07-进阶篇:函数计算
juejin_logo copyCreated with Sketch.

前言

大家都知道,CSS只是一门声明式语言,主要为标记语言HTML服务。很多开发者都会鄙视它,不愿意深入学习,更多会抛出一个原因:现在不是有很多UI框架吗,我还编写CSS干嘛!

虽然CSS看上去弱不禁风,常规开发也就是一堆静态属性声明。然而,这只是完全不了解CSS且还停留在编写属性声明的同学对CSS的理解而已。时至今日,随着前端技术的不断变革,也让曾经被鄙视的CSS变得越来越强大。过去只有声明式的CSS,现在也拥有了具备运算能力的函数。

CSS能做的事情可多了,JS变量函数CSS也有。本章先摸透一些常见函数,因为函数在后面章节中各种客串出场。

函数

CSS函数指复杂类型或调用特殊处理的组件值类型。为单调的属性声明增加了更强大的点缀,让简单的CSS变得更有艺术感。其语法也很简单,形式为function(params),与JS的函数调用一样。在CSS代码中,只要带有()的属性值都是函数。

有了函数后,可将一系列相关计算交给浏览器处理,减少大量人工计算甚至无需人工计算,大大提高代码的编写效率。

分类

我敢相信,很多同学用惯的函数只有url()rgb()rgba(),稍微深入一点的也只有calc()cubic-bezier()linear-gradient()

其实不然,函数怎么可能只有这几个。从W3C标准中查阅可知目前总共存在86个可用函数(不包括那些实验性的函数),一点也不比属性少。根据惯例,我又对其进行了合理的记忆分类,暂未得到很多浏览器支持且在Caniuse中未收录的函数就不在分类范围中。

颜色函数

  • rgb():RGB色彩模式
  • rgba():RGBA色彩模式
  • hsl():HSL色彩模式
  • hsla():HSLA色彩模式
  • color():色彩模式,基于当前颜色衍生出其他颜色

属性函数

  • attr():属性
  • var():变量

数学函数

  • clamp():区间范围值
  • counter():计数器
  • counters():嵌套计数器
  • calc():计算
  • max():最大值
  • min():最小值

背景函数

  • url():图像路径
  • element():图像映射,渲染指定元素为图像
  • image-set():图像集合,根据屏幕分辨率匹配图像
  • linear-gradient():线性渐变
  • radial-gradient():径向渐变
  • conic-gradient():锥形渐变
  • repeating-linear-gradient():重复线性渐变
  • repeating-radial-gradient():重复径向渐变
  • repeating-conic-gradient():重复锥形渐变

滤镜函数

  • blur():模糊
  • brightness():亮度
  • contrast():对比度
  • drop-shadow():阴影
  • grayscale():灰度
  • hue-rotate():色相旋转
  • invert():反相
  • opacity():透明度
  • saturate():饱和度
  • sepia():褐色

图形函数

  • circle():圆形
  • ellipse():椭圆形
  • inset():矩形
  • path():路径
  • polygon():多边行

变换函数

  • matrix():矩阵
  • matrix3d():3D矩阵
  • perspective():视距
  • rotate():旋转
  • rotate3d():3D旋转
  • rotateX():X轴旋转
  • rotateY():Y轴旋转
  • rotateZ():Z轴旋转
  • scale():缩放
  • scale3d():3D缩放
  • scaleX():X轴缩放
  • scaleY():Y轴缩放
  • scaleZ():Z轴缩放
  • skew():扭曲
  • skewX():X轴扭曲
  • skewY():Y轴扭曲
  • translate():位移
  • translate3d():3D位移
  • translateX():X轴位移
  • translateY():Y轴位移
  • translateZ():Z轴位移

缓动函数

  • cubic-bezier():贝塞尔曲线
  • steps():逐帧

分类完毕发现函数很多,但我觉得很多都用不上或不实用,所以我会列举一些我认为在项目中特别有用的函数,大家跟着节奏哈!

颜色函数

颜色函数是最常见函数之一,可用在border-coloroutline-colorbackground-colorbox-shadowcolorcaret-color等属性中。

RGB色彩模式

将文本声明为白色,普通声明可用color:whitecolor:#fff。有了颜色函数后,可用rgb()/rgba()声明。将原来的声明改成color:rgb(255,255,255)rgba(255,255,255,1)

rgb()R表示红色G表示绿色B表示蓝色,而rgba()多出来的A表示透明度。Aopacity声明的透明度不同,rgba()声明的透明度不会应用到子节点中,而opacity声明的透明度会应用到子节点中。

建议在声明普通颜色时使用HEX色彩模式(16进制色彩模式),若颜色存在透明度的需求,可用rgba(),但rgba()的参数不太友好,得把HEX转换为RGB。因为本课程使用Scss作为样式预处理语言,声明rgb()rgba()时使用HEX代替RGB即可。将原来的文本声明改成color:rgb(#fff)rgba(#fff,1)

HSL色彩模式

HSL色彩模式是一种工业界的色彩标准,因为它能涵盖到人类视觉所能感知的所有颜色,所以在工业界广泛应用。

hsl()/hsla()与上述两个颜色函数在CSSScss中的用法相似。H表示色相,S表示饱和度,L表示亮度,A表示透明度

色相又称色盘,指色彩的基本属性。就是常说的颜色名称,例如红色、绿色等,此时应想起画家那个装满不同颜料的色盘吧。色相的单位是deg,值的范围在0~360deg,若超过360deg则相当绕N圈再计算剩余的值。0deg360deg为红色,120deg为绿色,240deg为蓝色。

饱和度指色彩的纯度。值越高色彩越纯,值越低色彩越灰。饱和度的单位是%,值的范围在0~100%0%为灰色,100%为全色。

亮度指色彩的发光强度。值越高色彩越亮,值越低色彩越暗。亮度的单位是%,值的范围在0~100%0%为最暗,100%为最亮。若你想亮瞎别人的狗眼,把该值调整为100%吧。

饱和度与亮度的单位即使是0也得写成0%,否则整个函数都会失效。

HSL色彩模式其实是一种将RGB色彩模式中的点在圆柱坐标系中标记出来的表示法,该表示法试图做到比基于笛卡尔坐标系的几何结构RGB更直观。

属性函数

attr()

attr(val)用于返回节点属性,通常结合伪元素的content使用,是一个很优雅的函数。兼容性好不说了,还极其低调,导致很多同学都以为它是一个CSS3特性

<h1 class="Hello" data-name=" JowayYoung"></h1>
h1 {
	&::before {
		content: attr(class);
	}
	&::after {
		content: attr(data-name);
	}
}

::before通过attr()获取<h1 class>的属性值并赋值到content中,::after通过attr()获取<h1 data-name>的属性值并赋值到content中,最终<h1>innerTextHello JowayYoung

attr()可灵活结合选择器返回节点属性并赋值到伪元素的content中,通过attr()结合:hover:empty抓取节点需显示的内容是一个很不错的技巧。

  • 按钮1触发悬浮状态:hover时,通过attr()获取节点的data-msg并赋值到::aftercontent
  • 按钮2内容为空:empty时,通过attr()获取节点的href并赋值到::aftercontent

节点属性

<a class="hover-tips btn-1" href="https://www.baidu.com" data-msg="Hello World">提示框</a>
<a class="hover-tips btn-2" href="https://www.baidu.com"></a>
.hover-tips {
	position: relative;
	padding: 0 20px;
	border-radius: 10px;
	height: 40px;
	background-color: #66f;
	line-height: 40px;
	color: #fff;
	& + .hover-tips {
		margin-top: 10px;
	}
	&.btn-1 {
		&::after {
			position: absolute;
			left: 0;
			top: 0;
			border-radius: 5px;
			width: 100%;
			height: 100%;
			background-color: rgba(#000, .5);
			opacity: 0;
			text-align: center;
			font-size: 12px;
			content: attr(data-msg);
			transition: all 300ms;
		}
		&:hover::after {
			left: calc(100% + 20px);
			opacity: 1;
		}
	}
	&.btn-2:empty::after {
		content: attr(href);
	}
}

var()

var()用于引用自定义属性,是CSS变量的组成之一,在第8章会详细讲述。

数学函数

counter()/counters()

counter(val)用于返回计数器迭代值,必须结合伪元素的content使用。它以计数器名称作为参数并作为值传递给contentcounters(val)用于返回嵌套计数器迭代值,情况与counter()一样。

在使用counter()/counters()时,必须与counter-resetcounter-increment一起使用。

  • counter-reset:重置计数器名称与初始值,形式为counter-reset:name val
  • counter-increment:对指定计数器累计其计数值,形式为counter-increment:name,在使用地方的声明会累加

对于一些迭代需求通常都会使用HTML模板,例如Vue模板Pug模板等,所以counter()/counters()显得很苍白无力,我也很少发掘它的用处。以下就使用counter()巧妙搭配完成一个显示权重的迭代计数器。

迭代计数器

<div class="iterative-counter">
	<ul>
		<li>
			<input id="angular" type="checkbox">
			<label for="angular">Angular</label>
		</li>
		<li>
			<input id="react" type="checkbox">
			<label for="react">React</label>
		</li>
		<li>
			<input id="vue" type="checkbox">
			<label for="vue">Vue</label>
		</li>
	</ul>
	<p class="count" data-unit="个">框架:</p>
	<p class="weight" data-unit="%">权重:</p>
</div>
.iterative-counter {
	ul {
		counter-reset: index 0 count 0 weight 0;
	}
	li {
		display: flex;
		position: relative;
		align-items: center;
		counter-increment: index 1;
		&::before {
			content: counter(index)"、";
		}
		& + li {
			margin-top: 10px;
		}
	}
	input {
		overflow: hidden;
		position: absolute;
		width: 0;
		height: 0;
		opacity: 0;
		&:checked + label::before {
			color: #3c9;
			content: "\2713";
		}
	}
	label {
		display: flex;
		align-items: center;
		height: 20px;
		&::before {
			margin-right: 5px;
			border: 1px solid #3c9;
			width: 20px;
			height: 20px;
			cursor: pointer;
			line-height: 20px;
			text-align: center;
			color: transparent;
			content: "";
			transition: all 300ms;
		}
	}
	p {
		margin-top: 10px;
		&.count::after {
			content: counter(count) attr(data-unit);
		}
		&.weight::after {
			content: counter(weight) attr(data-unit);
		}
	}
}
#angular:checked {
	counter-increment: count 1 weight 20;
}
#react:checked {
	counter-increment: count 1 weight 50;
}
#vue:checked {
	counter-increment: count 1 weight 30;
}

calc()

calc(exp)用于动态计算单位,数值长度角度时间百分比都能作为参数。因为执行数学表达式返回运算后的计算值,可减少大量人工计算甚至无需人工计算,是最有用的函数之一。

calc()饥不择食,所有计量单位都能作为参数参与整个动态计算。

  • 数值整数浮点数
  • 长度pxemremvwvh
  • 角度degturn
  • 时间sms
  • 百分比%

calc()虽然好用,但新手难免会遇到一些坑,谨记以下特性,相信就能玩转calc()了。

  • 四则运算:只能使用+-*/作为运算符号
  • 运算顺序:遵循加减乘除运算顺序,可用()提升运算等级
  • 符号连接:每个运算符号必须使用空格间隔
  • 混合计算:可混合不同计量单位动态计算

第三点尤为重要,若未能遵守,浏览器直接忽略该属性。

还记得第5章的一行代码让网页自适应吗?font-size:calc(100vw / 7.5),其实这是根据设计图与浏览器视窗的比例动态计算<html>font-size100/750 = x/100vw

在单页面应用中有遇到因为有滚动条或无滚动条而导致网页路由在跳转时发生向左或向右的抖动吗?这让强迫症患者很不舒服,此时可用calc()巧妙解决该问题。

.elem {
	padding-right: calc(100vw - 100%);
}

100vw是视窗宽度,100%是内容宽度,那100vw - 100%就是滚动条宽度了,声明padding-right用于保留滚动条出现的位置,这样滚动条出不出现都不会让网页抖动了。

大部分浏览器的滚动条默认宽度是17px,那声明padding-right:17px不就行了吗?因为还有很少浏览器的滚动条默认宽度不是17px,另外Chrome系列浏览器都可自定义滚动条宽度,这是无法直接知道的,因此声明padding-right:calc(100vw - 100%)动态计算浏览器的滚动条默认宽度是最适合不过的。

calc()需结合变量才更好玩,后续章节都会有calc()乱入,大家记得注意喔!

clamp()/max()/min()

clamp()/max()/min()calc()类似,所有计量单位都能作为参数参与整个动态计算,这些函数与calc()可互相嵌套使用。

.elem {
	width: calc(min(1200px, 100%) / 5);
}

max(...val)用于返回最大值,min(...val)用于返回最小值,支持一个或多个值或数学表达式,值间使用,间隔。虽然max()名称是最大值,但实际上是用于限制最大值;min()名称是最小值,但实际上是用于限制最小值。

在响应式开发中,通常会声明内容宽度100%自适应且最大值不超过1200px

.elem {
	width: 100%;
	max-width: 1200px;
}

若用min()表示,只需一行声明。

.elem {
	width: min(1200px, 100%);
}

clamp(min, val, max)用于返回区间范围值。valmin~max则返回valval小于min则返回minval大于max则返回max,妥妥的响应式函数。

clamp(min, val, max)等价于max(min, min(val, max))clamp()可用于响应式开发,很好地履行响应式的义务,让组件属性在指定条件中使用指定值。

.elem {
	width: clamp(100px, 25vw, 300px);
}

节点宽度声明在100~300px,随着视窗宽度变化而变化。若视窗宽度大于300px则节点宽度一直保持300px,若视窗宽度在100~300px则节点宽度为25vw转化后的px,若视窗宽度小于100px则节点宽度一直保持100px

图形函数

clip-path用于创建一个只有节点的部分区域可显示的剪切区域。裁剪完毕,内部区域显示,外部区域隐藏。一般应用在SVG中,但也可当作裁剪效果用于节点中。当节点使用clip-path声明裁剪路径时,可用这五个图形函数裁剪区域,除了path()其他四个函数的兼容性还不错。

以下使用circle()ellipse()polygon()绘制一些常见图形。

各种图形

<ul class="figure-box" style="--count: 12;">
	<li class="star" style="--index: 0;"></li>
	<li class="ellipse" style="--index: 1;"></li>
	<li class="circle" style="--index: 2;"></li>
	<li class="triangle" style="--index: 3;"></li>
	<li class="rhombus" style="--index: 4;"></li>
	<li class="trapezoid" style="--index: 5;"></li>
	<li class="parallelogram" style="--index: 6;"></li>
	<li class="pentagon" style="--index: 7;"></li>
	<li class="left-arrow" style="--index: 8;"></li>
	<li class="right-arrow" style="--index: 9;"></li>
	<li class="close" style="--index: 10;"></li>
	<li class="message" style="--index: 11;"></li>
</ul>
.figure-box {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	max-width: 720px;
	li {
		--angle: calc(var(--index) / var(--count) * 1turn);
		margin: 10px;
		width: 100px;
		height: 100px;
		background-color: #3c9;
		filter: hue-rotate(var(--angle));
		&.star {
			clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
		}
		&.ellipse {
			clip-path: ellipse(40% 50% at 50% 50%);
		}
		&.circle {
			clip-path: circle(50% at 50% 50%);
		}
		&.triangle {
			clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
		}
		&.rhombus {
			clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
		}
		&.trapezoid {
			clip-path: polygon(20% 0%, 80% 0%, 100% 100%, 0% 100%);
		}
		&.parallelogram {
			clip-path: polygon(25% 0%, 100% 0%, 75% 100%, 0% 100%);
		}
		&.pentagon {
			clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%);
		}
		&.left-arrow {
			clip-path: polygon(40% 0%, 40% 20%, 100% 20%, 100% 80%, 40% 80%, 40% 100%, 0% 50%);
		}
		&.right-arrow {
			clip-path: polygon(0% 20%, 60% 20%, 60% 0%, 100% 50%, 60% 100%, 60% 80%, 0% 80%);
		}
		&.close {
			clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
		}
		&.message {
			clip-path: polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%);
		}
	}
}

整体来说在指定坐标中标记连线的点即可。推荐一个裁剪路径的网站Clippy,轻松绘制各种由线条组成的裁剪区域。clip-path有一个明显的限制,就是只能裁剪折线形成的图形,不能裁剪曲线形成的图形。

其他函数

因为后续章节都单独挂钩背景函数滤镜函数变换函数缓动函数,所以本章就不再讲述这些函数了。

留言
Ctrl + Enter
全部评论(16)
yuobey的头像
删除
前端 @ 蹲坑中
background-color: rgba(#000, .5); 还可以这样设置吗,这样子我在谷歌浏览器是无效的,
点赞
1
删除
(作者)
这个是sass写法
点赞
回复
Peanutssss的头像
删除
do it.
属性函数 attr 那里,鼠标放在提示框和弹出的提示中间会来回抖动
点赞
3
删除
(作者)
哈哈,这个demo写得瑕疵了
点赞
回复
删除
这个为啥会有抖动啊
哈哈,这个demo写得瑕疵了
点赞
回复
查看更多回复
黄小心的头像
删除
--Θ: calc(var(--index) / var(--count) * 1turn);
filter: hue-rotate(var(--Θ));
请问这块是在做什么?
点赞
3
删除
--x是在声明一个变量
var(--x)是在引用这个变量
点赞
回复
删除
(作者)
--Θ是动态计算角度,用filter的色相旋转依据这个角度做渐变色
1
回复
查看更多回复
积木立方的头像
删除
属性函数那一节 &::affter 多写了个字母f
点赞
1
删除
(作者)
已修复
点赞
回复
卡佩罗🍀的头像
删除
&::before 这个 &啥语法,啥意思
点赞
1
删除
(作者)
小册使用了sass作为样式预处理语言,&是sass里的语法
点赞
回复
alfred_li的头像
删除
前端 @ ringcentral
padding-right: calc(100vw - 100%); 这个牛🍺,最近刚好遇到在mac最新系统10.16,滚动条会占用位置,导致页面经常抖动,很头疼。现在终于看到比较好的解决方案了
5
1
删除
(作者)
是的,这个骚技巧很好用
2
回复