本文将按照以下步骤,封装一个能够实现同时运动和链式运动的JavaScript动画函数,即运动框架。
运动框架实现思路:
速度(改变值left、right、width、height、opacity)
缓冲运动(动画的快慢变化)
多物体运动
任意值变化
链式运动
同时运动
一、速度动画
效果:
思路:
创建一个div元素,使其隐藏在浏览器左侧(设置left值为其宽度的负数值,如div的width为200px,设置其left值为-200px)
当鼠标移入div时,div匀速显示出来(left值逐渐增加到0px,如:-200px -150px -100px -50px 0px)
当鼠标移出时,将div隐藏(left值逐渐减小为初始值,如:0px 50px 100px 150px 200px)
知识点:
oDiv1.offsetLeft 获取div1的left属性值,返回数字
http://www.imooc.com/video/2879/0
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
* { padding:0; margin:0;}
#div1 {
position:relative;
width:200px;
height:200px;
left:-200px;
background-color: #f00;
}
#btn {
position: absolute;
background-color: #0f0;
width:30px;
height:50px;
top:75px;
right:-30px;
}
</style>
<script type="text/javascript">
window.onload = function() {
var oDiv1 = document.getElementById('div1');
oDiv1.onmouseover = function() { slide(0); }
oDiv1.onmouseout = function() { slide(-200);}
}
var timer = null;
/*
*@param targetLeft 目标left值
*/
function slide(targetLeft) {
clearInterval(timer); //当鼠标重复移动到div1上时,会多次加载定时器,所以在函数开始运行时清除所有定时器
var oDiv1 = document.getElementById('div1');
timer = setInterval(function() {
var speed = 0;
if(targetLeft > oDiv1.offsetLeft) {
speed = 20; //left步长(速度),显示
}else {
speed = -20; //隐藏
}
if(oDiv1.offsetLeft == targetLeft) {
clearInterval(timer);
}else {
oDiv1.style.left = oDiv1.offsetLeft + speed + 'px';
}
},200)
}
</script>
</head>
<body>
<div id="div1"><span id="btn"></span></div>
</body>
</html>
二、透明度动画
知识点:
透明度
IE设置透明度
CSS设置透明度:
filter:alpha(opacity:30);
opacity的范围:0-100
JavaScript设置透明度:
node.style.filter = ‘alpha(opacity:30)’;
主流浏览器设置透明度
CSS设置透明度:
opacity:0.5;
opacity的范围:0-1
JavaScript设置透明度:
node.style.opacity = 0.5;
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#div1 {
width:200px;
height:200px;
background-color: #f00;
filter:alpha(opacity:30); //IE
opacity: 0.3;
}
</style>
<script type="text/javascript">
window.onload = function() {
var oDiv1 = document.getElementById('div1');
oDiv1.onmouseover = function() { moveAction(100); }
oDiv1.onmouseout = function() { moveAction(30); }
}
var timer = null;
var alpha = 30;
function moveAction(iTarget) {
clearInterval(timer);
var oDiv1 = document.getElementById('div1');
var speed = 0;
timer = setInterval(function() {
if(alpha > iTarget) {
speed = -10;
}
else {
speed = 10;
}
if(alpha == iTarget) {
clearInterval(timer);
}else {
alpha += speed;
oDiv1.style.filter = 'alpha(opacity:' + alpha + ')'; //IE
oDiv1.style.opacity = alpha/100; //主流浏览器
}
},30);
}
</script>
</head>
<body>
<div id="div1"></div>
</body>
</html>
效果:
三、缓冲运动
使用第一个例子。将speed值改为当前left值减去目标left值,并除以一个系数以控制速度,使其动画由快变慢。
不同的只是函数:
function slide(targetLeft) {
clearInterval(timer); //当鼠标重复移动到div1上时,会多次加载定时器,所以在函数开始运行时清除所有定时器
var oDiv1 = document.getElementById('div1');
timer = setInterval(function() {
var speed = 0;
//缓冲运动实现,控制speed值
speed = (targetLeft-oDiv1.offsetLeft)/20;
//speed值越来越小,由于是浮点型,最终会无限趋近于0,而不会到达0,所以要将speed值取整
speed = (speed>0)?Math.ceil(speed):Math.floor(speed);
//console.log(speed);
if(oDiv1.offsetLeft == targetLeft) {
clearInterval(timer);
}else {
oDiv1.style.left = oDiv1.offsetLeft + speed + 'px';
}
},30)
}
效果:
四、多物体动画
注意:多物体运动,所有参数和变量、定时器等都不能共用。
使用参数this和使用“.”给对象添加属性的方法使得变量和参数区分开来。
例子1:宽度值变化
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
* { padding:0; margin:0;}
ul,li {
list-style: none;
}
li {
width:200px;
height:100px;
margin:10px 0;
background-color: #ff0;
}
</style>
<script type="text/javascript">
window.onload = function() {
var oLi = document.getElementsByTagName('li');
for(var i=0;i<oLi.length;i++) {
oLi[i].timer = null;
oLi[i].onmouseover = function() {
slide(this,400);
}
oLi[i].onmouseout = function() {
slide(this,200);
}
}
}
/*
*@param iTarget 动画属性要达到的目标值
*/
function slide(obj,iTarget) {
clearInterval(obj.timer); //当鼠标重复移动到div1上时,会多次加载定时器,所以在函数开始运行时清除所有定时器
obj.timer = setInterval(function() {
//缓冲运动实现,控制speed值
speed = (iTarget-obj.offsetWidth)/20;
//speed值越来越小,由于是浮点型,最终会无限趋近于0,而不会到达0,所以要将speed值取整
speed = (speed>0)?Math.ceil(speed):Math.floor(speed);
//console.log(speed);
if(obj.offsetWidth == iTarget) {
clearInterval(obj.timer);
}else {
obj.style.width = obj.offsetWidth + speed + 'px';
}
},30)
}
</script>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</body>
</html>
效果:
例子2:透明度变化
透明度的多物体运动。注意IE定义透明度的方法在其他浏览器是不能识别的,这会导致写在诸如:filter:alpha('opacity:30')代码之后的CSS样式不被解析。故需要将IE的语法单独声明,防止出错。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
div {
width:150px;
height:150px;
background-color: #f00;
margin:10px;
opacity: 0.3;
float:left;
}
div {
filter:alpha(opacity:30); //IE
}
</style>
<script type="text/javascript">
window.onload = function() {
var oDiv = document.getElementsByTagName('div');
for(var i=0;i<oDiv.length;i++) {
oDiv[i].timer = null;
oDiv[i].alpha = 30;
oDiv[i].onmouseover = function() {
moveAction(this,100);
}
oDiv[i].onmouseout = function() {
moveAction(this,30);
}
}
}
function moveAction(obj,iTarget) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
var speed = 0;
if(obj.alpha > iTarget) {
speed = -10;
}else {
speed = 10;
}
if(obj.alpha == iTarget) {
clearInterval(obj.timer);
}else {
obj.alpha += speed;
obj.style.filter = 'alpha(opacity:' + obj.alpha + ')'; //IE
obj.style.opacity = obj.alpha/100; //主流浏览器
}
},30);
}
</script>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
<div id="div4"></div>
</body>
</html>
效果:
例子3:任意属性的多物体动画:
知识点:
设置样式
node.style[attr];
如:
node.style['width'];
node.style['fontSize'];
获取样式值
IE:
node.currentStyle(attr);
如:node.currentStyle('width');
FireFox:
getComputedStyle(obj,false)[attr];
如:getComputedStyle(node,false)['fontSize'];
以上都返回结果都带单位。
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
* { padding:0; margin:0;}
div {
width:100px;
height:100px;
background-color: #ff0;
border: 2px solid #000;
margin:10px;
}
#div1 {
}
#div2 {
}
</style>
<script type="text/javascript">
window.onload = function() {
var oDiv1 = document.getElementById('div1');
var oDiv2 = document.getElementById('div2');
oDiv1.onmouseover = function() {
slide(this,'height',400);
}
oDiv1.onmouseout = function() {
slide(this,'height',100);
}
oDiv2.onmouseover = function() {
slide(this,'width',200);
}
oDiv2.onmouseout = function() {
slide(this,'width',100);
}
}
/*
*@param iTarget 动画属性要达到的目标值
*/
function slide(obj,attr,iTarget) {
clearInterval(obj.timer); //当鼠标重复移动到div1上时,会多次加载定时器,所以在函数开始运行时清除所有定时器
obj.timer = setInterval(function() {
var iCur = parseInt(getStyle(obj,attr));
//缓冲运动实现,控制speed值
speed = (iTarget-iCur)/20;
//speed值越来越小,由于是浮点型,最终会无限趋近于0,而不会到达0,所以要将speed值取整
speed = (speed>0)?Math.ceil(speed):Math.floor(speed);
//console.log(speed);
if(iCur == iTarget) {
clearInterval(obj.timer);
}else {
obj.style[attr] = iCur + speed + 'px';
}
},30)
}
function getStyle(obj,attr) {
//IE
if(obj.currentStyle) {
return obj.currentStyle[attr];
}else {
return getComputedStyle(obj,false)[attr];
}
}
</script>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
</body>
</html>
效果:
例子4:兼容opacity透明度动画
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
* { padding:0; margin:0;}
div {
width:100px;
height:100px;
background-color: #ff0;
border: 2px solid #000;
margin:10px;
opacity:0.3;
}
div {
filter:alpha(opacity:30);
}
</style>
<script type="text/javascript">
window.onload = function() {
var oDiv1 = document.getElementById('div1');
oDiv1.onmouseover = function() {
slide(this,'opacity',100);
}
oDiv1.onmouseout = function() {
slide(this,'opacity',30);
}
}
/*
*@param targetLeft 目标left值
*/
function slide(obj,attr,iTarget) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
var iCur = 0;
if(attr == 'opacity') {
iCur = Math.round(parseFloat(getStyle(obj,attr))*100);
}else {
var iCur = parseInt(getStyle(obj,attr));
}
//缓冲运动实现,控制speed值
speed = (iTarget-iCur)/20;
//speed值越来越小,由于是浮点型,最终会无限趋近于0,而不会到达0,所以要将speed值取整
speed = (speed>0)?Math.ceil(speed):Math.floor(speed);
//console.log(speed);
if(iCur == iTarget) {
clearInterval(obj.timer);
}else {
if(attr == 'opacity') {
obj.style.filter = iCur + speed; //IE
obj.style[attr] = (iCur + speed)/100; //FireFox
}else {
obj.style[attr] = iCur + speed + 'px';
}
}
},30)
}
function getStyle(obj,attr) {
//IE
if(obj.currentStyle) {
return obj.currentStyle[attr];
}else {
return getComputedStyle(obj,false)[attr];
}
}
</script>
</head>
<body>
<div id="div1"></div>
</body>
</html>
由于例子三的局限性,它能实现宽、高、偏移等的动画(单位一致都为px),但不能设置透明度值,透明度不带单位,属性值为0-1的小数(FireFox,IE),或0-100的整数,不再适用例子三中的动画函数,故需对函数进行完善。
要实现透明度动画,只需判断attr参数是否是opacity,然后做相应处理即可。注意计算机不能准确存储小数这一点。
效果:
五、链式运动
链式运动就是一个动画完成后紧接着进行下一个动画,比如增加一个元素的高度后,马上增加其宽度,然后变化其透明度,依此类推。
为实现链式运动,将以上slide(obj,attr,iTarget)函数改为slide(obj,attr,iTarget,fun),增加一个fun参数,fun参数作为回调函数,传递的同样是slide函数。
在当前动画完成后判断是否有fun参数,如果有,就调用fun()执行下一个动画。
将slide函数单独取出封装到外部js文件。
move.js:
/*
*动画函数
*@param obj 对象名
*@param attr 对象属性名
*@param iTarget 属性目标值
*/
function move(obj,attr,iTarget,fun) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
//1.获取当前属性值
var iCur = 0;
if(attr == 'opacity') {
iCur = Math.round(parseFloat(getStyle(obj,attr))*100);
}else {
var iCur = parseInt(getStyle(obj,attr));
}
//2.计算动画速度
speed = (iTarget-iCur)/20;
speed = (speed>0)?Math.ceil(speed):Math.floor(speed);
//console.log(speed); //for debug
//3.检测是否停止动画
if(iCur == iTarget) {
clearInterval(obj.timer);
if(fun) {
fun();
}
}else {
if(attr == 'opacity') {
obj.style.filter = iCur + speed; //IE
obj.style[attr] = (iCur + speed)/100; //FireFox
}else {
obj.style[attr] = iCur + speed + 'px';
}
}
},30)
}
/*
*获取属性
*@param obj 对象名
*@param attr 属性名
*@return 属性值
*/
function getStyle(obj,attr) {
//IE
if(obj.currentStyle) {
return obj.currentStyle[attr];
}else {
return getComputedStyle(obj,false)[attr];
}
}
下面写一个链式动画的例子:
demo.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/move.js"></script>
<style type="text/css">
* { padding:0; margin:0;}
#div1 {
width:100px;
height:100px;
background-color: #ff0;
border: 2px solid #000;
margin:10px;
opacity:0.3;
}
#div1 {
filter:alpha(opacity:30);
}
</style>
<script type="text/javascript">
window.onload = function() {
var oDiv1 = document.getElementById('div1');
oDiv1.onmouseover = function() {
move(oDiv1,'width',200,function() {
move(oDiv1,'height',200,function(){
move(oDiv1,'opacity',100);
});
});
}
oDiv1.onmouseout = function() {
move(oDiv1,'opacity',30,function(){
move(oDiv1,'height',100,function(){
move(oDiv1,'width',100);
});
});
}
}
</script>
</head>
<body>
<div id="div1"></div>
</body>
</html>
效果:
六、同时运动
想一想,如果要实现宽高同时变化,上面的方法能实现吗?
答案是不能。因为每次调用函数的时候,都清除了定时器,也就是说,一次最多只能有一个定时器在运行,每次只能完成一个属性的动画。
那么,如何实现同时运动呢?
可以使用字面量创建对象,将多组属性和值声明在一个字面量对象中。利用for...in...循环完成多组属性动画的遍历。
具体就是将原来的move函数进行如下变换:
move(obj,attr,iTarget,fun) 将attr和iTarget组合为字面量对象格式
变换后:move(obj,json,fun)
字面量创建含多组属性和值的对象——json参数的格式:
var json = {width:200,height:100,opacity:100}
使用for...in...遍历json:
for(var attr in json)
attr就表示属性名称
json[attr]就表示属性值
例如:
var json = {width:200,height:100,opacity:100}
for(var attr in json) {
console.log(attr); //输出:width,height,opacity
console.log(json[attr]); //输出:200,100,100
}
同时要注意,当判断是否完成动画时,要所有动画都完成才可以,否则,当一个属性的动画完成后,会清除定时器。可以创建一个flag标记是否所有动画都完成。
完善后的move函数如下:
/*
*动画函数
*@param obj 对象名
*@json json格式:{attr:iTarget[,attr:iTarget,...]}
*@param attr 对象属性名
*@param iTarget 属性目标值
*/
function move(obj,json,fun) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
var iCur = 0;
var flag = true;
for(var attr in json) {
//1.获取当前属性值
if(attr == 'opacity') {
iCur = Math.round(parseFloat(getStyle(obj,attr))*100);
}else {
var iCur = parseInt(getStyle(obj,attr));
}
//2.计算动画速度
speed = (json[attr]-iCur)/10;
speed = (speed>0)?Math.ceil(speed):Math.floor(speed);
console.log(iCur); //for debug
//3.检测是否停止动画
if(iCur != json[attr]) {
flag = false;
}
if(attr == 'opacity') {
obj.style.filter = iCur + speed; //IE
obj.style[attr] = (iCur + speed)/100; //FireFox
}else {
obj.style[attr] = iCur + speed + 'px';
}
}
if(flag) {
clearInterval(obj.timer);
if(fun) {
fun();
}
}
},30)
}
沿用第五节HTML:
<script type="text/javascript">
window.onload = function() {
var oDiv1 = document.getElementById('div1');
oDiv1.onmouseover = function() {
move(oDiv1,{width:150,height:200,opacity:100},function() {move(oDiv1,{borderWidth:10})});
}
oDiv1.onmouseout = function() {
move(oDiv1,{height:100,width:100,opacity:30},function() {move(oDiv1,{borderWidth:2})});
}
}
</script>
效果:
--to be continued--