我之前也接触过动画,但是平时没怎么用,一段时间不看就又忘了,transition、translate、transform记得头皮发麻。若本篇文章从概念的角度去介绍transition和animation的用法,会显得枯燥冗余,而且网上资料很多,解释比较单一,比较难理解。今天换一种方式,主要从应用的角度去认识transition和animation的用法。
关于transition和animation属性
Elastic transition (弹性过渡)
小球下落动画
根据常识(自由落体)我们都知道,球下降速度会不断变化,接触到地面会反弹,因为还有速度,会继续上升一段时间,后再次下落,如此往复,直到静止。
重力势能->动能->重力势能->动能->静止(阻力的存在)
@keyframes
@keyframes创建动画的原理是,定义关键帧的CSS样式,将一套 CSS 样式逐渐变化为另一套 CSS 样式。
以百分比来规定改变发生的时间,或者通过关键词 “from” 和 “to”,等价于 0% 和 100%。
语法
1
@keyframes animationname {keyframes-selector{css-style;}}
示例
1
2
3
4
5
6
7
8
9
10
11
12@keyframes bounce{
60%,80%, to{//60%、80%、100%时的位置
transform: translateY(400px);
animation-timing-function: ease;/*特别规定其的timing函数*/
}
70%{//70%时的位置
transform: translateY(300px);
}
90%{//90%时的位置
transform: translateY(360px);
}
}
贝塞尔曲线
timing-function有五个可选值:ease(默认),linear(匀速),ease-in(逐渐加速),ease-out(逐渐减速),ease-in-out(先加速后减速)。想要定义自己想要的效果,就要用到贝塞尔函数cubic-bezier(x1,y1,x2,y2)
语法
对应图中的坐标(x1,y1)、(x2,y2),其中x轴是时间轴,y轴是进度轴,导数就是这个时刻点的速率。1
cubic-bezier(x1,y1,x2,y2)
贝塞尔曲线网站
cubic-bezier
弹性过渡
背景
有这样一个需求:一个输入框,当focus的时候有一个tip弹出,提示用户输入框该输入啥,tip显隐的时候就会有过渡的效果。代码
HTML
1
2
3
4
5<label>
Your username: <input name="elastic" id="username" />
<span class="callout">Only letters, numbers,
underscores (_) and hyphens (-) allowed!</span>
</label>CSS
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
46body {
padding: 1.5em;
font: 200%/1.6 Baskerville;
}
input {
display: block;
padding: 0 .4em;
font: inherit;
}
.callout{
position: absolute;//@@inline元素scale()无效
max-width: 14em;
padding: .6em .8em;
border-radius: .3em;
margin: .3em 0 0 -.2em;
background: #fed;
border: 1px solid rgba(0,0,0,.3);
box-shadow: .05em .2em .6em rgba(0,0,0,.2);
font-size: 75%;
}
.callout:before{/*完成倒三角*/
position: absolute;
top: -.4em;
left: 1em;
content: '';
padding: .35em;
background: #fed;
border: inherit;
border-right: 0;
border-bottom: 0;
transform: rotate(45deg);
}
/*用transition来实现*/
input[name="elastic"]:not(:focus) + .callout{
transform: scale(0);
/*属性对应解释
*@@ 消失的时间会比出现的时间长,因为出现只用了500ms的一半,加个transition-duration控制时间
*@@ 加上transition-property,指定要过渡的属性为transform
*@@ 规定了不聚焦时的动画函数为ease,不加这个,消失时会突然变大一下,默认用了和出现时相同的timing-function
*/
transition: .25s transform ease;
}
.callout{
transition: .5s transform cubic-bezier(.25,.1,.3,1.5);/*贝塞尔函数横轴(Time)不能超过1,但纵轴(Progress)可以,有Back效果*/
transform-origin: 1.4em -.4em;/*动画起步点*/
}
Frame by frame animation (逐帧动画)
背景
在我们拥有一个动画每一帧的情况下,我们来完成一个loading效果,雪碧图如下animation-timing-function之steps()
相较于贝塞尔曲线,steps可以被认为是分段函数
代码
HTML
1
<div class="loader">Loading...</div>
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18@keyframes loader{
to{
background-position: -800px 0;
}
}
.loader{
width: 100px;
height: 100px;
background: url(img/loader.png) 0 0;
/*steps()
*@@ steps(8)表示loader的每个阶段分8帧
*@@ 这里整个动画时间为1s,动画只有一个阶段,有八帧,说明每帧的停留时间就是0.125s(125ms)
*@@ steps的设置都是针对两个关键帧之间的,而非是整个keyframes
*/
animation: loader 1s infinite steps(8);
line-height: 100px;
text-align: center;
}
Blinking (闪烁)
之前是有
思路
闪烁其实就是文字的显示隐藏,当频率达到一定值时,就有闪烁的感觉:
- 可以通过对dom结构的opacity设置为0来隐藏
- 可以通过对文本处理,color为transparent来隐藏
- 可以通过border-color来达到闪烁的效果
我们接下来以普遍的文字闪烁进行讲解。
代码
HTML结构
1
<p class="highlight">blink blink blink</p>
CSS
1
2
3
4
5
6
7
8
9
10//这样达到了闪烁的预期,但是,还是有需要优化的地方:我们能明显感觉到,字体消失的下一个又突然的变亮,不够的自然。
@keyframes blink-smooth{
50%{/*有1/2帧在停留状态*/
color: transparent;
}
}
.highlight{
animation: 1s blink-smooth infinite;
font-size: 50px;
}
以上代码产生的效果会不够自然,颜色变化可以参照下图
想要有渐变的效果,就要对动画的方向进行改变
animation-direction
四个值
- Normal 规定的正向效果
- Reverse 规定的反向
- Alternate 奇数次时是正向,偶数次时是反向
- Alternate-revere 偶数次时是正向,奇数次时是反向
四个值分别对应的效果为改进后的CSS
1
2
3
4
5
6
7
8
9@keyframes blink-smooth{
to{//100%时字体是透明的
color: transparent;
}
}
.highlight{
animation: 1s blink-smooth infinite alternate;/*随着奇偶反转*/
font-size: 50px;
}
Typing animation (打字效果)
有时候啊,我们希望文字一个个出现,模拟一下打字的场景,感觉那样非常的酷
饿了么
思路
我们可以用动画的效果逐渐增加可见区域dom的长度,从而实现打字的效果
局限性,只能打一行的字,不支持多行,但是一行也能满足我们的要求
初体验
代码
HTML
1
<h1>CSS is awesome!</h1>
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13@keyframes typing{
from{
width: 0;
}
}
h1{
animation: typing 8s;
font-size: 2em;
width: 30em;/*15*2=30em*/
white-space: nowrap;
overflow: hidden;
}
新的问题
新的问题出现:
- 动画太平滑了,没有那种一个文字一个文字打出来的感觉
- 还有个不明显的问题就是:我们这边写死了宽度,这个宽度是哪里来的,瞎蒙的么?字数变化了,我们又该怎么计算这个宽度?
- 缺少光标
解决方案
第一个问题用steps来解决,因为steps可以给我们提供一帧一帧的感觉
第二个问题:
引入ch单位的概念,表示0这个字符的长度,monospace(单间隔)字体中,0和任意字符的长度是一样的,所以长度是15(包括空格),但是这个还是要手动去设置dom的长度,结合js我们可以自动设置dom的长度,便于拓展和可维护性
第三个问题发挥想象力,最右边的字永远是靠近dom结构的border的,所以我们可以把border作为我们的光标
再次尝试
HTML
1
2<h1>CSS is awesome!</h1>
<h1>CSS is awesome!hahahah</h1>CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@keyframes typing{
from{
width: 0;
}
}
@keyframes caret{
50%{
border-color: transparent;
}
}
h1{
font: bold 200% Consolas, Monaco, monospace;/*一定要是单间隔字体哦!*/
white-space: nowrap;
overflow: hidden;
border-right: .05em solid;/*不需要去指定border的颜色,默认为text的颜色*/
animation: typing 7s steps(15),caret 1s steps(1) infinite;
}JS
1
2
3
4
5[].forEach.call(document.getElementsByTagName('h1'),function(h1) {
var len = h1.textContent.length, s = h1.style;
s.width = len + 'ch';
s.animationTimingFunction = "steps("+len+"),steps(1)";//一个是typing,一个是caret
});
Smooth state animations (平滑动画)
当我们有一个很长的图片,比如说风景图,
这张风景图(1295x616)呢,要显示在一个相对小很多的容器里面,比如说150x300,我们要怎么处理?
首先一种思路我们可能压缩图片嘛,但是必须是等比例的压缩,不然会变形
然后想到了css属性background-size可以调整图片尺寸大小,比如说contain(照片会被包含在容器内,但往往会放大缩小),cover(损失掉多出的部分)
强行设置background-sizing:100% 100%;图片就会变形
其实我们可以用动画的效果让图片不断的滚动,这样用户可以看到图片上的所有内容,也好似身临其境,用户体验是非常棒的。
思路
通过动画让图片滚动
初体验
HTML
1
<div class="panoramic"></div>
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13@keyframes panoramic{
to {
background-position: 100% 0;
}
}
.panoramic{
margin: 0 auto;
width: 150px;
height: 300px;
background: url('./img/lanscape.jpg');
background-size: auto 100%;
animation: panoramic 10s linear infinite alternate;
}
新的问题
新的问题出现:
- 有时候我们需要的动画不是自动播放的,我们需要根据用户的一些操作去响应我们的动画,比如:hover伪类的时候实现动画效果
那么我们需要考虑的就hover的时候动画开始,鼠标移除的时候,动画停止。
解决方案
我们要是能设置鼠标移入和移出时的动作就好了,控制动画的状态那就完美,这就要用到下面的属性
Animation-play-state(用来控制动画的状态)有两个状态:paused(动画暂停)和running(动画开始)
再次尝试
HTML
1
<div class="panoramic"></div>
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@keyframes panoramic{
to {
background-position: 100% 0;
}
}
.panoramic{
margin: 0 auto;
width: 150px;
height: 300px;
background: url('./img/lanscape.jpg');
background-size: auto 100%;
animation: panoramic 10s linear infinite alternate;
animation-play-state: paused;
}
.panoramic:hover,.panoramic:focus{
animation-play-state: running;
}