JS封装动画函数

本文介绍JavaScript实现动画效果的封装函数,将从匀速水平移动开始,在此基础上不断增加新功能,如二级标题所示。

匀速水平移动动画

这哥们可以用来写轮播图。

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
<head>
<meta charset="UTF-8">
<title>title</title>
<style>

* {
margin: 0;
padding: 0;
}

input {
margin-top: 20px;
}

div {
margin-top: 30px;
width: 200px;
height: 100px;
background-color: green;
position: absolute;
}
</style>
</head>
<body>
<input type="button" value="移动到400px" id="btn1"/>
<input type="button" value="移动到800px" id="btn2"/>
<div id="dv"></div>

</body>


JavaScript代码:

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
<script>
function my$(id){return document.getElementById(id);}
//div要移动,要脱离文档流---position:absolute
//如果样式的代码是在style的标签中设置,外面是获取不到
//如果样式的代码是在style的属性设置,外面是可以获取
//获取div的当前位置
//console.log(my$("dv").offsetLeft);

//点击第一个按钮移动到400px

my$("btn1").onclick = function () {
animate(my$("dv"), 400);
};
//点击第二个按钮移动到800px

my$("btn2").onclick = function () {
animate(my$("dv"), 800);
};
//动画函数---任意一个元素移动到指定的目标位置
function animate(element, target) {
//先清理定时器,防止多次点击产生多个定时器
clearInterval(element.timeId);
//一会要清理定时器(只产生一个定时器)
element.timeId = setInterval(function () {
//获取div的当前的位置
var current = element.offsetLeft;//数字类型,没有px
//div每次移动多少像素---步数
var step = 10;
step = current < target ? step : -step;
//每次移动后的距离
current += step;
//判断当前移动后的位置是否到达目标位置
if (Math.abs(target - current) > Math.abs(step)) {
element.style.left = current + "px";
} else {
//清理定时器
clearInterval( element.timeId );
element.style.left = target + "px";
}
}, 20);
}
</script>


变速水平移动动画(缓动)

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
<script>
//点击按钮移动div

my$("btn1").onclick = function () {
animate(my$("dv"), 400);
};
my$("btn2").onclick = function () {
animate(my$("dv"), 800);
};

//匀速动画
function animate(element, target) {
//清理定时器
clearInterval(element.timeId);
element.timeId = setInterval(function () {
//获取元素的当前位置
var current = element.offsetLeft;
//移动的步数
var step = (target-current)/10;
step = step>0?Math.ceil(step):Math.floor(step);
current += step;
element.style.left = current + "px";
if(current==target) {
//清理定时器
clearInterval(element.timeId);
}
//测试代码:
console.log("目标位置:"+target+",当前位置:"+current+",每次移动步数:"+step);
}, 20);
}
</script>


封装getStyle()

在进一步写动画改变属性函数之前,我们先来封装一个getStyle()函数,一会儿要用到。

先来看一段css代码:

1
2
3
4
5
6
7
8
   //CSS部分,这个div有个id值"dv"
div{
width: 200px;
height: 100px;
background-color: purple;
left:100px;
top:0;
}

然后我们想打印出这个div的left值,执行代码console.log(document.getElementById(“dv”).offsetLeft); 我们期望看到控制台输出100,然而并没有。因为没有脱标,只有加上了position: absolute之后才会如愿输出100。

如果我们就是不想加上position: absolute,又要怎样获取呢?有两种方法:

1
2
3
4
5
//谷歌,火狐支持
console.log(window.getComputedStyle(my$("dv"),null).left);
console.log(window.getComputedStyle(my$("dv"),null)["left"]);
//IE8支持
console.log(my$("dv").currentStyle.left);


接下来时兼容代码:

1
2
3
4
5
6
 function getStyle(element,attr) {
//判断浏览器是否支持这个方法
return window.getComputedStyle? window.getComputedStyle(element,null)[attr]:element.currentStyle[attr];
}

console.log(getStyle(document.getElementById("dv"),"left"));

完成。这个函数可以得到一个元素的任意一个样式属性值,字符串类型。



动画之改变单个属性的值

在上述动画封装函数的基础上,我们可以进一步写出修改属性值的动画函数,当然这个函数依旧可以实现水平移动动画的效果。

先看效果:

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
<script>

function my$(id){ return document.getElementById(id); }


my$("btn1").onclick = function () {
//获取div此时left的当前位置,达到目标400
// animate(my$("dv"),"left",400);
//获取div此时的宽度,达到目标400
animate(my$("dv"),"width",400);
};
my$("btn2").onclick = function () {
animate(my$("dv"), "height", 600);
}

//获取任意的一个属性的当前的属性值: left--->此时的left属性的值,width---当前的元素的宽
function getStyle(element,attr) {
//判断浏览器是否支持这个方法
return window.getComputedStyle? window.getComputedStyle(element,null)[attr]:element.currentStyle[attr];
}

//element---元素
//attr---属性名字,在原来animate的基础上加了一个形参attr
//target---目标位置
function animate(element,attr ,target) {
//清理定时器
clearInterval(element.timeId);
element.timeId = setInterval(function () {
//获取元素的当前位置
var current = parseInt(getStyle(element,attr));//getStyle返回字符串类型转为数字类型
//移动的步数
var step = (target-current)/10;
step = step>0?Math.ceil(step):Math.floor(step);
current += step;
element.style[attr] = current + "px";
if(current==target) {
//清理定时器
clearInterval(element.timeId);
}
//测试代码
console.log("目标:"+target+",当前:"+current+",每次的移动步数:"+step);
}, 20);
}
</script>


动画之改变多个属性的值

在上面改变单个属性的函数代码基础上,使用json格式来存储所要的目的状态。

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
    function my$(id) {   return document.getElementById(id);    }
//点击按钮,改变宽度到达一个目标值,高度到达一个目标值

//获取任意一个元素的任意一个属性的当前的值---当前属性的位置值
function getStyle(element,attr) {
return window.getComputedStyle? window.getComputedStyle(element,null)[attr]:element.currentStyle[attr]||0;
}



function animate(element,json) {
clearInterval(element.timeId);
element.timeId=setInterval(function () {
var flag=true;//默认,假设,全部到达目标
for(var attr in json){
//获取元素这个属性的当前的值
var current=parseInt(getStyle(element,attr));
//当前的属性对应的目标值
var target=json[attr];
//移动的步数
var step=(target-current)/10;
step=step>0?Math.ceil(step):Math.floor(step);
current+=step;//移动后的值
element.style[attr]=current+"px";
if(current!=target){
flag=false;
}
}
if(flag){
//清理定时器
clearInterval(element.timeId);
}

//测试代码
console.log("目标:"+target+",当前:"+current+",每次的移动步数:"+step);
},20);
}



my$("btn1").onclick=function () {

animate(my$("dv"),{"width":400,"height":500,"left":500,"top":80});
};

</script>


动画之回调函数

回调是什么?

简单讲: 回调是指在另一个函数执行完成之后被调用的函数  ——  因此得名“回调”。

稍复杂地讲: 在 JavaScript 中,函数也是对象。因此,函数可以传入函数作为参数,也可以被其他函数返回。这样的函数称为高阶函数。被作为参数传入的函数就叫做回调函数

如果我们想要实现下图中的效果,则需要在动画函数中加入作为参数的回调函数。

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
<script>

function my$(id) {
return document.getElementById(id);
}

//获取任意一个元素的任意一个属性的当前的值---当前属性的位置值
function getStyle(element,attr) {
return window.getComputedStyle? window.getComputedStyle(element,null)[attr]:element.currentStyle[attr]||0;
}



//element---元素
//json---对象---多个属性及多个目标值
//fn---函数
function animate(element,json,fn) {
clearInterval(element.timeId);
element.timeId=setInterval(function () {
var flag=true;//默认,假设,全部到达目标
for(var attr in json){
//获取元素这个属性的当前的值
var current=parseInt(getStyle(element,attr));
//当前的属性对应的目标值
var target=json[attr];
//移动的步数
var step=(target-current)/10;
step=step>0?Math.ceil(step):Math.floor(step);
current+=step;//移动后的值
element.style[attr]=current+"px";
if(current!=target){
flag=false;
}
}
if(flag){
//清理定时器
clearInterval(element.timeId);
//所有的属性到达目标才能使用这个函数,前提是用户传入了这个函数
if(fn){
fn();
}
}


//测试代码
console.log("目标:"+target+",当前:"+current+",每次的移动步数:"+step);
},20);
}



my$("btn1").onclick=function () {

var json1={"width":400,"height":500,"left":500,"top":80};

animate(my$("dv"), json1, function () {
var json2={"width":40,"height":50,"left":50,"top":10};
animate(my$("dv"),json2,function () {
var json3={"width":450,"height":500,"left":550,"top":80};
animate(my$("dv"),json3);
});
});
};

</script>



动画之改变透明度与层级

改变透明度和层级这两部分需要进行if判断,代码如下:

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
<script>
function my$(id){ return document.getElementById(id);}

//获取任意一个元素的任意一个属性的当前的值---当前属性的位置值
function getStyle(element, attr) {
return window.getComputedStyle ? window.getComputedStyle(element, null)[attr] : element.currentStyle[attr] || 0;
}


function animate(element, json, fn) {
clearInterval(element.timeId);//清理定时器
//定时器,返回的是定时器的id
element.timeId = setInterval(function () {
var flag = true;//默认,假设,全部到达目标
//遍历json对象中的每个属性还有属性对应的目标值
for (var attr in json) {
//判断这个属性attr中是不是opacity
if (attr == "opacity") {
//获取元素的当前的透明度,当前的透明度放大100倍
var current = getStyle(element, attr) * 100;
//目标的透明度放大100倍
var target = json[attr] * 100;
var step = (target - current) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
current += step;//移动后的值
element.style[attr] = current / 100;//原先放大100倍现在就要缩小100倍
} else if (attr == "zIndex") { //判断这个属性attr中是不是zIndex
//层级改变就是直接改变这个属性的值
element.style[attr] = json[attr];
} else {
//普通的属性
//获取元素这个属性的当前的值
var current = parseInt(getStyle(element, attr));
//当前的属性对应的目标值
var target = json[attr];
//移动的步数
var step = (target - current) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
current += step;//移动后的值
element.style[attr] = current + "px";
}
//是否到达目标
if (current != target) {
flag = false;
}
}
if (flag) {
//清理定时器
clearInterval(element.timeId);
//所有的属性到达目标才能使用这个函数,前提是用户传入了这个函数
if (fn) {
fn();
}
}
//测试代码
console.log("目标:" + target + ",当前:" + current + ",每次的移动步数:" + step);
}, 20);
}


//zIndex:1000
//透明度: 数字类型----小数---放大100倍
my$("btn1").onclick = function () {

var json1 = {"width": 400, "height": 500, "left": 500, "top": 80, "opacity": 0.2};
animate(my$("dv"), json1, function () {
animate(my$("dv"), {"width": 40, "height": 50, "left": 0, "top": 0, "opacity": 1, "zIndex": 1000});
});
};

</script>


至此JavaScript封装动画函数实现暂告一段落。