本文将按照如下步骤,封装一个可以实现同时运动和链式运动的JavaScript动画函数,即运动框架。javascript
运动框架实现思路:css
速度(改变值left、right、width、height、opacity)
html
缓冲运动(动画的快慢变化)java
多物体运动node
任意值变化json
链式运动浏览器
同时运动框架
效果:ide
思路:函数
建立一个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>
知识点:
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和使用“.”给对象添加属性的方法使得变量和参数区分开来。
<!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>
效果:
透明度的多物体运动。注意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>
效果:
知识点:
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>
效果:
<!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--