动画

transition

元素的样式发生改变,给元素添加一个过渡动画

参数:

- transition-delay 延迟时间,动画延迟多长时间执行(s|ms) 默认值0s 可选

- transition-duration 动画时长,动画用多长时间完成(s|ms) 默认值 0s 必要

- transition-property 要动画的样式 (即css属性,如width等)默认值 all,若分别对不同属性设置不同动画,中间用逗号隔开

eg:transition: 1s width, 1s 2s height;

- transition-timing-function 动画形式

linear 匀速

ease 缓冲(默认值)

ease-in 加速

ease-out 减速

ease-in-out 先加速再减速

贝塞尔曲线运动 cubic-bezier()*

transition 在使用时需要注意一个小问题,我们首先来看看下面这个代码

transition

我们给box加了一个两秒的动画,让他的宽从100变为500,但实际上,他只是在点击按钮式显现并变化但没有动画效果,原因是元素在页面上渲染完之前,transition 是不起效果的,也就是当我点击按钮时,浏览器让box显示出来是需要渲染时间的,虽然很短,但仍长于解析速度,也就是我可能几微秒的时间内就已经解析到动画那一步了,但我还没渲染完,而我们又无法监视浏览器什么时候渲染完,因此可以设置一个稍微长一点时间的计时器来解决这个问题,时间最好不要少于20ms

这下,问题就迎刃而解啦

transitionend

一个监测动画结束的事件

照理来说,应该可以像鼠标移入啊,我用item.ontransitionend就可以触发事件,但好像不行,应该用下面这种形式

其中,注意后面的WebKitTransitionEnd,这是为了兼容一些低版本的,这些大小写很烦,要仔细点。

监听事件

事件监听基本用法item.addEventListener(‘事件名(不加on)’,fn);

取消事件监听:item.removeEventListener(‘事件名(不加on)’,fn); 如果要取消不能使用匿名函数*

不用匿名函数什么意思,就是防止后来取消的时候找不到,也就是不能写成

1
2
3
4
5
6
7
8
item.addEventListener('click',function(){

})
function(){
item.removeEventListener('click',function(){

})
}

addEventListener是很常用的一种手段,相比较于用on事件,这种方式更好,因为他不会覆盖,你可以同时为一个元素添加多个点击事件且都会生效,如果用on事件就会被覆盖只有一个生效

那给个正确的例子吧

1
2
3
4
5
6
box.addEventListener("click",fn);
function fn(){
var w = parseInt(getComputedStyle(box)["width"]);
this.style.width = w + 100 + "px";
box.removeEventListener("click",fn);
}

上面这个例子就是如果写了取消,那一次点击后,下一次不会再生效;要是不写取消,宽度就会随着你的点击次数逐渐增加100px

animation

与transition相比,可以精确动画,需要动画帧keyframes

先来看看keyframes的写法

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
<style>
/* 动画帧 */
//move为动画帧名,在后面调用animation时要注明使用哪个动画帧
@keyframes move {
/* 0%{
width: 100px;
height: 100px;
}
动画帧不定义的时候,默认就使用计算后样式(即元素本来的样式)
*/
//某些情况下需要将0%写成与计算后样式不一样的,如下
/* 0% {
width: 0;
height: 0;
} */
25% {
width: 100px;
height: 300px;
}
50% {
width: 300px;
height: 300px;
}
75% {
width: 300px;
height: 100px;
}
/* 100% {
width: 100px;
height: 100px;
}
100% 不定义 默认会回到计算后样式,剩下的与0%哪里一样
*/
100% {
width: 0;
height: 0;
}
}
/*
默认动画执行完毕之后,会回到计算后样式
*/
#box {
width: 100px;
height: 100px;
background: red;
animation: move 2s;
}
</style>

animation的属性

- animation-name 动画帧名称 必需

- animation-duration 动画持续时间 必需

- animation-timing-function 动画形式(参考 transition)

- animation-delay 动画开始前的延迟时间

- animation-iteration-count 动画执行次数 number(具体的数字) | infinite(无限次)

- animation-direction alternate(1,3,5奇数次正常,2,4,6偶数次倒序执行倒序执行) normal顺序执行

-animation-fill-mode:

backwards 动画开始前,元素的样式保留在动画帧 0

forwards 动画结束后,元素的样式保留在动画帧 100

both: backwards + forwards(开始前在0,结束后在100,即不回到计算后样式)

-animation-play-state: paused 暂停, running 播放

低版本兼容需要写成-webkit-animation···

把很多属性写进animation时可没有顺序,但一定是动画持续时间在动画延迟时间前

animation相关事件

1
2
3
4
5
6
7
8
9
10
box.addEventListener("animationstart",function(){
console.log("动画开始");
});
box.addEventListener("animationend",function(){
console.log("动画结束");
});
// animationiteration 动画多次执行时,使用,监听动画又开始
box.addEventListener("animationiteration",function(){
console.log("动画又开始");
});

transform

变换:旋转,斜切,缩放,位移

旋转:rotate() 单位deg

斜切:*skew(x,y)

skewX() +:左上角,右下角 -:右上角,左下角 与y轴夹角

skewY() +:左上角,右下角 -:右上角,左下角 与x轴夹角

​ 单位:deg

缩放:(倍数)

​ scale(x ,y) 一起缩放

​ scaleX()

scaleY()

位移:translate(x,y)

translateX()

translateY()

​ 单位:px

变换原点:

默认 旋转,缩放,斜切 都是围绕着元素的中心点进行变换

transform-origin 变换基点 (旋转,缩放,斜切 围绕着那个点进行)

默认值: center center

0,0点 在元素的左上角

注意,当写多个函数变换时,后写的先计算样式

图

如上面栗子,第一个是先算好缩放到一半再移200,第二个是先移200,再缩,因此第一个比第二个多走100

图

js获取transform

或许你想这样子获取

getComputedStyle(item)["transform"]

but你得到的将是这么个玩应儿

matrix(0.707107, 0.707107, -0.707107, 0.707107, 0, 0)

没错,上面所讲的旋转啊,位移啊,其实都是css为了方便我们使用才弄得,事实上,我们做的都是对就九个数操作,emmmm就是矩阵,等3D的时候就是16位啦哈哈哈哈哈哈哈哈,不过我们只用操作12位就可以了

换句话说,你获取不到transform的哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈

emmmmmm不过你可以通过js对这9个数进行你想要的旋转缩放位移!了解就好,一般用不上哈,线性代数还可以再等等。

那先来看看简单的,就位移吧

首先默认情况下matrix是这样的

1
2
3
4
5
6
7
// matrix(1, 0, 0, 1, 0, 0);
var a = 1;
var b = 0;
var c = 0;
var d = 1;
var e = 0;
var f = 0;

那你要是问我为啥是这样的,我也不知道,记住就好了哈哈哈哈哈,不行就百度一下子

因而我们就用a~f代替一下子这9个数,到时候字符串拼接好用

那就来看看位移的实现吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// x 位移
function translateX(x){
e += x; //对e操作
box.style.transform = 'matrix('+a+', '+b+', '+c+', '+d+', '+e+', '+f+')';
}
// y 位移
function translateY(y){
f += y;//对f操作
box.style.transform = 'matrix('+a+', '+b+', '+c+', '+d+', '+e+', '+f+')';
}
box.onclick = function(){
translateX(10);//调用
translateY(10);//调用
};

是不是很简单哈哈哈哈哈,那再来看看缩放吧,也差不多的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// x 缩放(a,c,e)
function scaleX(x){
a *= x;
c *= x;
e *= x;
box.style.transform = 'matrix('+a+', '+b+', '+c+', '+d+', '+e+', '+f+')';
}
// y 缩放(b,d,f)
function scaleY(y){
b *= y;
d *= y;
f *= y;
box.style.transform = 'matrix('+a+', '+b+', '+c+', '+d+', '+e+', '+f+')';
}
box.onclick = function(){
scaleX(1.2);
scaleY(1.2)
};

emmmm在说旋转之前先来看看一些基础叭

1
2
3
4
Math.tan(rad) 正切函数
//你像这个rad就是烦人之处,他不接受角度,因而我们需要把角度转换成弧度一下子
Math.PI π圆周率
角度转弧度: deg/180*Math.PI

下面来看正题-旋转的实现

1
2
3
4
5
6
7
8
function rotate(deg){
a=Math.cos(deg/180*Math.PI);
b=Math.sin(deg/180*Math.PI);
c=-Math.sin(deg/180*Math.PI);
d=Math.cos(deg/180*Math.PI);
box.style.transform = 'matrix('+a+', '+b+', '+c+', '+d+', '+e+', '+f+')';
}
rotate(30);

再来看看斜切~

1
2
3
4
5
6
7
8
9
10
// x轴倾斜
function skewX(xDeg){
c = Math.tan(xDeg/180*Math.PI);
box.style.transform = 'matrix('+a+', '+b+', '+c+', '+d+', '+e+', '+f+')';
}
// y轴倾斜
function skewY(yDeg){
b = Math.tan(yDeg/180*Math.PI);
box.style.transform = 'matrix('+a+', '+b+', '+c+', '+d+', '+e+', '+f+')';
}

transform 3D

实现3D,必须加上这两个东西

transform-style: preserve-3d; 设置到父元素,使其子元素保留其3D位置

perspective 景深,一般设置到父样式的父元素,设置成想加3D效果的元素的一两倍即可

还有一个需要你灵活设置,那就是transform-origin,就是你看的角度,emmm我觉得应该是位置好一点,相对于2D,3D这里需要多设置一个z轴距离,设置在父元素上

像这样transform-origin: center center -100px;

还有一个属性

​ backface-visibility: hidden; 当元素不面向屏幕时是否可见。 设置在3D变化的元素上即可

以上所总结的是设置在父元素上还是哪上如果忘了,可以去看看3D导航这个demo

下面再来讲讲3D实现方法

transform3D 变换

1. 旋转

rorateX() 围绕着 X 轴的旋转 (上下翻转)

rorateY() 围绕着 Y 轴的旋转

rorateZ() 围绕着 Z 轴的旋转

2. 位移

translateZ z轴位移

Tween动画算法

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
var Tween = {
linear: function (t, b, c, d){ //匀速
return c*t/d + b;
},
easeIn: function(t, b, c, d){ //加速曲线
return c*(t/=d)*t + b;
},
easeOut: function(t, b, c, d){ //减速曲线
return -c *(t/=d)*(t-2) + b;
},
easeBoth: function(t, b, c, d){ //加速减速曲线
if ((t/=d/2) < 1) {
return c/2*t*t + b;
}
return -c/2 * ((--t)*(t-2) - 1) + b;
},
easeInStrong: function(t, b, c, d){ //加加速曲线
return c*(t/=d)*t*t*t + b;
},
easeOutStrong: function(t, b, c, d){ //减减速曲线
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeBothStrong: function(t, b, c, d){ //加加速减减速曲线
if ((t/=d/2) < 1) {
return c/2*t*t*t*t + b;
}
return -c/2 * ((t-=2)*t*t*t - 2) + b;
},
elasticIn: function(t, b, c, d, a, p){ //正弦衰减曲线(弹动渐入)
if (t === 0) {
return b;
}
if ( (t /= d) == 1 ) {
return b+c;
}
if (!p) {
p=d*0.3;
}
if (!a || a < Math.abs(c)) {
a = c;
var s = p/4;
} else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},
elasticOut: function(t, b, c, d, a, p){ //*正弦增强曲线(弹动渐出)
if (t === 0) {
return b;
}
if ( (t /= d) == 1 ) {
return b+c;
}
if (!p) {
p=d*0.3;
}
if (!a || a < Math.abs(c)) {
a = c;
var s = p / 4;
} else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
},
elasticBoth: function(t, b, c, d, a, p){
if (t === 0) {
return b;
}
if ( (t /= d/2) == 2 ) {
return b+c;
}
if (!p) {
p = d*(0.3*1.5);
}
if ( !a || a < Math.abs(c) ) {
a = c;
var s = p/4;
}
else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
if (t < 1) {
return - 0.5*(a*Math.pow(2,10*(t-=1)) *
Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
}
return a*Math.pow(2,-10*(t-=1)) *
Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
},
backIn: function(t, b, c, d, s){ //回退加速(回退渐入)
if (typeof s == 'undefined') {
s = 1.70158;
}
return c*(t/=d)*t*((s+1)*t - s) + b;
},
backOut: function(t, b, c, d, s){
if (typeof s == 'undefined') {
s = 1.70158; //回缩的距离
}
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
backBoth: function(t, b, c, d, s){
if (typeof s == 'undefined') {
s = 1.70158;
}
if ((t /= d/2 ) < 1) {
return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
}
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
},
bounceIn: function(t, b, c, d){ //弹球减振(弹球渐出)
return c - Tween['bounceOut'](d-t, 0, c, d) + b;
},
bounceOut: function(t, b, c, d){//*
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
}
return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
},
bounceBoth: function(t, b, c, d){
if (t < d/2) {
return Tween['bounceIn'](t*2, 0, c, d) * 0.5 + b;
}
return Tween['bounceOut'](t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
}
};
/*
Tween四个必要参数:
- t: current time(当前时间 - 当前运动次数)
- b: beginning value(初始值)
- c: change in value(变化量)
- d: duration(持续时间 - 运动总次数)
*/
(function(){
var run = document.querySelector("#run");
var stop = document.querySelector("#stop");
var box = document.querySelector("#box");
var x = 0;
var speed = 5;
var timer = 0;
var t = 0; //动画执行到第几次 (动画已经消耗的时间)
var b = 100; // 动画开始前的初始值
var c = 500; // 动画初始值 和 目标点之间的差值
var d = 60; // 动画执行总次数, 动画执行时间
run.onclick = function(){
cancelAnimationFrame(timer);
timer = requestAnimationFrame(move);
function move(){
t++;
var val = Tween["elasticOut"](t,b,c,d);// val 动画执行到第 t 次时,动画应该走到哪个位置
// console.log(val,t);
//box.style.transform = 'translateX('+val+'px)';
box.style.width = val + 'px';
console.log(t);
if(t < d){
timer = requestAnimationFrame(move);
}
}
};
stop.onclick = function(){
cancelAnimationFrame(timer);
};

})();

动画框架的使用

所用框架戳这里

首先我们来看看我们能操作哪些属性

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
"width",
"height",
"left",
"top",
"right",
"bottom",
"marginBottom",
"marginleft",
"marginRight",
"marginTop",
"paddingLeft",
"paddingRight",
"paddingTop",
"paddingBottom"
"opacity"

"rotate",
"rotateX",
"rotateY",
"rotateZ",
"translateX",
"translateY",
"translateZ",
"scale",
"scaleX",
"scaleY",
"skewX",
"skewY"

注意这里都是数值样式,如果是非数值样式,比如颜色,可以获取但不能用作动画操作

那我们就先来看看咋获取吧

1
2
3
css(box,"height");//获取
css(box,"height",200);//修改
css(box,"background","blue");//修改

但是有一个特别重要的东西就是有关transform的值,也就是上面给出的可操作的部分的下半部分,是只能先设置再获取的

1
2
css(box,"scale",.5);
css(box,"scale");

如果没有上面代码块的第一行,那么久无法获取

下面进入正题,看看框架的具体使用参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// transform 相关的样式,在动画前,一定记得先设置初始值
css(box,"rotate",0);
mTween({
el: box,
attr: {//改变后的样式值
width: 300,
height: 300,
rotate: 360
},
//duration: 100 //默认值 400
duration: {
multiple: .5, /* 根据要动画的样式中,最大的差值计算一个时间 multiple 差值的倍数 */
min: 400, //完成动画的最少时间 毫秒
max: 1000 //完成动画的最大时间 毫秒
},
fx : "backOut" // 动画形式: 参考 Tween
cb: function(){
console.log("动画执行完成");
},
moveing: function(){//注意moveing的写法
console.log("动画执行过程中");
}
//停止动画 mTween.stop(box);
});
-------------本文结束感谢您的阅读-------------