前面我们已经掌握了很多运动相关的知识,也构造出了自己的运动框架,当然,它离真正的完成版运动框架还有很远的距离。
链式运动框架
在我们讲解链式运动框架时,我们需要讲解一下什么是回调函数。在我们之前的一个运动函数startMove中,如果我们再添加一个参数,而且该参数是一个函数,我们希望在运动结束后调用这个函数——这就是所谓的回调函数。
function startMove(obj, attr, iTarget, fnEnd){ clearInterval(obj.timer); obj.timer=setInterval(function (){ var cur=0; if(attr=='opacity') { cur=Math.round(parseFloat(getStyle(obj, attr))*100); } else { cur=parseInt(getStyle(obj, attr)); } var speed=(iTarget-cur)/6; speed=speed>0?Math.ceil(speed):Math.floor(speed); if(cur==iTarget) { clearInterval(obj.timer); if(fnEnd)fnEnd(); } else { if(attr=='opacity') { obj.style.filter='alpha(opacity:'+(cur+speed)+')'; obj.style.opacity=(cur+speed)/100; } else { obj.style[attr]=cur+speed+'px'; } } }, 30);};
在clearInterval之后,我们调用这个函数(当然这里需要判断一下函数是否被传入)。
<!DOCTYPE HTML><html> <head> <meta charset="utf-8"> <title>无标题文档</title> <style> #div1 {width:100px; height:100px; background:red; filter:alpha(opacity:30); opacity:0.3;} </style> <script src="move.js"></script> <script> window.onload=function () { var oDiv=document.getElementById('div1'); oDiv.onmouseover=function () { startMove(oDiv, 'width', 300, function (){ startMove(oDiv, 'height', 300, function (){ startMove(oDiv, 'opacity', 100); }); }); }; oDiv.onmouseout=function () { startMove(oDiv, 'opacity', 30, function (){ startMove(oDiv, 'height', 100, function (){ startMove(oDiv, 'width', 100); }); }); }; }; </script> </head> <body> <div id="div1"></div> </body></html>
效果如下:
可以看到,我们通过回调函数的嵌套实现了一个比较炫酷的伸缩展开效果——这就是一个简单的链式运动框架。
完美运动框架
到目前为止,我们学习的运动框架依然是有问题存在的:
<html> <head> <meta charset="utf-8"> <title>无标题文档</title> <style> #div1 {width:100px; height:100px; background:red;} </style> <script src="move.js"></script> <script> window.onload=function () { var oBtn=document.getElementById('btn1'); var oDiv=document.getElementById('div1'); oBtn.onclick=function () { startMove(oDiv, 'width', 300); startMove(oDiv, 'height', 300); }; }; </script> </head> <body> <input id="btn1" type="button" value="运动" /> <div id="div1"></div> </body></html>
效果如下:
可以看到,我们试图让div的宽和高同时变化,但结果而言只有高度发生了变化,原因在于两次调用函数的定时器产生了干扰,因此只有后一个startmove函数生效了。那么,我们现在的运动框架无法使好几个属性同时发生变化,应该怎么解决它呢?答案是通过json,json有一个重要用法是循环——使用for in方法进行。我们之前传入属性和属性值是通过两个参数进行的,现在我们直接传入一个json,将属性和属性值分别作为键名和键值传入,这样我们就可以同时传入好几组值了。
function startMove(obj, json, fnEnd){ clearInterval(obj.timer); obj.timer=setInterval(function (){ var bStop=true; //假设:所有值都已经到了 for(var attr in json) { var cur=0; if(attr=='opacity') { cur=Math.round(parseFloat(getStyle(obj, attr))*100); } else { cur=parseInt(getStyle(obj, attr)); } var speed=(json[attr]-cur)/6; speed=speed>0?Math.ceil(speed):Math.floor(speed); if(cur!=json[attr]) bStop=false; if(attr=='opacity') { obj.style.filter='alpha(opacity:'+(cur+speed)+')'; obj.style.opacity=(cur+speed)/100; } else { obj.style[attr]=cur+speed+'px'; } } if(bStop) { clearInterval(obj.timer); if(fnEnd)fnEnd(); } }, 30);}
通过json键值对和for in循环,我们就可以同时改变一个元素的好几个属性了。这里注意一点,原本的运动框架当属性值等于目标值时,运动就会停下来,但同时运动的时候几个运动结束的时间并不是一样的——我们应该等所有运动都结束之后再关闭定时器,因此我们建立了一个bStop变量来判断是否所有运动都到达了终点。
<html> <head> <meta charset="utf-8"> <title>无标题文档</title> <style> #div1 {width:100px; height:100px; background:red; filter:alpha(opacity:30); opacity:0.3;} </style> <script src="move2.js"></script> <script> window.onload=function () { var oBtn=document.getElementById('btn1'); var oDiv=document.getElementById('div1'); oBtn.onclick=function () { startMove(oDiv, {width:101, height: 300, opacity: 100}, function (){ alert('a'); }); }; }; </script> </head> <body> <input id="btn1" type="button" value="运动" /> <div id="div1"></div> </body></html>
效果如下:
这样,这个框架既可以同时改变元素的不同属性值(通过json实现),也可以分阶段进行属性值改变(通过回调函数实现),就形成了一个比较完美的运动框架,在css2范围内,这个运动框架已经足够使用了。
到这里我们的运动框架基本就已经讲解结束了,这里为大家总结一下我们编写过的运动框架:
startMove(iTarget) 运动框架
startMove(obj, iTarget) 多物体
startMove(obj, attr, iTarget) 任意值
startMove(obj, attr, iTarget, fn) 链式运动
startMove(obj, json) 多值运动
startMove(obj, json, fn) 完美运动框架
我是石川(Blue),如果你觉得我的文章还不错,请多帮我推荐给你的朋友,多谢了。
作者简介:前阿里巴巴高级技术经理,现开课吧技术学院院长。精通C/C++、Java、Python、前端开发等多种开发技术,曾参与淘宝网的早期建设和优化,拥有丰富的企业级系统开发经验,对HTML5移动端互联网技术及生态体系有深厚的造诣。
了解更多前端知识,可以关注文章的评论。
本文暂时没有评论,来添加一个吧(●'◡'●)