注意:本文章所有阶段皆有demo预览,可见每章最后一节“源码及预览”。
起
本篇博文主要是通过一些实例来学习用css实现一些动画及特殊效果。
在以下的内容中,可能会用到以下知识点: transform
、transition
、animation
、景深等。首先简单介绍一下这些知识点。然后在后面的实例中会详细的介绍这些知识点。
知识点
transform
可以对元素进行移动、旋转、缩放、拉伸等转换。具体的用法可以参照2D转换和3D转换。
transition
transition
用来给元素的变换添加一些效果。具体的用法可以参照过渡。
animation
animation
用来给元素添加动画。使用 @keyframes
规则。具体的用法可以参照动画。
景深
景深就是肉眼距离屏幕的距离,将当前容器变成3D的,让本来的皮影戏变成舞台剧。景深设置的越大,元素离我们越远,反之则是离我们越近。用法如下。
<div class="box"> <div></div> </div> <style> .box { perspective: 500px; } </style>
|
浏览器坐标轴
这里以一个正方形的div
为例。当一个元素进行一些2D或者3D变换时,作用的坐标轴的中心点就是这个元素的中心。需要注意的是,当元素进行变换时,其坐标轴会跟随移动,始终保持在元素中心。
一些实例
旋转的正方体
舞台
首先我们给正方体搭建一个舞台,让它有机会可以展示自己。
<div class="container"> <div class="cube-box"> </div> </div>
|
搭建舞台的核心是设置景深,以及让子元素在3D空间内变换,因为正方体是一个三维图案,需要借助景深将屏幕变成一个三维空间。这里需要注意的是,设置景深和变换类型需要设在不同的元素上,景深在父元素上,变换类型在子元素上。添加如下css代码。
.container { position: relative; margin: 20px auto; width: 500px; height: 500px; background: radial-gradient(rgba(0, 0, 0, 0) 10%, rgba(0, 0, 0, .5) 100%); perspective: 500px; }
.cube-box { position: absolute; top: 200px; left: 200px; width: 100px; height: 100px; line-height: 100px; text-align: center; transform-style: preserve-3d; }
|
这样,一个简易舞台就搭建起来了,当然,在没有添加任何“演员”之前,这个舞台看不出来任何特殊之处。
坐标轴
在添加“演员”之前,为了后面方便理解,我们先构建出一个坐标轴(包含xyz轴)。
<div class="container"> <div class="cube-box"> <div class="x-axis"></div> <div class="y-axis"></div> <div class="z-axis"></div> </div> </div>
|
然后分别添加三个轴的样式。首先是x轴。
.x-axis { position: absolute; top: 50%; left: 50%; margin-left: -100px; margin-top: -1px; width: 200px; height: 1px; background: #000; }
.x-axis::before { content: '>'; display: block; position: absolute; width: 16px; height: 16px; line-height: 16px; font-size: 12px; text-align: center; right: -7px; top: -9px; }
.x-axis::after { content: 'x'; display: block; position: absolute; width: 16px; height: 16px; line-height: 16px; font-size: 12px; text-align: center; right: 4px; top: -16px; }
|
这里通过position
将x轴定位在了屏幕的正中心,并且通过两个伪元素给坐标轴添加了标识。同样的方式来添加一下y轴的样式。
.y-axis { background: #000; width: 1px; height: 200px; position: absolute; top: 50%; left: 50%; margin-top: -100px; margin-left: -1px; }
.y-axis::before { content: '>'; display: block; position: absolute; width: 16px; height: 16px; line-height: 16px; font-size: 12px; text-align: center; right: -8.5px; bottom: -7px; transform: rotateZ(90deg); }
.y-axis::after { content: 'y'; display: block; position: absolute; width: 16px; height: 16px; line-height: 16px; font-size: 12px; text-align: center; right: -14px; bottom: 6px; }
|
在这里用到了transform: rotateZ(90deg)
来实现将>
旋转90°达到标向y轴正轴方向的目的。我们来学习一下rotate()
。
{ transform: rotate(45deg); transform: rotate3d(1, -1, 0, 45deg); transform: rotateX(45deg); transform: rotateY(45deg); transform: rotateZ(45deg); }
|
注意:rotate3d(x,y,z,angle)
前三个值的范围是0到1,但是实际上可以是任意数值,这个数值会使该方向上旋转的角度发生变化,但是不是单纯的矢量值与角度的乘积,而是有一个特殊的变化弧度。
最后是z轴的样式。
.z-axis { background: #000; width: 200px; height: 1px; position: absolute; top: 50%; left: 50%; margin-left: -100px; margin-top: -1px; transform: rotateY(90deg); }
.z-axis::before { content: '<'; display: block; position: absolute; width: 16px; height: 16px; line-height: 16px; font-size: 12px; text-align: center; left: -8px; top: -9px; }
.z-axis::after { content: 'z'; display: block; position: absolute; width: 16px; height: 16px; line-height: 16px; font-size: 12px; text-align: center; left: 4px; top: -16px; }
|
z轴上用到了transform: rotateY(90deg)
将坐标轴放到正确的方向上。需要注意的是当z轴到正确的方向上时,我们是看不到z轴的,需要先将他的伪元素样式调好再进行旋转。
下面我们进入正题,正式开始画正方体。
六个面
首先我们先画出正方体的六个面。
<div class="container"> <div class="cube-box"> <div class="item front">front</div> <div class="item behind">behind</div> <div class="item left">left</div> <div class="item right">right</div> <div class="item top">top</div> <div class="item down">down</div> <div class="x-axis"></div> <div class="y-axis"></div> <div class="z-axis"></div> </div> </div>
|
然后给这六个面添加样式,因为是相同的六个面,所以可以使用同一套样式。添加如下CSS代码。
.item { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #fff; opacity: 0.6; }
|
添加完样式看效果,发现正方体的6个面都已经堆叠在一起了,这时候应该让他们各自归位了,首先先看正面和背面。
正面和背面都在xy平面上,也就是肉眼看到的这一面。其中正面是不需要动的,只需要安静的呆在你的面前就可以了。而背面需要向z轴负半轴方向移动正方体的边长的长度。为了方便四个侧面的处理,这里将正面向z轴正半轴的方法移动半个边长的长度,背面向z轴负半轴方向移动半个边长的长度。元素的移动就需要用到 transform
的 translate()
方法。
{ transform: translate(50px, -50px); transform: translate3d(50px, 50px, 50px); transform: translateX(50px); transform: translateY(50px); transform: translateZ(50px); }
|
添加如下CSS代码。
.front { transform: translateZ(50px); } .behind { transform: translateZ(-50px); }
|
因为他们的中心坐标始终都在同一个XY坐标点上。所以这时候只能透过半透明的 front
看到后面“变小”的其他面。
现在我们来处理左边和右边这两面,这两个面在yz平面上。发挥你的想象力想象一下,他们两个面在xy面上都需要绕Y轴进行旋转才能到yz面上去,也就是要用到 transform
的 rotate()
方法。而为了让其能够正常的旋转到正确的位置上,还需要利用 translate
将其旋转点移动到两边。添加如下css代码。
.left { transform: translateX(-50px) rotateY(90deg); } .right { transform: translateX(50px) rotateY(-90deg); }
|
同样的思想,让我们处理一下 top
和 down
这两个面。这两个面为了出现在上面和下面,也就是xz平面上,需要绕x轴旋转90度。添加如下css代码。
.top { transform: translateY(-50px) rotateX(90deg); } .down { transform: translateY(50px) rotateX(-90deg); }
|
这个时候一个正方体就已经成型了,可是还是只能看到一个面啊,那我们就让其动起来。
正方体旋转动画
这里默认你已经了解了上面我所说的 animation
相关的知识点了。
首先我们定义一个旋转的动画,控制不同阶段的不同旋转角度,命名为 cubeRotating
。
@keyframes cubeRotating { 0% { transform: rotate(0deg); } 25% { transform: rotateX(180deg); } 50% { transform: rotateX(360deg) rotateY(180deg); } 75% { transform: rotateY(360deg) rotateZ(180deg); } 100% { transform: rotateZ(360deg); } }
|
这里定义了5个阶段的状态。0% 和 100% 都是初始状态。中间的几个阶段分别进行了不同程度和方向上的旋转。需要注意的是,在每个阶段完成后添加下个阶段的变换时,都需要以变换后的新坐标系为基准添加。
前面我们定义了正方体外壳 div
的位置,其中心点刚好是正方体的正中心。给其添加此动画。
.cube-box {
animation: cubeRotating 6s linear 1s infinite; }
|
源码&预览
预览:
源码:GITHUB
赛博朋克的按钮特效
无意间发现赛博朋克2077的官网中的“现已发售”按钮的特效与游戏中的特效差不多,非常具有科幻感,于是乎就研究了一下这种特效。
按钮
首先我们做一个类似的按钮。比较简单,就直接放代码了。
<div class="btn"> <span class="btn-text">现已发售</span> </div>
|
.btn { position: relative; display: flex; align-items: center; justify-content: center; margin: 100px 0 0 265px; width: 337px; height: 85px; font-size: 26px; line-height: 1; font-weight: 700; color: #fff; letter-spacing: 2px; background: linear-gradient(45deg, transparent, transparent 5%, #ff003c 5%, #ff003c); cursor: pointer; }
|
官网对于按钮背景使用的是图片,这里通过线性渐变实现了类似的效果。只要使两个颜色的位置相同,就会产生这样割裂的效果。
clip-path
在开始做特效之前,我们先看看这个css属性:clip-path
。
clip-path CSS 属性使用裁剪方式创建元素的可显示区域。区域内的部分显示,区域外的隐藏。
从简介来看,这个属性是用来裁剪元素的,通过控制可显示区域来达到画画的效果,类似于svg中的<clipPath>
。
他可以接收以下几种类型的值。
clip-path: <clip-source> | [ <basic-shape> || <geometry-box> ] | none
其中
<clip-source> = <url>
<basic-shape> = <inset()> | <circle()> | <ellipse()> | <polygon()> | <path()>
<geometry-box> = border-box | padding-box | content-box | margin-box | fill-box | stroke-box | view-box
<clip-source>用 <url> 表示剪切元素的路径。
<basic-shape>是一种形状,其大小和位置由<几何盒>值定义。如果没有指定几何框,则边框将用作参考框。
<geometry-box>如果同 <basic-shape> 一起声明,它将为基本形状提供相应的参考框盒。通过自定义,它将利用确定的盒子边缘包括任何形状边角(比如说,被 border-radius 定义的剪切路径)
下面对其详细的值做一个介绍。
值类型 |
说明 |
none |
默认值。不裁剪 |
circle([r]? [at x y]?) |
定义一个圆形剪切路径,其中r为半径,支持百分比,x、y为圆心坐标 |
ellipse([xr yr]? [at x y]?) |
定义一个椭圆形剪切路径,其中xr为x轴半径,yr为y轴半径,支持百分比,x、y为圆心坐标 |
inset(top right bottom left [round radius]?) |
定义一个矩形,top、right、bottom和left分别为距离四个边的距离,可缩写。redius为圆角值,与border-redius值写法一致 |
polygon(<fill-rule>?, [x, y]#) |
定义一个多边形,fill-rule为填充规则,可选值有nonzero和evenodd,默认值是nonzero。x、y为多边形顶点坐标 |
path([<fill-rule>,]? <string>) |
定义一段路径,fill-rule为填充规则,string为一个字符串,内容为路径点 |
margin-box |
使用 margin box 作为引用框 |
padding-box |
使用 padding box 作为引用框 |
content-box |
使用 content box 作为引用框 |
border-box |
使用 border box 作为引用框 |
fill-box |
利用对象边界框作为引用框。 |
stroke-box |
使用笔触边界框(stroke bounding box)作为引用框。 |
view-box |
使用最近的 SVG 视口(viewport)作为引用框。 |
简单地说就是,可以直接引入外部svg资源来画路径;或者自己通过预设的方案画一个路径,同时标注其作用的区域。
官方的介绍还是不太清楚,我们通过分别用几个例子来展示这几个值的用法。
circle()
下面的代码是几种不同的携带参数的效果的展示。最后一个是利用此方法实现了一个简单的动画。
注意:当未携带参数时,半径为较短边的边长,效果等同于border-radius:50%;
。
ellipse()
下面的代码是几种不同的携带参数的效果的展示。最后一个是利用此方法实现了一个简单的动画。
注意:当未携带参数时,两个半径分别为所在方向的边的边长的一半。
inset()
下面的代码是几种不同的携带参数的效果的展示。最后一个是利用此方法实现了一个简单的动画。
注意:当未携带参数时,效果等同于参数为0,不裁剪。
polygon()
下面的代码是几种不同的携带参数的效果的展示。最后一个是利用此方法实现了一个简单的动画。多边形做动画时,需前后顶点的个数一致,不然动画无法正确执行。
注意:当未携带参数时,不裁剪。
path()
下面的代码只展示了一个。这是因为目前我也没完全的学会去自己画路径,后面如果学的差不多了再来补充。想要学习这方面内容可以看最后一节我列出的文章,或者直接搜索svg的path详解,二者说一样的。
<geometry-box>
所有的浏览器对geometry-box的支持都不够,现在没有一个浏览器可以看出来效果,我们只需要知道默认值是padding-box
即可。
源码及预览
预览:
源码:GITHUB
对于上一小节通过渐变实现的缺角按钮也可以通过这种方式来实现。下面我们用clip-path
来试着实现一下缺角按钮,只需要用polygon()
将所有的顶点都按顺序写出来即可。
.btn { background: #ff003c; clip-path: polygon(0 0, 337px 0, 337px 85px, 20px 85px, 0 65px); }
|
用上面的css替换原来的渐变,可以发现效果和原来是基本一样的。下面我们试试去实现官网上的不规则播放器。这里我们以一个图片替代播放器。
不规则播放器
对于这个不规则的播放器,我们同样可以使用polygon()
将所有的边全部描述出来。
<div class="video-box"> <div class="video"></div> </div>
|
.video-box { width: 669px; height: 375px; margin: 100px; clip-path: polygon(98% 5%, 95% 0%, 25% 0%, 20% 5%, 0% 5%, 0% 66.67%, 23% 100%, 60% 100%, 65% 95%, 100% 95%, 100% 60%, 98% 55%, 98% 5%); }
.video-box .video { width: 100%; height: 100%; background: url(https://cyberpunk.gog-statics.com/build/images/home3/screen-image-about-768457e5.jpg) 0 0/100% 100%; }
|
特效
下面我们正式进入特效部分。通过仔细观察官网中的按钮可以发现,这个按钮其实是有两层的,且两层都一模一样。下面的一层做为主要显示,上面的一层通过剪切以及移动来达到现在我们看到的效果。
我们先在原来的基础上添加一个同样的按钮置于上层。
<div class="btn"> <span class="btn-text">现已发售</span> <span class="copy-btn"></span> </div>
|
.copy-btn { position: absolute; display: flex; align-items: center; justify-content: center; width: 337px; height: 85px; font-size: 26px; line-height: 1; font-weight: 700; color: #fff; letter-spacing: 2px; cursor: pointer; background: linear-gradient(45deg, transparent, transparent 5%, #ff003c 5%, #ff003c); }
.copy-btn::after { content: '现已发售'; display: inline-block; }
|
在这里我们将背景效果的呈现全部换为了渐变。这是因为接下来的动画我们还会用到clip-path
进行剪切,如果上层的背景也用clip-path
会因为剪切点不同而导致动画部分效果不生效。对于下层是因为用了clip-path
会导致上层的移动无法在区域外显示。
下面我们定义一个动画。动画也很简单,就是在不同的阶段进行不同区域的剪切,然后再加上translate()
实现左右闪动的效果。
@keyframes glitch-anim-1 { 0% { transform:translateZ(0); clip-path:polygon(0 2%, 100% 2%, 100% 5%, 0 5%) } 2% { clip-path:polygon(0 78%, 100% 78%, 100% 100%, 0 100%); transform:translate(-5px) } 6% { clip-path:polygon(0 78%, 100% 78%, 100% 100%, 0 100%); transform:translate(5px) } 8% { clip-path:polygon(0 78%, 100% 78%, 100% 100%, 0 100%); transform:translate(-5px) } 9% { clip-path:polygon(0 78%, 100% 78%, 100% 100%, 0 100%); transform:translate(0) } 10% { clip-path:polygon(0 54%, 100% 54%, 100% 44%, 0 44%); transform:translate3d(5px, 0, 0) } 13% { clip-path:polygon(0 54%, 100% 54%, 100% 44%, 0 44%); transform:translateZ(0) } 13.1% { clip-path:polygon(0 0, 0 0, 0 0, 0 0); transform:translate3d(5px, 0, 0) } 15% { clip-path:polygon(0 60%, 100% 60%, 100% 40%, 0 40%); transform:translate3d(5px, 0, 0) } 20% { clip-path:polygon(0 60%, 100% 60%, 100% 40%, 0 40%); transform:translate3d(-5px, 0, 0) } 20.1% { clip-path:polygon(0 0, 0 0, 0 0, 0 0); transform:translate3d(5px, 0, 0) } 25% { clip-path:polygon(0 85%, 100% 85%, 100% 40%, 0 40%); transform:translate3d(5px, 0, 0) } 30% { clip-path:polygon(0 85%, 100% 85%, 100% 40%, 0 40%); transform:translate3d(-5px, 0, 0) } 30.1% { clip-path:polygon(0 0, 0 0, 0 0, 0 0) } 35% { clip-path:polygon(0 63%, 100% 63%, 100% 80%, 0 80%); transform:translate(-5px) } 40% { clip-path:polygon(0 63%, 100% 63%, 100% 80%, 0 80%); transform:translate(5px) } 45% { clip-path:polygon(0 63%, 100% 63%, 100% 80%, 0 80%); transform:translate(-5px) } 50% { clip-path:polygon(0 63%, 100% 63%, 100% 80%, 0 80%); transform:translate(0) } 55% { clip-path:polygon(0 10%, 100% 10%, 100% 0, 0 0); transform:translate3d(5px, 0, 0) } 60% { clip-path:polygon(0 10%, 100% 10%, 100% 0, 0 0); transform:translateZ(0); } 60.1% { clip-path:polygon(0 0, 0 0, 0 0, 0 0); } to { clip-path:polygon(0 0, 0 0, 0 0, 0 0); } }
|
上面的代码中用到了很多的xx.1%
,是为了让每个阶段之间更加具有割裂感,使动画效果更加棒。
最后我们用一下这个动画。
.copy-btn:hover { animation: glitch-anim-1 2s linear alternate infinite; }
|
然后就可以看到和官网一模一样的效果了。
源码及预览
预览:
源码:GITHUB
未完待续
后面的文章会继续进行实例的学习。
参考
菜鸟教程
clip-path
svg之path详解
clippy
CSS 奇思妙想边框动画