为期一个多月,我们针对只用一个 div 元素一共可以写出多少种形状的话题,通过六个篇章(分了八篇文章)进行了详细的展开。
其中,前三个篇章,我们主要围绕欧几里得几何中的基本形状做的展开,其中蕴涵大量的初中几何知识,也让我们可以灵活运用到写这些形状当中。
在平行四边形篇中,我们直接基于块级元素的基本特性,通过对四边形的剖析,从最简单的矩形和正方形。然后,我们再引入 transform 属性,写出平行四边形和菱形。
在三角形与梯形篇中,我们通过对边框属性(border)的详细讲解,并引导大家对边框属性的巧妙运用,写出了各种各样的三角形与梯形。
在弧形篇中,我们通过对边框属性的扩展,引入圆角边框(border-radius)的概念,通过对圆角边框的灵活运用,写出了最基本的圆形和椭圆形,以及蛋的形状。又通过直线与曲线相结合,写出了扇形、半圆、吃豆人等更加有趣的形状。
后三个篇章,我们在前三个篇章的基础上,通过更加灵活的属性与扩展,实现了从单一形状到简易图形的质的飞跃,让我们做到只用一个 div 元素就可以实现一些图形的效果。
在多边形篇中,我们通过对阴影属性(box-shadow)的详细讲解,引导大家巧妙地使用阴影属性对原 div 元素的形状进行全等或相似的“复制”操作,实现了更加丰富多彩的图形。
在伪元素篇中,我们通过对伪元素的详细讲解,通过 ::before 和 ::after 两个伪元素,让我们巧妙的在 div 元素中多了两个可以任意操作的子元素。并且通过上、下两个篇章,写了爱心、太极等更多美丽的图形。
最后,我们在不规则图形篇中,引入了裁剪属性(clip-path),通过对 clip-path 属性的教科书式讲解,让我们完全确定,只用一个 div 元素,就可以写出无数个形状以及一些简单的图形。
通过六个篇章的洗礼,只用一个 div 元素,理论上可以写出一幅非常美丽的图片,无论图片中的颜色有多么鲜艳,图片构成有多么复杂!
那么今天,我们就在这六个篇章的基础上,让我们的元素动起来~~~~~
一、过渡与动画
让一个元素动起来,视觉上就是一种动画效果。在 CSS 中,元素的动画效果主要分为过渡和动画两种。
作为本系列的最后一个篇章,本着每一篇文章都干货满满的初衷,除了给大家细讲解过渡和动画的基础知识,还会给大家科普一下动画原理。
1. 视觉暂留
咱们来做一个小小的实验:
如果我们盯着这张反色的照片中间的那个黑点不眨眼五秒钟,五秒钟后照片突然变成下面这张灰度模式的照片:
为了大家方便,我放上一张动图,我们不眨眼的盯着反色照片中间的黑点,五秒钟后反色照片自动换成灰度模式的照片,大家会看到什么?
认真做这个实验的您一定会惊讶的发现,当照片变成灰度模式的时候,您一定会产生一个错觉,感觉照片不仅仅不是灰度模式,而且是彩色的。
这是因为,在照片从反色照片切换到灰度照片的瞬间,反色照片的信号还会在我们的视觉神经中残留大概 100ms 左右的时间,这期间会和灰度照片的信息叠加在一起,我们大脑识别的时候就叠加成了彩色照片了。
人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,视觉的这一现象被称为“视觉残留”。
2. 动画原理
我们使用这个费纳奇镜实验:
我们让这个费纳奇镜旋转起来,每 100ms 旋转 36deg,看看效果:
可以看到,费纳奇镜中的男女开始了华尔兹,猴子也开始了跳圈,感觉画面动起来了。
动画的原理是人眼的视觉暂留现象,也就是说,一组画面连续快速的在人眼前出现,在前一幅画还未从人的视网膜上消失时,就接上下一幅画面,这两幅画面就“接”在了一起,使人们产生画面在动的错觉,也就产生了动画。
3. CSS 过渡
CSS 过渡提供了一种在更改 CSS 属性时控制动画速度的方法。可以让属性变化成为一个持续一段时间的,而不是立即生效的过程。
直接献上 CSS 过渡的属性:
transition-property:
指定哪个或哪些 CSS 属性用于过渡;
只有指定的属性才会在过渡中发生动画,其他属性仍如通常那样瞬间变化;
默认值为 all,代表所有属性;
没有过渡动画为 none;
多个值之间使用逗号分隔。
transition-duration:
指定过渡的时长;
可以为所有属性指定一个值,或者指定多个值,或者为每个属性指定不同的时长;
默认值为 0s,代表没有过渡时长;
过渡时长也可以使用逗号分隔多个值,分别对应不同属性的过渡变化所需要的时长。
transition-timing-function:
指定一个函数,定义属性值怎么变化;
缓动函数定义属性如何计算;
默认值为 ease,代表三次贝塞尔曲线;
最常用的是三次贝塞尔曲线,下面用案例演示。
transition-delay:
指定延迟,即属性开始变化时与过渡开始发生时之间的时长;
默认值为 0s,代表没有延迟时长;
延迟时长也可以使用逗号分隔多个值,分别对应不同属性的过渡变化所需要等待的时长。
div {
position: fixed;
top: 0;
bottom: 0;
left: 0;
margin: auto;
width: 205px;
height: 25px;
background: red;
transition-property: width;
transition-duration: 1s;
transition-timing-function: linear;
transition-delay: 1s;
}
div:hover {
width: 100%;
}
我们想在鼠标悬停的时候,图片宽度变成 100%。为了有一个我们可以看到的过渡效果,我们就设置了 CSS 过渡属性。直接看效果:
可以看到,当鼠标进入到 div 上的时候,div 在等待了 1s 之后才开始变长,变长到 100% 所用时长也是 1s。然后,当鼠标离开之后,也是等待了 1s 之后,div 的宽度缩短回 205px,div 缩短所用时长也是 1s。并且有一点需要强调,两次变化中的速度都是匀速的。
速度是匀速的,是因为 transition-timing-function 属性的值是一个 linear 关键字。可见,transition-timing-function 属性具有多个关键字,我们就不每个盘点了,直接通过一个案例演示一下吧!
<section>
<div>ease</div>
<div>ease-in</div>
<div>ease-out</div>
<div>ease-in-out</div>
<div>linear</div>
<div>cubic-bezier(0.2, -2, 0.8, 2)</div>
<div>jump-start</div>
<div>jump-end</div>
<div>jump-both</div>
<div>jump-none</div>
<div>step-start</div>
<div>step-end</div>
</section>
在 HTML 中,我们把各个关键字各放到一个 div 元素中,并一起放到一个 section 元素中。
section {
margin: 10px 700px 10px 10px;
border: 1px solid green;
}
通过 CSS 控制了 section 的外边距和一个绿色的边框,让我们可以看清 section 容器的大小和位置。
div {
width: 205px;
height: 25px;
background: red;
margin-top: 25px;
white-space: nowrap;
}
控制了 div 元素的宽度为 205px,给一个红色的背景色,以便于我们可以看清每一个 div 的大小。
div {
width: 205px;
height: 25px;
background: red;
margin-top: 25px;
white-space: nowrap;
transition-property: width;
transition-duration: 5s;
}
设置 transition-property 属性的值为 width,即我们的过渡效果只针对 width 属性。并设置 transition-duration 属性为 5s,让过渡持续时长为 5s。
div:nth-of-type(1) {
transition-timing-function: ease;
}
div:nth-of-type(2) {
transition-timing-function: ease-in;
}
div:nth-of-type(3) {
transition-timing-function: ease-out;
}
div:nth-of-type(4) {
transition-timing-function: ease-in-out;
}
div:nth-of-type(5) {
transition-timing-function: linear;
}
div:nth-of-type(6) {
transition-timing-function: cubic-bezier(0.2, -2, 0.8, 2);
}
div:nth-of-type(7) {
transition-timing-function: steps(5, jump-start);
}
div:nth-of-type(8) {
transition-timing-function: steps(5, jump-end);
}
div:nth-of-type(9) {
transition-timing-function: steps(5, jump-both);
}
div:nth-of-type(10) {
transition-timing-function: steps(5, jump-none);
}
div:nth-of-type(11) {
transition-timing-function: step-start;
}
div:nth-of-type(12) {
transition-timing-function: step-end;
}
分别对每一个 div 的 transition-timing-function 属性设置不同的关键字,让其显示不同的过渡效果。
section:hover > div {
width: 100%;
}
设置鼠标悬停的时候,把 div 的宽度设置成 100%,即在鼠标进入或鼠标离开的时候发生过渡效果。
通过这个案例及其效果,相信您对这些关键字已经有了一个比较清晰的了解。至于 cubic-bezier 的贝塞尔曲线算法,牵扯到了图形学的知识,这里就不再过多展开了。
我们回到前面一个 div 的案例。
div {
position: fixed;
top: 0;
bottom: 0;
left: 0;
margin: auto;
width: 205px;
height: 25px;
background: red;
transition: width 1s linear 1s;
}
div:hover {
width: 100%;
}
可以看到,我写了一个 transition 属性,却也实现了四个属性的效果:
很明显,这是一个简写,语法如下:
transition: <property> <duration> <timing-function> <delay>;
掌握了四个属性的顺序,简写可以写的比较简便。
4. CSS 动画
动画的概念不同于一般意义上的动画片,动画是一种综合艺术,它是集合了绘画、电影、数字媒体、摄影、音乐、文学等众多艺术门类于一身的艺术表现形式。
动画的英文有很多表述,如 animation、cartoon、animated cartoon、cameracature。其中较正式的 “Animation” 一词源自于拉丁文字根 anima,意思为“灵魂”,动词 animate 是“赋予生命”的意思,引申为使某物活起来的意思。所以动画可以定义为使用绘画的手法,创造生命运动的艺术。
CSS 动画使得可以将从一个 CSS 样式配置转换到另一个 CSS 样式配置。动画包括两个部分:描述动画的样式规则和用于指定动画开始、结束以及中间点样式的关键帧序列。
首先,我们先直接献上配置 CSS 动画的样式规则:
animation-name:指定由 @keyframes 描述的关键帧名称。
animation-duration:设置动画一个周期的时长,默认值为 0s。
animation-timing-function:设置动画速度,即通过建立加速度曲线,设置动画在关键帧之间是如何变化,默认值为 ease。
animation-delay:设置延时,即从元素加载完成之后到动画序列开始执行的这段时间,默认值为 0s。
animation-iteration-count:设置动画重复次数,可以指定 infinite 无限次重复动画,默认值为 1。
animation-direction:设置动画在每次运行完后是反向运行还是重新回到开始位置重复运行,默认值为 normal。
animation-fill-mode:指定动画执行前后如何为目标元素应用样式,默认值为 none。
animation-play-state:允许暂停和恢复动画,默认值为 running。
和 CSS 过渡一样,CSS 动画也存在一个简写,语法如下:
animation: name duration timing-function delay iteration-count direction fill-mode play-state;
然后,我们再直接献上定义 CSS 动画的关键帧序列:
关键帧 @keyframes 的 at-rule 规则通过在动画序列中定义关键帧(或 waypoints)的样式来控制 CSS 动画序列中的中间步骤。
At-rule 规则是一个 CSS 语句,用来指示 CSS 如何运行。以 at 符号开头,'@',后跟一个标识符,并包括直到下一个分号';'的所有内容,或下一个 CSS 块,以先到者为准。
和过渡 transition 相比,关键帧 keyframes 可以控制动画序列的中间步骤。
语法如下:
@keyframes animationName {
keyframesSelector { cssStyles; }
}
animationName:
表示当前动画的名称;
作为该关键帧序列引用时的唯一标识;
不能为空。
keyframesSelector:
关键帧选择器,即指定当前关键帧要应用到整个动画过程中的时间位置;
值可以是一个百分比或者是 from 和 to 关键字;
from 和 0% 效果相同,表示动画的开始;
to 和 100% 效果相同,表示动画的结束。
cssStyles:
定义执行到当前关键帧时对应的动画状态,由 CSS 样式属性进行定义;
多个属性之间用分号分隔;
不能为空。
在我们的模板中,控制 div 的宽度为 205px,高度为 25px,背景色为红色。
div {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 205px;
height: 25px;
background: red;
}
这个时候是在中央显示一个小条:
定义一个 CSS 动画,让 div 的宽度从 0% 到 100%。
@keyframes animate {
from { width: 0%; }
to { width: 100%; }
}
调用该 CSS 动画,控制动画时长为 5s。
animation-name: animate;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 2s;
这里,我们设置动画时长为 2s,动画速度为匀速,并且动画会延迟 2s 开始。效果如下:
我们的 div 元素设置的宽度为 205px,所以在动画延迟的 2s 中,div 元素的宽度为 205px。在动画关键帧中,from 的时候宽度为 0%,所以动画开始的时候,div 元素的宽度会突然变成 0%,并且在 2s 的时间内变成到 100%,然后再次变成 205px 的宽度后就禁止不动了。
我们可以设置 animation-iteration-count 属性控制动画运行的次数:
animation-iteration-count: 5;
这样可以让动画运行 5 次:
可以看到,动画延迟的 2s 是针对整个动画的,第一次动画结束之后会立刻执行第二次动画,而不会再次延迟 2s。
若需要让动画无限循环,我们可以设置 infinite 关键字。
我们也可以设置 animation-direction 属性控制动画运行的方向:
normal
动画在每个循环中正向播放;
每次动画循环时,动画将重置为起始状态并重新开始。
reverse
动画在每个循环中反向播放;
每次动画循环时,动画将重置为结束状态并重新开始;
动画步骤将反向执行,并且时间函数也将被反转,例如 ease-in 时间函数变为 ease-out。
alternate
动画在每个循环中正反交替播放,第一次迭代是正向播放;
确定循环是奇数还是偶数的计数从 1 开始。
alternate-reverse
动画在每个循环中正反交替播放,第一次迭代是反向播放;
确定循环是奇数还是偶数的计数从 1 开始。
animation-direction: alternate;
看看效果:
可以看到,动画有三次由短变长,有两次由长变短,最后一次变长之后突然回到了默认宽度了。
我们可以设置 animation-fill-mode 属性以控制动画执行之后保留的样式:
none:当动画未执行时,动画将不会将任何样式应用于目标,而是已经赋予给该元素的 CSS 规则来显示该元素。这是默认值。
forwards:目标将保留由执行期间遇到的最后一个关键帧计算值。
backwards:动画将在应用于目标时立即应用第一个关键帧中定义的值,并在 animation-delay 期间保留此值。
both:动画将遵循 forwards 和 backwards 的规则,从而在两个方向上扩展动画属性。
animation-fill-mode: forwards;
最后一个关键帧是宽度为 100% 的:
我们可以看到,动画结束之后,保留了宽度为 100% 的样式。
我们可以使用 animation-play-state 属性控制动画是运行还是暂停,直接在例子中说明:
animation-play-state: running;
我们设置 div 的动画为运行。
div:hover {
animation-play-state: paused;
}
鼠标悬停的时候,让其暂停。看看效果:
效果很明显,也很实用。附上完整的 CSS 代码,给大家看的直观:
div {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 205px;
height: 25px;
background: red;
animation-name: animate;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 2s;
animation-iteration-count: 5;
animation-direction: alternate;
animation-fill-mode: forwards;
animation-play-state: running;
}
div:hover {
animation-play-state: paused;
}
@keyframes animate {
from { width: 0%; }
to { width: 100%; }
}
其中,调用动画可以简写成:
animation: animate 2s linear 2s 5 alternate forwards running;
注意顺序噢!!!!!
二、动画案例
我们就从前面的六个篇章中各取出一个案例来添加动画效果吧!
1. 平行四边形篇
这个篇章,我们使用最后那个菱形来做一个旋转动画吧!
我们先把那个篇章的菱形代码拿出来:
width: 400px;
height: 346.41px;
background: red;
transform: rotate(-30deg) skew(30deg);
定义 CSS 关键帧序列,控制开始和结束的 transform 属性。
@keyframes animate {
from { transform: rotate(-30deg) skew(30deg); }
to { transform: rotate(330deg) skew(30deg); }
}
调用该关键帧序列:
animation: animate 1s linear 1s infinite;
我们设置一个鼠标悬停暂停动画效果,让其交互直观一些:
div:hover {
animation-play-state: paused;
}
看看效果:
2. 三角形篇
这个篇章,我们使用向上的那个三角形来做一个旋转动画吧!
我们先把那个篇章的向上的三角形代码拿出来:
width: 0;
height: 0;
border-right: 200px solid transparent;
border-bottom: 200px solid red;
border-left: 200px solid transparent;
同样,定义一个 CSS 关键帧序列,控制开始和结束的 transform 属性,用于控制旋转。
@keyframes animate {
from { transform: rotate(0deg); }
to { transform: rotate(330deg); }
}
调用该关键帧序列:
animation: animate 1s linear 1s infinite;
我们设置一个鼠标悬停暂停动画效果,让其交互直观一些:
div:hover {
animation-play-state: paused;
}
看看效果:
3. 弧形篇
有点无聊,我们用弧形篇中的蛋写一个跳动的动画吧!
我们先把弧形篇中的蛋的代码拿出来:
width: 400px;
height: 600px;
background: red;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
定义一个 CSS 关键帧序列,通过控制蛋的高度和竖直方向上的平移,通过五个关键帧实现蛋从下蹲后再跳起来的动画效果。
@keyframes animate {
0% { height: 600px; transform: translateY(0); }
35% { height: 500px; transform: translateY(100px); }
60% { height: 600px; transform: translateY(0); }
85% { height: 600px; transform: translateY(-150px); }
100% { height: 600px; transform: translateY(0); }
}
调用该关键帧序列:
animation: animate .5s linear infinite;
看看效果:
4. 多边形篇
马上中秋节了,我们来看看月食的效果吧!
我们先把多边形篇中的月牙的代码拿出来:
width: 400px;
height: 400px;
background: transparent;
border-radius: 50%;
box-shadow: 25px 25px red;
定义一个 CSS 关键帧序列,通过控制阴影的偏移,实现月食的动画效果。
@keyframes animate {
from { box-shadow: -400px -200px red; }
to { box-shadow: 400px 200px red; }
}
调用该关键帧序列:
animation: animate 5s linear infinite;
看看效果:
5. 伪元素篇
无极生太极,太极生两仪,两仪生三才,三才生四象,四象生五行,五行生六合,六合生七星,七星生八卦,八卦生九宫,一切归十方。
这些规律,当太极图旋转起来,随着时间的越来越快,我们就会慢慢看到。
我们先把伪元素篇中的太极的代码拿出来:
div {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 400px;
height: 200px;
border: 5px solid red;
border-bottom-width: 205px;
border-radius: 50%;
}
div::before,
div::after {
position: absolute;
top: 100px;
display: block;
width: 70px;
height: 70px;
border-radius: 50%;
content: '';
}
div::before {
background: white;
border: 65px solid red;
}
div::after {
right: 0;
background: red;
border: 65px solid white;
}
定义一个 CSS 关键帧序列,通过控制旋转的角度,看看太极的天干地支吧!
@keyframes animate {
0% { transform: rotate(0); }
10% { transform: rotate(180deg); }
15% { transform: rotate(360deg); }
20% { transform: rotate(720deg); }
30% { transform: rotate(1440deg); }
40% { transform: rotate(2880deg); }
50% { transform: rotate(5760deg); }
60% { transform: rotate(11520deg); }
70% { transform: rotate(23040deg); }
80% { transform: rotate(46080deg); }
90% { transform: rotate(92160deg); }
100% { transform: rotate(184320deg); }
}
调用该关键帧序列:
animation: animate 10s linear infinite;
看看效果:
录制 GIF 动画的显示效果不是很理想,大家可以自己在浏览器上试试看,或者直接搜索太极旋转的视频,会有意想不到的效果噢!
6. 不规则图形篇
既然不规则图形篇主要使用的是 clip-path 属性,那么我们就用这个属性也写一个有意思的动画吧。
我们先写一个圆角边框矩形:
margin: auto;
width: 400px;
height: 200px;
background: red;
border-radius: 20px;
然后使用伪元素写一个向外扩展的边框:
div::before,
div::after {
position: absolute;
top: -20px;
right: -20px;
bottom: -20px;
left: -20px;
border: 5px solid red;
border-radius: 20px;
content: '';
}
可以看到,边框在矩形的外面:
定义一个 CSS 关键帧序列,控制每一帧的 clip-path 属性:
@keyframes animate {
0% { clip-path: inset(0 0 98% 0); }
25% { clip-path: inset(0 98% 0 0); }
50% { clip-path: inset(98% 0 0 0); }
75% { clip-path: inset(0 0 0 98%); }
100% { clip-path: inset(0 0 98% 0); }
}
在伪元素中调用该关键帧序列:
animation: animate 1s infinite linear;
单独控制其中一个伪元素的动画延迟为周期的一半:
div::after {
animation-delay: -0.5s;
}
看看效果:
↓
↓
↓
至此,咱们的一个元素系列,到这里就已经完结了。其中,我们涉及到了大量的知识干货,相信大家一定学到了不少!
将来,我们会推出更多干货级别的视频和文章,让我们一起期待吧!
最后,祝大家教师节快乐!
关注“临界程序员”微信公众号,为您献上更多精彩内容!