Transition
语法
1 2 3 4 5 6 7 |
.example {
transition-property: all | none | <custom-ident>;
transition-duration: 0s;
transition-timing-function: ease | ease-in | ease-out | ease-in-out |
cubic-bezier(<number>, <number>, <number>, <number>);
transition-delay: 0s;
}
|
简写:
1 2 3 4 |
.example {
transition: [transition-property] [transition-duration]
[transition-timing-function] [transition-delay];
}
|
多个过渡效果:
1 2 3 |
.example {
transition: width 2s linear, opacity 2s linear, color 2s linear;
}
|
触发过渡效果
仅定义过渡的规则,但是没有明确什么时候、如何触发过渡,将不会产生任何效果。
:hover :active :focus :checked这四个伪类元素都可以触发:
1 2 3 4 5 6 7 |
.example {
background-color: green;
transition: background-color 2s ease-in 1s;
}
.example:hover {
background-color: blue;
}
|
媒体查询media:
1 2 3 4 5 6 7 8 9 10 11 |
.example {
width: 200px;
height: 200px;
transition: width 2s ease, height 2s ease;
}
@media only screen and (max-width: 960px) {
.example {
width: 100px;
height: 100px;
}
}
|
Javascript:
1 2 3 4 5 |
$(function() {
$('#button').click(function() {
$('.box').toggleClass('style-change')
})
})
|
1 2 3 4 5 6 7 8 9 |
.box {
width: 200px;
height: 200px;
transition: width 2s ease, height 2s ease-in;
}
.style-change {
width: 300px;
height: 300px;
}
|
Hover on 和 Hover off 的 Transition
1 2 3 4 5 6 7 8 9 |
.example {
background-color: #ff9ebb;
transform: rotate(720deg);
transition: all 2s ease-out;
}
.example:hover {
background-color: #ed2d50;
transform: rotate(0deg);
}
|
一般我们不会在:hover时设置transition,这时,鼠标移动进入元素,元素按照:hover里定义的样式发生形变,鼠标移出移出元素时,元素的样式会过渡变回初始状态,如图:
让我们在:hover里也设置transition试一试:
1 2 3 4 5 6 7 8 9 10 |
.example {
background-color: #ff9ebb;
transform: rotate(720deg);
transition: all 2s ease-out;
}
.example:hover {
background-color: #ed2d50;
transform: rotate(0deg);
transition: background-color 2s linear 0.5s;
}
|
当:hover时,后定义的transition会覆盖先定义的值,所以,鼠标移入元素时,只有background-color会发生变化,当鼠标移出元素时,先定义的transition: all 2s ease-out生效,backgound-color和transform都发生的变化。
综上所述,在:hover处定义的transition影响鼠标移入时的过渡效果,在元素内定义的transition起到的作用就如开始时提到,使元素的样式从:hover时的状态恢复的初始状态,表现为影响鼠标移出时的过滤效果:
cubic-bezier 函数实现弹跳效果
我们将实现一个小风扇弹跳展开的效果:
HTML 结构:
1 2 3 4 5 6 7 8 9 |
<div class="fan"> <input type="checkbox" id="checkbox"> <div class="leaf"></div> <div class="leaf"></div> <div class="leaf"></div> <div class="leaf"></div> <div class="leaf"></div> <div class="center"></div> </div> |
小风扇有 5 个扇叶,一个中心花蕊开关,一个checkbox,我们将checkbox定位在中心花蕊后面,通过checkbox勾选与取消勾选操作控制小风扇的打开与收起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
.fan {
width: 60px;
height: 120px;
position: relative;
}
#checkbox {
position: absolute;
width: 40px;
height: 50px;
cursor: pointer;
}
.center {
position: absolute;
width: 45px;
height: 50px;
background-color: #fff;
border-radius: 50%;
pointer-events: none;
}
|
扇叶的形状可以通过border-radius绘制出来。可以看出扇叶是左右对称的,说明border-radius的水平半径是50%,其次扇叶上短下长,说明垂直半径上方少,下方长,即可设border-radius: 50% / 30% 30% 70% 70%:
1 |
border-radius: htl htr hbr hbl / vtl vtr vbr vbl; |
从效果图中可以看出,扇叶的旋转中心并不是center center,而是扇叶上方的中心,已知扇叶宽60px,可得旋转中心坐标应为tranform-origin: 30px 30px:
1 2 3 4 5 6 7 8 9 |
.leaf {
width: 60px;
height: 120px;
position: absolute;
border-radius: 50% / 30% 30% 70% 70%;
pointer-events: none;
transform-origin: 30px 30px;
transition: transform 1s;
}
|
下面设置扇叶旋转的位置,假设第三片扇叶旋转 180° 垂直向上,剩下四片扇叶依次间隔(360-60)/4=75px,别忘了这个例子触发过渡效果的方法是:checked:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#checkbox:checked ~ .leaf:nth-of-type(5) {
transform: rotate(35deg);
}
#checkbox:checked ~ .leaf:nth-of-type(4) {
transform: rotate(105deg);
}
#checkbox:checked ~ .leaf:nth-of-type(3) {
transform: rotate(180deg);
}
#checkbox:checked ~ .leaf:nth-of-type(2) {
transform: rotate(255deg);
}
#checkbox:checked ~ .leaf:nth-of-type(1) {
transform: rotate(325deg);
}
|
到这里,小风扇已经可以旋转了:
现在,只要改变transition的transition-timing-function就是可以实现弹跳的效果了,在这之前,先了解一下cubic-bezier函数:
1 |
cubic-bezier(t1, d1, t2, d2) |
t代表时间time,d代表距离distance。
假设由一个盒子要用 6s 的时间,从 A 点移动到 B 点,设t1=0, d1=1:
1 |
cubic-bezier(0, 1, 0, 0) |
从图中可以看出,盒子几乎没有花费任何时间从 A 点到达中点,然后匀速从中点向 B 点移动。
再设t1=1, d1=0:
1 |
cubic-bezier(1, 0, 0, 0) |
从图中可以看出,前 3s 盒子保持不动,然后几乎没有花费任何时间从 A 点到达中点,然后匀速从中点向 B 点移动。
再设t1=1, d1=1, t2=0, d1=1:
1 |
cubic-bezier(1,1,0,1) |
从图中可以看出,盒子用了 3s 的时间从 A 点移动到中点,然后几乎没用任何时间从中点移动到了 B 点。
从上面的例子中可以看出,t1 d1控制 A 点到中点的时间和距离,t2 d2控制中点到 B 点的时间和距离。
弹跳函数cubic-bezier的实现:
1 |
cubic-bezier(.8,-.5,.2,1.4) |
细心的观察,小风扇打开和收起时的弹跳效果并不完全一样,这里需要用到上一节说到的hover on and off transition:
1 2 3 4 5 6 |
.leaf {
transition: transform 1s cubic-bezier(0.8, 0.5, 0.2, 1.4);
}
#checkbox:checked ~ .leaf {
transition: transform 1s cubic-bezier(0.8, -0.5, 0.2, 1.4);
}
|
为小风扇设置两个transition,打开的时候先收缩一下。
https://codepen.io/pennySU/pen/NMoBZe
https://codepen.io/pennySU/pen/QrYrEX
https://codepen.io/pennySU/pen/qYgxJG
https://codepen.io/pennySU/pen/KRJZKj
https://codepen.io/pennySU/pen/JvxONd
Animation
定义一个动画需要设置animation子属性来配置动画的时间、时长等其他细节,动画的实际表现,是由@keyframes规则实现的。
语法
1 2 3 4 5 6 7 8 9 10 11 |
@keyframes Animation-name {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#box {
animation: Animation-name 5s infinite; /*多个动画可用逗号分隔*/
}
|
简写:
1 2 |
animation: duration | timing-function | delay | iteration-count | direction | fill-mode | play-state | name; |
微博点赞效果
HTML 结构:
1 |
<div class="like"></div> |
下图是用来实现动效的图片steps_praised.png:
我们的思路是,点赞时,让图片从左到右按一定的速度动起来,实现动画效果,首先定义动画帧:
1 2 3 4 5 6 7 8 |
@keyframes steps {
0% {
background-position: left;
}
100% {
background-position: right;
}
}
|
动画帧很简单,从左到右移动即可。设置动画执行的属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
.like {
position: relative;
&:before {
content: '';
position: absolute;
width: 23px;
height: 28px;
background-image: url(/steps_praised.png);
background-repeat: no-repeat;
background-size: 483px 28px;
}
&.active {
background-position: right;
&:before {
animation: 0.65s steps(20) 1 forwards steps;
}
}
}
|
首先,给.like元素的伪元素:before通过background-image的方式设置点赞按钮的初始状态,当点击元素时,为元素添加.active类,执行动画。动画共进行0.65s,分20步,执行1次,执行完保持最后一帧的状态。steps(20)等同于steps(20,end)。
背景图片steps_praised.png宽966px,共有 21 帧,设置background-size: 483px 28px;后,每帧动画的宽为483 / 21 = 23px,即:before的宽为23px。初始状态已经展示一帧了,所以只需展示剩下的 20 帧即可,故设置step(20),动画结束时保存最后一帧的状态。点击看效果
使用雪碧图制作动画,可减少 gif 的使用,一般 gif 文件的大小要大于雪碧图。雪碧图动画
Loading
JSFiddle 的 loading 效果。
HTML 结构:
1 2 3 4 |
<div class="jump"> <svg class="loader"></svg> <span class="shadow"></span> </div> |
一个蓝胖子,一个阴影元素。蓝胖子上下有移动,也有缩小和放大,阴影有明暗和大小的变化。所以分别为它两定义动画的表现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@keyframes jump {
from {
transform: translateY(0) scale(1.15, 0.8);
}
20% {
transform: translateY(-35px) scaleY(1.1);
}
50% {
transform: translateY(-50px) scale(1);
}
80% {
transform: translateY(-35px) scale(1);
}
to {
transform: translateY(0) scale(1.15, 0.8);
}
}
|
动画jump从下到上,再从上到下移动,同时由“矮”“胖”到“高”再到“正常”最后又变为“矮”“胖”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@keyframes scale-shadow {
from {
opacity: 0.3;
transform: scale(1);
}
50% {
opacity: 0.2;
transform: scale(0.5);
}
to {
opacity: 0.3;
transform: scale(1);
}
}
|
动画scale-shadow先变小然后恢复,透明度先变淡再变深。
1 2 3 4 5 6 |
.loader {
animation: jump 0.8s ease-in infinite;
}
.shadow {
animation: scale-shadow 0.8s ease-in infinite;
}
|
设置animation属性使动画动起来。点击看效果
再介绍一个利用负动画延迟animation-delay实现的 loading 效果。
HTML 结构:
1 2 3 4 5 6 7 8 9 10 |
<div class="roller" title="roller"> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> </div> |
一个roller容器,八个点点。
动画效果其实很简单,八个点点旋转完整的一圈:
1 2 3 4 5 6 7 8 |
@keyframes roller {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
|
定义动画roller旋转 360°。
为容器和点点设置一些样式:
1 2 3 4 5 6 7 8 |
.roller {
width: 64px;
height: 64px;
}
.roller div {
margin: -3px 0 0 -3px;
transform-origin: 32px 32px;
}
|
从效果图中,可以看出,八个点点的旋转中心是容器的中点,transform-origin是根据元素的左上角定位的,即八个div元素的位置必须是在同一个位置,这样它们才能有相同的旋转中心。所以我们用:before伪元素实现圆点的效果并且绝对定位形成一个半圆弧形状,保证八个div元素没有宽高使得它们都在同一个位置拥有相同的旋转中心。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
div {
margin: -3px 0 0 -3px;
transform-origin: 32px 32px;
&:before {
content: '';
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
}
&:nth-child(1) {
&:before {
background-color: #ff2121;
left: 50px;
top: 50px;
}
}
&:nth-child(2) {
&:before {
background-color: #ff7200;
top: 54px;
left: 45px;
}
}
&:nth-child(3) {
&:before {
background-color: #ffdc59;
top: 57px;
left: 39px;
}
}
&:nth-child(4) {
&:before {
background-color: #84d458;
top: 58px;
left: 32px;
}
}
&:nth-child(5) {
&:before {
background-color: #7bc576;
top: 57px;
left: 25px;
}
}
&:nth-child(6) {
&:before {
background-color: #2e89c1;
top: 54px;
left: 19px;
}
}
&:nth-child(7) {
&:before {
background-color: #2b52c6;
top: 50px;
left: 14px;
}
}
&:nth-child(8) {
&:before {
background-color: #8366af;
top: 45px;
left: 10px;
}
}
}
|
接下来,为每个点点设置不同的延迟(由小到大):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
div {
animation: roller 1.2s ease-in-out infinite;
&:nth-child(1) {
animation-delay: -0.036s;
}
&:nth-child(2) {
animation-delay: -0.072s;
}
&:nth-child(3) {
animation-delay: -0.108s;
}
&:nth-child(4) {
animation-delay: -0.144s;
}
&:nth-child(5) {
animation-delay: -0.18s;
}
&:nth-child(6) {
animation-delay: -0.216s;
}
&:nth-child(7) {
animation-delay: -0.252s;
}
&:nth-child(8) {
animation-delay: -0.288s;
}
}
|
现在,八个点点就排着队转起来了。
最后介绍一个不同阶段使用不同animation-timing-function效果的 loading。
HTML 结构:
1 |
<div class="disk" title="disk"></div> |
html 结构很简单,只有一个disk圆盘元素。
从效果图可以看出,圆盘元素旋转了很多圈,但是旋转速度有很明显的快慢的区别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@keyframes disk {
0%,
100% {
animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
}
0% {
transform: rotateY(0deg);
}
50% {
transform: rotateY(1800deg);
animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
}
100% {
transform: rotateY(3600deg);
}
}
|
disk动画分三阶段:初始阶级,50%旋转 1800°=5 圈,100%旋转 3600°=10 圈。每个阶段有自己的animation-timing-function,可以使用 cubic-bezier 工具查看这两个函数的效果:cubic-bezier(0.5, 0, 1, 0.5)、cubic-bezier(0, 0.5, 0.5, 1),前五圈由慢到快,后五圈由快到慢。
1 2 3 |
.disk {
animation: disk 2.4s infinite;
}
|
利用animation可以实现很多有趣的 loading 效果,更多可以点击看效果。
https://codepen.io/pennySU/pen/deLZrG/
上面提到过hover on and hover off的transition,其实,hover on时定义animation也可以覆盖transition的效果,同时hover off是依然可以展示transition设置的动效:
1 2 3 4 5 6 7 8 9 10 11 |
.mask {
transform: translateY(-200px);
opacity: 0;
transition: all 0.3s ease-out 0.5s;
}
.mask:hover {
opacity: 1;
transform: translateY(0px);
transition-delay: 0s;
animation: bounceY 0.9s linear;
}
|
.mask鼠标移入hover时,设置transition要执行动效的初始状态:opacity: 1;transform: translateY(0px);;,并执行animation动画;鼠标移出时,执行transition动效。
文字的效果使用了mask-image属性,顾名思义,使图片作为元素的蒙版图层,很神奇的属性,张鑫旭和大漠都有相应的文章。
这里主要想说一说backwards的作用:
1 2 3 4 5 6 |
span {
color: #444;
text-shadow: 0 0 2px #444, 1px 1px 4px rgba(0, 0, 0, 0.7);
-webkit-mask-image: url(https://tympanus.net/Tutorials/TypographyEffects/images/mask2.png);
animation: sharpen 0.6s linear backwards;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@keyframes sharpen {
0% {
opacity: 0;
color: transparent;
text-shadow: 0 0 100px #444;
}
90% {
opacity: 0.9;
color: transparent;
text-shadow: 0 0 10px #444;
}
100% {
opacity: 1;
color: #444;
text-shadow: 0px 0px 2px #444, 1px 1px 4px rgba(0, 0, 0, 0.7);
}
}
|
在动画延迟期间,保存动画第一帧的样式。如果不设置backwards,文字在动画开始前将保持默认状态,即color 、text-shadow 、mask-image共同作用的状态(非隐藏),而不是效果图中开始时隐藏的状态。设置了backwards之后,会在延迟期间保持 0% 时设置的状态。
animation-name
动画名字,由@keyframes定义的动画序列。
animation-duration
动画的周期时长。
1 2 3 |
#box {
animation-duration: 1s, 120ms;
}
|
animation-timing-function
每个关键帧周期动画执行的节奏。取值可见上面的transition-timing-function,此外,还有一个frames()属性:
- frames(2) 将按照给定的数值分成几个相等长度的时间。
来看一下steps()和frames()的区别:
帧定时函数和步进定时函数的区别是,帧定时函数会在闭区间[0,1]之间按输入的数值平均分成若干份。这个特性在循环播放的动画中,第一帧和最后一帧执行的时间一样时很适用。
animation-delay
动画从应用在元素到开始的时间。
定义一个负值会让动画立即开始,但是动画会从动画序列的某处开始,如,设-1s,动画会从动画序列的第 1 秒位置处立即开始。
1 2 3 |
#box {
animation-delay: 2s;
}
|
animation-iteration-count
动画运行的次数。
1 2 3 |
#box {
animation-iteration-count: infinite | 2.3 | 0;
}
|
animation-direction
动画的播放顺序:
-
normal每个动画循环结束,动画重置到起点重新开始。 -
alternate动画交替反向运行。 -
reverse每个周期有尾到头运行。 -
alternate-reverse第一次反向运行,第二次正向,后面依次循环。
animation-fill-mode
设置动画执行之前和之后如何给动画应用样式:
-
none动画执行前后不改变任何样式。 -
forwards动画完成后最后一帧的样式,最后一帧取决于animation-direction和animation-iteration-count。 -
backwards在animation-delay时间内,动画的样式,保持第一帧的样式,取决于animation-direction。 -
bothforwards和backwards都会执行。
animation-play-state
设置动画是执行还是暂停。
1 |
animation-play-state: running | paused; |