《CSS揭秘》读书笔记

1,962 阅读24分钟

摘要

《CSS揭秘》主要是介绍了使用CSS的技巧,通过47个案例来灵活的使用CSS进行实现,同时在实现过程中注重CSS代码的灵活性与健壮性。通过阅读这本书有利于我们编写高质量的CSS代码以及打破使用CSS时的固定思维,能够更加灵活的使用CSS。

《CSS揭秘》中的所有案例代码均上传到:play.csssecrets.io/。

关于《CSS揭秘》的读书笔记主要是记录通过阅读书中的CSS案例所获得启发和对CSS样式新的认知点。

第二章 背景与边框

background-clip

background-clip属性设置元素的背景(背景图片或颜色)覆盖到什么位置。
属性值为:border-box(默认值) | padding-box | content-box | text
注:text属性值是设置将背景被裁剪为文字的前景色(chrome浏览器支持需要添加-webkit-前缀;最新版本的火狐浏览器 [ 63.0.3 (64 位) ]直接支持background-clip: text 属性)。

background-origin

background-origin规定了指定背景图片background-image属性的原点位置的背景相对区域。
属性值为:border-box | padding-box(默认) | content-box

多重边框

传统做法:通过多重元素嵌套来生成多重边框。

新的思路:
方法一:通过box-shadow的第四个参数可以让投影面积增大或者缩小,设置box-shadow: 0 0 0 10px #809;得到的“投影”其实就像一道实线边框,并且box-shadow支持逗号分割法,因此我们可以创建任意数量的投影。
缺点:边框的样式单一,只能够是实线边框。

方法二:通过outline描边属性来设置第二重边框
缺点:只能够适用于两重边框(border一重,outline一重),并且outline的描边不能够贴合border-radius的圆角,只能够是矩形。

背景图片的灵活定位

传统做法:通过计算元素的大小,设置background-position的位置。

新的思路:
方法一:background-position属性模式是通过左上角进行定位的,可以随机指定距离任意角的偏移量,background-position:left 20px top 30px;

方法二:通过calc()函数来实时计算background-position的位置,background-position: calc(100% - 20px) calc(100% - 30px);

方法三:可以通过background-origin:content-box,使得background-position以内容区的左上角作为基准,图片距离边角的偏移量等于内边距。

边框内圆角(矩形容器,内侧有圆角)

传统做法:通过两个div嵌套,内部的div设置为圆角,外部的div保持矩形边框形状。

新的思路:outline属性结合box-shadow属性。
1、通过outline可以为border-radius描边,但是描边为矩形,与圆角之前存在空白缝隙,通过box-shadow可以填充缝隙。
2、需要填充的缝隙大小为,圆角半径为r,圆心到描边顶角的距离为√2 r,因此填充的距离为(√2 - 1)r
缺点:因为box-shadow的宽度至少需要设置为(√2 - 1)r,如果描边的宽度小于这个值,那么描边将会被box-shadow遮住。

linear-gradient()

linear-gradient()函数创建一个渐变“图像”
函数内容为:linear-gradient(direction, colorItem1,colorItem2...)
1、角度设置是根据时钟顺时针进行计算的;
2、下一个颜色开始位置与上一个颜色的结束位置一致,则不需要填充颜色之间空余的位置,形成不同的颜色条而非过渡条;
3、当后面颜色位置小于前面颜色位置时,浏览器之间将后面颜色位置更正为与前面颜色位置一致。

repeating-linear-gradient()

repeating-linear-gradient()创建一个重复线性渐变的“图像”
函数内容为:repeating-linear-gradient(direction, colorItem1,colorItem2...)
每次重复时,色标位置的偏移量都是基准渐变长度(最后一个色标和第一个之间的距离)的倍数,因此要设置开始处(0位置)的颜色。

条纹背景

传统做法:通过两种颜色进行设置

新的思路:
方法一:首先通过background-color设置背景色,然后通过background-image:linear-gradient(rgba(0,0,0,0.2) 50%,transparent 50%);以及background-size:auto 2em;创建一个“黑白条”盖在上面并且高度为2em(1em代表为1行)。

background-color:red;
background-image:linear-gradient(rgba(0,0,0,0.2) 50%,transparent 50%);
background-size:auto 2em;

方法二:通过linear-gradient()直接设置两种颜色,然后设置background-size 大小,根据background直接复制平铺。

方法三:通过repeating-linear-gradient()直接设置颜色自动重复。

错落相间的背景图案

1、通过设置两个linear-gradient(),然后各自设置其background-position,来使的背景图案错落开来。
2、矩形的背景图案,可以通过linear-gradient()45deg的创建2个直角三角形拼接而成。

带有图案的边框

传统做法:通过创建两个div,外层的div设置背景图片,内层的div在背景图片上覆盖一层白色背景色,两层之间的差距就形成了图案边框。

新的思路:
1、background可以设置多个背景,但是白色背景色会默认在最底层,无法上浮到图片上方,所以白色背景色通过线性渐变来实现,并且将背景图片的铺盖范围设置为border-box。
2、background-origin默认值是padding-box,因此图片默认是放置在padding-box的原点上,然后通过平铺复制蔓延到border-box,因此border区域的图片显示异常,所以要将border-origin设置border-box。

padding: 1em;
border: 1em solid transparent;
background: linear-gradient(white, white),
	        url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSGTVf63Vm3XgOncMVSOy0-jSxdMT8KVJIc8WiWaevuWiPGe0Pm);
background-size:cover;
background-clip:padding-box,border-box;
background-origin:border-box;

注:除了图片之外还可以通过渐变来实现有规律的边框图案。

第三章 形状

半椭圆/四分之一椭圆/八分之一椭圆

通过border-radius可以分别设置每个角的水平和垂直圆形半径,进而达到对每个角的变形设置。

平行四边形

传统做法:对元素添加skew()属性进行变形。
缺点:元素的形状是发生了改变,但是内部的文本和其他元素也都全部发生了变形。

新的思路:
方法一:通过嵌套元素,外部元素负责形状的变形,内部元素进行反向变形,使元素恢复正常。如果需要图片填充变形的形状,除了对内部图片进行反向变形外,还需要scale放大,填充满整个变形图案。
缺点:需要添加额外的html元素。

方法二:通过添加伪元素,让伪元素来完成形状的变形,元素本身不发生变化。
缺点:无法使图片填充变形的形状。

.button {
	position: relative;
	display: inline-block;
}
.button::before {
	content: ''; /* To generate the box */
	position: absolute;
	top: 0; right: 0; bottom: 0; left: 0; /* 能够自适应元素大小的变化 */
	z-index: -1; /* 背景颜色不会覆盖在字体上方 */
	background: #58a;
	transform: skew(45deg);
}

注:以上方法适用于任何我们想要变形一个元素而不想变形它的内容情况

菱形图片

除了使用“平行四边形”中的方法一来创建一个菱形图片之外,还可以使用clip-path属性来直接对图片元素进行裁剪。
缺点:需要提前考虑clip-path属性的兼容性。

img {
	-webkit-clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
	clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}

切角效果

方法一:
1、切角效果的核心方式就是使用:linear-gradient(45deg, transparent 15px, red 0);通过渐变来实现一个透明的小角,导致视觉效果像切角了。
2、当对元素进行多个切角时,将背景进行划分,每块单独做切角处理,然后定位每块的位置,“拼接”成一个背景。

div{
	background: #58a;
	background: linear-gradient(135deg, transparent 15px, #58a 0) top left,
	            linear-gradient(-135deg, transparent 15px, #58a 0) top right,
	            linear-gradient(-45deg, transparent 15px, #58a 0) bottom right,
	            linear-gradient(45deg, transparent 15px, #58a 0) bottom left;
	background-size: 50% 50%;
	background-repeat: no-repeat;
}

3、要实现弧形切角(内凹圆角)时,只需要将径向渐变替换线性渐变即可。

div{
	background: #58a;
	background:	radial-gradient(circle at top left, transparent 15px, #58a 0) top left,
	            radial-gradient(circle at top right, transparent 15px, #58a 0) top right,
	            radial-gradient(circle at bottom right, transparent 15px, #58a 0) bottom right,
	            radial-gradient(circle at bottom left, transparent 15px, #58a 0) bottom left;
	background-size: 50% 50%;
	background-repeat: no-repeat;
}

缺点:只能是纯色背景。

方法二:通过SVG画一个切角效果的图片,然后基于border-image的工作原理,将切角效果应用于border上。

div{
	border: 15px solid transparent;
	border-image: 1 url('data:image/svg+xml,\
	                      <svg xmlns="http://www.w3.org/2000/svg" width="3" height="3" fill="%2358a">\
	                      <polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2" />\
	                      </svg>');
	background: #58a;
	background-clip: padding-box;
}

缺点:背景颜色只能是纯色或者是边缘接近纯色的背景图片。

方法三:使用clip-path对元素进行裁剪,裁剪出切角效果,无论你的背景是什么这种方法都能够适用,但是要注意clip-path属性的兼容性。

div {
	height:50px;
	background: #58a;
	-webkit-clip-path: 
		polygon(20px 0, calc(100% - 20px) 0, 100% 20px, 100% calc(100% - 20px),
		calc(100% - 20px) 100%,
		20px 100%, 0 calc(100% - 20px), 0 20px);
	clip-path:
	 	polygon(20px 0, calc(100% - 20px) 0, 100% 20px, 100% calc(100% - 20px),
	 	calc(100% - 20px) 100%,
	 	20px 100%, 0 calc(100% - 20px), 0 20px);
}

梯形图案

1、通过元素3D旋转投影成2D的梯形形状,然后通过scale将元素按照比例放大,弥补因为旋转造成的高度缩小。
2、通过固定元素的transform-origin,选择元素的哪几条边不需要变化。
3、使用添加伪元素变形方案,因为3D旋转无法进行反向变形,因此不采纳嵌套元素变形方案。
缺点:同种旋转角度不同的宽度,梯形元素的倾斜角因为宽度原因会不相同。

div::before {
	content: ''; /* To generate the box */
	position: absolute;
	top: 0; right: 0; bottom: 0; left: 0;
	z-index: -1;
	background: red;
	transform: scaleY(1.5) perspective(.5em) rotateX(5deg);
	transform-origin: bottom; /* 通过设置transform-origin为bottom left或bottom right可以得到左侧倾斜和右侧倾斜的梯形 */
}

饼状图

方法一:
步骤一:
1、通过创建一个阴阳图,左边为饼状图默认颜色,右边为饼状图显示颜色。
2、0~50%通过创建伪元素(颜色为默认颜色),挡住右边的显示颜色,通过旋转(0~0.5turn),一点点将显示颜色显示出来。
3、50%~100%通过创建伪元素(颜色为显示颜色),通过旋转(0~0.5turn),一点点覆盖左边的默认颜色。
4、通过animation切换伪元素背景色与重置旋转角度。

.pie {
	width: 100px; height: 100px;
	border-radius: 50%;
	background: yellowgreen;
	background-image: linear-gradient(to right, transparent 50%, currentColor 0);
	color: #655;
}

.pie::before {
	content: '';
	display: block;
	margin-left: 50%;
	height: 100%;
	border-radius: 0 100% 100% 0 / 50%;
	background-color: inherit;
	transform-origin: left;
	animation: spin 3s linear infinite, 
				bg 6s step-end infinite;
}

@keyframes spin {
	to { transform: rotate(.5turn); }
}
@keyframes bg {
	50% { background: currentColor; }
}

步骤二:
1、通过animation-delay将直接跳转到动画的指定时间点。

// html
<div class='pie'>50%</div>
// css
.pie {
	display: inline-block;
	position: relative;
	width: 100px;
	line-height: 100px;
	border-radius: 50%;
	background: yellowgreen;
	background-image: linear-gradient(to right, transparent 50%, #655 0);
	color: transparent;
	text-align: center;
}

@keyframes spin {
	to { transform: rotate(.5turn); }
}
@keyframes bg {
	50% { background: #655; }
}

.pie::before {
	content: '';
	position: absolute;
	top: 0; left: 50%;
	width: 50%; height: 100%;
	border-radius: 0 100% 100% 0 / 50%;
	background-color: inherit;
	transform-origin: left;
	animation: spin 50s linear infinite,
				bg 100s step-end infinite;
	animation-play-state: paused;
	animation-delay: inherit;
}
// js
var _dom = document.querySelector('.pie')
_dom.style.animationDelay = '-' + parseFloat(_dom.textContent) + 's';

方法二:
1、使用SVG中的虚线描边,将圆形描边的大小设置为半径的2倍,能够完全覆盖圆形。
2、设置虚线的间隔为圆形的周长,能够保证只显示一块虚线。
3、设置虚线的长度为所占比例,显示出我们所需要的饼状图。

// html
<svg viewBox='0 0 32 32'>
	<circle r='16' cx='16' cy='16'
</svg>
// css
svg {
	width:100px;
	height:100px;
	background:yellowgreen;
	border-radius:50%;
	transform:rotate(-90deg);
}
circle{
	fill:yellowgreen;
	stroke:#655;
	stroke-width:32; 
	stroke-dasharray:20 100; /* 半径为16,会使的周长刚刚好为100,通过设置虚线长度,直接就能够显示为百分比饼状图 */
}

总结

通过以上的案例发现,元素的变形主要通过以下几种方式:
方法一:使用transform变形属性对元素进行2D/3D变形,然后通过scale方法元素来弥补因为变形所造成的宽高损失。

方法二:使用clip-path属性直接对元素进行裁剪。

方法三:使用linear-gradient()对元素进行处理。

第四章 视觉效果

单侧投影/邻边投影/双侧投影

1、box-shadow的第四个参数,称为扩张半径,可以根据指定的值去扩大或者缩小投影的的尺寸。
2、根据扩张尺寸,投影时如果阴影在别的边漫出来了,我们可以设置扩张尺寸为负,这样就能够在保留阴影效果的同时,将其他边的阴影缩进去已到达去除阴影的效果。

不规则投影

1、box-shadow的投影无法作用于伪元素或者半透明的装饰上(比如虚线中间的缝隙,切角效果)
方法一:使用CSS滤镜filter属性的drop-shadow()函数,它可以为任何非透明的地方添加上阴影,参数等同于box-shadow但是不支持inset关键字和扩张半径。

div {
	filter:url(drop-shadow.svg#drop-shaodow) // 附上一个SVG滤镜可以得到稍好一点的浏览器支持度
	filter: drop-shadow(2px 2px 10px rgba(0,0,0,.5));
}

缺点:drop-shaodow可以为任何非透明的地方添加上阴影,因此如果你的背景是透明的,则drop-shadow会为字体添加上阴影。

图片染色效果

方法一:在图片的上层覆盖一层半透明的纯色或者把图片设为半透明并覆盖在一层实色背景之上。
缺点:并不是真正的染色效果,并且削弱了图片的对比度。

方法二:把图片放置在<canvas>中,并利用脚本对其进行染色处理。

方法三:通过多个filter滤镜组合对图片进行处理。

img {
	// 根据需求自由组合滤镜
	filter: 
		sepia()  // 将图像转换为深褐色
		saturate(4) // 转换图像饱和度
		hue-rotate(295deg); // 给图像应用色相旋转
}

缺点:当添加动画将img切换会原图的效果时,切换效果不是一点点的渐变而是将滤镜一个个剔除的过渡变化。

方法四:混合模式-mix-blend-mode,只需要把图片包裹在一个容器中,并为这个容器的背景色设置为我们想要的主色调。

<div>
	<img src='demo.jpg'/>
</div>
div {
	background:rgba(255,0,0,0.5);
}
img {
	mix-blend-mode:luminosity;
}

缺点:要注意属性的兼容性,混合模式不支持动画,可以将容器的背景色设置为透明来实现正常/染色的切换。

方法五:混合模式-background-blend-mode,只需要使用一个div元素,将第一层背景设置为要染色的图片,将第二层背景设置为我们想要的主色调。

<div style="background-image:url(demo.jpg)"></div>
div {
	width: 640px; 
	height: 440px;
	background-size: cover;
	background-color: rgba(255,0,0,0.5);
	background-blend-mode: luminosity;
}

缺点:要注意属性的兼容性,混合模式不支持动画,可以将容器的背景色设置为透明来实现正常/染色的切换。

毛玻璃效果

1、背景图片之上存在文字,文本层所覆盖的那部分图片区域作模糊处理。
方法一: 为文本层添加一个伪元素,伪元素的设置的背景图片与之前的背景图片一致,导致伪元素的背景图片重叠在原背景图片上方,然后只需要对伪元素背景图片做模糊处理就可以了。

div, .text::before {
	background: url("demo.jpg") 0 / cover fixed;
}

.text {
	position: relative;
	background: hsla(0,0%,100%,.25) border-box; 
	overflow: hidden;
}

.text::before {
	content: '';
	position: absolute;
	top: 0; right: 0; bottom: 0; left: 0;
	margin: -30px; // 模糊到文本层边缘的时候会消失,扩大一圈区域,避免边缘无模糊。
	z-index: -1;
	-webkit-filter: blur(10px);
	filter: blur(10px);
}

折角效果

1、使用渐变描绘一个空白的折角区域。
2、通过伪元素创建一个折角,通过三角函数来计算折角的大小,通过折角的角度来计算折角旋转角度和偏移角度,来创造出合乎常理的折角效果。
缺点:只能够对纯色背景进行折角效果。

第五章 字体排印

插入换行

传统做法:通过<br/>标签来进行换行

新的思路:可以通过:before或:after伪类选择器中的content设置为“\A”来进行换行,因为Unicode字符中代表换行符的为“0x000A”,在CSS中可以写为“\000A”或者简化为“\A”

div::after {
	content: "\A";
	white-space: pre; // 能够保留源代码中空白和换行符,否则content中的换行符将被忽略。
}

tab-size属性

在需要展示代码的网页上时,通过设置tab-size属性能够指定一个tab缩进几个字符。

为某些字符单独设置字体

传统做法:为需要单独设置字体的字符设置class,通过class为它们设置独特的字体。

新的思路:@font-face中的unicode-range属性可以设置字体的适用于哪些字符。

@font-face {
	font-family: demoFont;
	src: local('Baskerville-Italic'), local('GoudyOldStyleT-Italic'); /* 应用于指定字符的字体 */
	unicode-range: U+26; /* 设置生效字符,'&'的Unicode码位,可以通过'&'.chatCodeAt(0).toString(16)获取'&'的十六进制码位'26',然后前面加上'U+'前缀 */
}

p {
	font-family: demoFont, Helvetica; /* demoFont字体只应用于'&'字符,其他字符会适用后续的字体'Helvetica' */
}

自定义文本下划线

传统做法:通过text-decoration:underline;

新的思路:
1、通过background与background-size设置一条自定义格式的下划线;
2、通过text-shadow可以让字符周围出现一圈白边,使的显示为下划线遇到字母的降部自动断开避让。
3、文本最好放置在行内元素内,这样下划线才能随这文本的换行也进行换行。

span {
	background: linear-gradient(gray, gray) no-repeat;
	background-size: 100% 1px;
	background-position: 0 1.2em;
	text-shadow: .05em 0 white, -.05em 0 white;
}

空心字效果/文字外发光效果/文字凸起效果

通过text-shadow为文本添加效果来实现。

环形文字

1、通过SVG的path画出一个圆形。
2、通过text与textPath添加文本,并通过xlink:href属性将文本链接到路径上。

<div class='demo'>
	<svg viewBox='0 0 100 100'>
		<path d='M0,50 a50,50 0 1,1 0,1z' id='circle'></path>
		<text>
			<textPath xlink:href="#circle">fdsfdsfdsfdsfsdfdsfsdfds</textPath>		
		</text>
	</svg>
</div>

.demo {
	width:300px;
	height:300px;
	margin: 4em auto 0;
}
svg{
	display:block;
	overflow: visible;
}
path {
	fill:none;
}

注:这种方法不但适用于环形文字还适用于所有需要特殊路径排序的文字。

第六章 用户体验

扩大用户交互热区

方法一:使用透明的border扩大交互热区。使用background-clip重新定义背景的填充区域。

.demo1 {
	border:20px solid transparent;
	background-clip:padding-box;
	/* 实际border-radius效果值为border-radius减去border的值 */
	border-radius:25px;
	box-shadow: 0 0 0 1px rgba(0,0,0,0.3) inset;
}

注:
1、如果要设置border-radius的值时需要注意,实际border-radius效果值为border-radius减去border的值。
2、如果要设置边框可以通过内嵌投影在模拟出一道实色边框。
缺点:无法为元素设置box-shadow阴影效果,因为阴影效果时设置border-box外面的。

方法二:使用一个伪元素,扩大伪元素的范围,伪元素的范围也可以触发热区。通过伪元素可以随意设置热区的尺寸、位置、形状。

.demo2 {
	position:relative;
}
.demo2:before {
	content:'';
	position:absolute;
	top:-10px;
	bottom:-10px;
	left:-10px;
	right:-10px;
}

自定义check样式

使用label与checkbox进行相关联,然后将checkbox隐藏起来,最后通过设置label伪元素的样式来顶替checkbox的样式。

创建蒙层

传统做法:创建一个额外HTML元素来设置为蒙层。

新的思路:
方法一:通过body上的伪元素来设置为蒙层。
缺点:伪元素无法绑定独立的JS事件处理函数以及对于元素的z-index设置可能会出现偏差。

方法二:通过box-shadow来设置蒙层。box-shoadow: 0 0 0 50vmax rgba(0,0,0,.8)
缺点:只能覆盖当前窗口,页面如果滚动就会露馅以及无法阻止用户与页面其他元素交互。

方法三:使用dialog元素backdrop伪元素可以只能蒙层。
缺点:要考虑backdrop伪元素的兼容性。

弹出窗口背景模糊

实现效果类似毛玻璃效果,只是模糊的不是一张背景图片而是页面上的任意元素。
方法一:页面的元素全部包裹在一个div中,dialog与这个div同级,dialog弹出的时候,将这个div添加上模糊滤镜blur().

滚动提示

方法一:
1、background-attachment:scroll可以使的背景图片随着元素滚动而固定在元素上,因此可以在滚动的时候创建一个渐变阴影。这样滚动的时候上方都会存在一个阴影。
2、background-attachment:local可以使背景图片相对于元素的内容固定,因此可以创建一个白色遮罩层当用户滚动到顶部的时候遮挡住阴影。

ul {
	overflow: auto;
	background: linear-gradient(white 15px,rgba(255,255,255,0)),
		radial-gradient(at top, rgba(0,0,0,.9), transparent 70%);
	background-repeat:no-repeat;
	background-size:100% 50px,100% 15px;
	background-attachment: local, scroll;
}

交互式图片对比

方法一:将两张图片重叠在一起,然后通过resize:horizontal来调节覆盖在上层的图片width。

<div class="image-slider">
	<div>
		<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/5/2/16a777e69b3ab1e4~tplv-t2oaga2asx-image.image" alt="Before" />
	</div>
	<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/5/2/16a777e6a9e65856~tplv-t2oaga2asx-image.image" />
</div>

.image-slider {
	position:relative;
	display: inline-block;
}

.image-slider > div { /* 覆盖在上层的图片 */
	position: absolute;
	top: 0; bottom: 0; left: 0;
	width: 50%;
	max-width: 100%;
	overflow: hidden;
	resize: horizontal;
}

.image-slider img {
	display: block;
	user-select: none;
}

第七章 结构与布局

自适应内部元素

width与height中存在一些关键词,其中width:min-content将会解析为这个容器内最大的不可断行元素的宽度(即最宽的单词、图片和具有固有宽度的盒元素)

<figure>
	<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/5/2/16a777e6a9e65856~tplv-t2oaga2asx-image.image" />
	<figcaption>
		The great Sir Adam Catlace was named after Countess Ada Lovelace, the first programmer ever.
	</figcaption>
</figure>

figure {
	max-width: 300px; /* 兼容回退处理 */
	max-width: min-content;
	margin: auto;
}

figure > img { max-width: inherit }

精确控制表格样式

通过table-layout:fixed能够让我们更加自由的控制表格中的种种样式。

根据兄弟元素数量/范围设置样式

1、只有1个子元素

li:only-child { /* code */}
或
li:first-child:nth-last-child(1) {/* code */ } /* 元素是第一个元素同时也是最后一个元素,就等同于只有一个元素 */

2、判断有多少个元素

li:first-child:nth-last-child(4) {/* code */ } /* 元素是第一个元素同时也是倒数第四个元素,就等同于一共有四个元素 */
li:first-child:nth-last-child(4)~ li {/* code */} /* 当列表只有4个元素时,为列表设置样式 */

3、根据兄弟元素范围

li:first-child:nth-last-child(n+4)~ li {/* code */} /* n可以为任何数,表示当列表至少含有4个元素时,为列表设置样式 */
li:first-child:nth-last-child(-n+4)~ li {/* code */} /* n可以为任何数,表示当列表最多含有4个元素时,为列表设置样式 */
li:first-child:nth-last-child(n+2):nth-last-child(-n+6)~ li {/* code */} /* n可以为任何数,表示当列表含有2~6个元素时,为列表设置样式 */

满幅的背景,定宽的内容

传统做法:外层的html元素设置为满幅的背景,然后内层html设置为定宽居中的内容。
缺点:需要额外一层html元素。

新的思路:通过calc()函数来设置内容的左右边距,使其定宽居中。

outer {
	padding:0 calc(50% - 450px);/* 通过calc()函数计算内容居中时元素的左右边距;450px为内容定宽900px/2计算得到 */
}

垂直居中

CSS 技巧篇(七):设置元素居中

紧贴底部的页脚

方法一:设置mai主体元素的min-height为窗口高度减去footer的高度。

.main {
	min-height:calc(100vh - 100px) /* 100px为footer的高度*/
}

缺点:要求footer的高度必须是固定已知的。

方法二:将body元素设置为flexBox,然后设置main主体元素设置为flex:1。

body {
	display:flex;
	flex-flow:colum;
	min-height:100vh;
}
.main {
	flex:1;
}

第八章 过渡与动画

缓动效果

在animation或者transition中的调速参数除了ease等关键词之外,还可以通过cubic-bezier()函数自定义调速函数。
cubic-bezier()函数在线调试可以前往贝塞尔曲线在线调试。

逐帧动画(以loading动画为例)

传统做法:设置为一张gif图片。
缺点:gif无法设置为透明并且无法修改动画的属性。

新的思路:将动画划分为逐帧的图片,然后通过animation对图片进行过渡切换,steps()能够将动画切分为多帧,而且是帧与帧之间的硬切。

@keyframes loader {
	to { background-position: -800px 0; }
}

.loader {
	width: 100px; height: 100px;
	text-indent: 999px; overflow: hidden; /* Hide text */
	background: url(http://dabblet.com/img/loader.png) 0 0;
	animation: loader 1s infinite steps(8);
}

闪烁效果

方法一:动画的50%设置为transparent,这样动画就形成了从有->无→有的变化。

@keyframes blink { 50% { color: transparent } }
.blink-smooth {
	animation: 1s blink 3;
}

方法二:通过animation中的animation-direction:alternate属性能够设置动画的循环周期。

@keyframes blink { to { color: transparent } }
.blink-smooth {
	animation: .5s blink 6;
	animation-direction: alternate;
}

打字动画

1、通过animation和steps控制width的值,将字符一个个的显示出来;
2、通过border-right来创造光标;
3、ch可以表示为每一个字符的宽度。

@keyframes typing {
	from { width: 0 }
}

@keyframes caret {
	50% { border-right-color: currentColor; }
}

h1 {
	/*width: 8.25em;*/
	width: 15ch; /* ch单位为每个字符的宽度。 */
	white-space: nowrap; /* 禁止内容换行 */
	overflow: hidden;
	border-right: .05em solid transparent; /* 通过border-right来创造光标 */
	animation: typing 8s steps(15),   /* 打字动画 */
	           caret 1s steps(1) infinite; /* 光标动画 */
}

沿环形路径平移的动画

方法一:在图片外在包裹一层div,div为顺时针围绕一个大圆的圆心正常旋转,而img则是相对自身中心逆时针旋转来抵消外部旋转。

<div class="path">
	<div class="avatar">
		<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/5/2/16a777e684aa60d7~tplv-t2oaga2asx-image.image" />
	</div>
</div>

@keyframes spin {
	to { transform: rotate(1turn); }
}

.avatar {
	animation: spin 3s infinite linear;
	transform-origin: 50% 150px;
}

.avatar > img {
	animation: inherit;
	animation-direction: reverse;
}

/* Anything below this is just styling */

.avatar {
	width: 50px;
	margin: 0 auto;
	border-radius: 50%;
	overflow: hidden;
}

.avatar > img {
	display: block;
	width: inherit;
}

.path {
	width: 300px; height: 300px;
	padding: 20px;
	border-radius: 50%;
	background: #fb3;
}

方式二:通过translate的移动来顶替transform-origin的效果

<div class="path">
	<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/5/2/16a777e684aa60d7~tplv-t2oaga2asx-image.image" class="avatar" />
</div>

@keyframes spin {
	from {
		transform: 
				   translateY(150px) translateY(-50%)
				   rotate(0turn)
		           translateY(-150px) translateY(50%)
		           rotate(1turn)
	}
	to {
		transform: 
				   translateY(150px) translateY(-50%)
				   rotate(1turn)
		           translateY(-150px) translateY(50%)
		           rotate(0turn);
	}
}

.avatar {
	animation: spin 3s infinite linear;
}

/* Anything below this is just styling */

.avatar {
	margin: 0 auto;
	display: block;
	width: 50px;
	border-radius: 50%;
	overflow: hidden;
}

.path {
	width: 300px; height: 300px;
	padding: 20px;
	border-radius: 50%;
	background: #fb3;
}

总结 总结正文