标签:
运动,其实就是在一段时间内改变left、right、width、height、opactiy的值,到达目的地之后停止。
现在按照以下步骤来进行我们的运动框架的封装:
(第一部分):匀速运动
运动基础
思考:如何让div动起来?
如下:
根据上面的信息我们就可以开始封装运动框架创建一个变化的div了。
1 2 3 4 5 6 7 8 9 10 |
/** * 运动框架-1-动起来 * @param {HTMLElement} element 进行运动的节点 */ var timer = null; function startMove(element) { timer = setInterval(function () {//定时器 element.style.left = element.offsetLeft + 5 + "px"; }, 30); } |
例如:window.onload = function()
{
var oBtn = document.getElementById(‘btn‘); //元素的获取
oBtn.onclick = function()
{
var oDiv = document.getElementById(‘div1‘); //设置定时器
setInterval(function()
{ //改变物体位置
oDiv.style.left = oDiv.offsetLeft + 10 + ‘px‘;
},30)
}
}
你没看错,就是那么简单。但是等等, what? 怎么不会停?
那是因为我们没有运动终止条件。好再还是比较简单。直接在定时器内部,判断到达目标值,清除定时器就行啦!
判断很重要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * 运动框架-2-运动终止 * @param {HTMLElement} element 进行运动的节点 * @param {number} iTarget 运动终止条件。 */ var timer = null; function startMove(element, iTarget) { timer = setInterval(function () { element.style.left = element.offsetLeft + 5 + "px"; if (element.offsetLeft === iTarget) {//停止条件 clearInterval(timer); } }, 30); } |
例如:window.onload = function()
{
var timer=null;
var oBtn = document.getElementById(‘btn‘);
oBtn.onclick = function()
{
var oDiv = document.getElementById(‘div1‘); //设置定时器
var timer = setInterval(function()
{ //判断停止条件
if(oDiv.offsetLeft > 300)
{
clearInterval(timer);
}
else{ //改变物体位置
oDiv.style.left = oDiv.offsetLeft + 10 + ‘px‘;
document.title = oDiv.offsetLeft; //显示当前的边界值
}
},30); }
}
就这样是不是就完成了呢?已经ok了呢?
no。还有一些Bug需要处理。
运动中的Bug
解决BUG
var timer = null;
function startMove(element, iTarget) {
clearInterval(timer);//在开始运动时,关闭已有定时器
timer = setInterval(function () {
var iSpeed = 5;//把速度用变量保存
//把运动和停止隔开(if/else)
if (element.offsetLeft === iTarget) {//结束运动
clearInterval(timer);
} else {
element.style.left = element.offsetLeft + iSpeed + "px";
}
}, 30);
这样一个简单的运动框架就完成了。但是,再等等。只能向右走?别急,我们不是定义了把速度变成为了变量吗?只需要对它进行一些处理就行啦!
var iSpeed = 5;–>
1 2 3 4 5 6 7 |
//判断距离目标位置,达到自动变化速度正负 var iSpeed = 0; if (element.offsetLeft < iTarget) { iSpeed = 5; } else { iSpeed = -5; } |
例如:
透明度动画
1 2 3 4 5 6 7 8 |
//透明度浏览器兼容实现 if (alpha === iTarget) { clearInterval(time); } else { alpha += speed; element.style.filter = ‘alpha(opacity:‘ + alpha + ‘)‘; //兼容IE element.style.opacity = alpha / 100;//标准 } |
例如:
(二)缓冲动画
思考:怎么样才是缓冲动画?
应该有以下几点:
还是对速度作文章:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * 运动框架-4-缓冲动画 */ function startMove(element, iTarget) { clearInterval(timer); timer = setInterval(function () { //因为速度要动态改变,所以必须放在定时器中 var iSpeed = (iTarget - element.offsetLeft) / 10; //(目标值-当前值)/缩放系数=速度 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 if (element.offsetLeft === iTarget) {//结束运动 clearInterval(timer); } else { element.style.left = element.offsetLeft + iSpeed + "px"; } }, 30); } |
潜在问题目标值不是整数时
例如:
上述方法可以得到缓冲运动,但是实际运行效果,物体并没有在 300的位置停掉,而是在 293的位置就停掉了。究其原因。因为当物体的速度小于1 的时候。物体位置为293。此时计算的速度是 0.875.通过 oDiv.style.left = oDiv.offsetLeft + iSpeed + ‘px‘; 物体的位置为 293.875.可是计算机不能识别小数,将小数省略了。此时的 位置offsetLeft仍然是 293.再计算一次,还是同样的结果。定时器没有关掉,但是物体的位置却再也无法改变,故停留在了 293的位置。解决方案,就是将速度进行向上取整。但是,像上述运动,速度是正的,可是,当速度是负的时候,就同样会有相同的结果,因此需要在速度为负的时候,向下取整。
(三)多物体运动
思考:如何实现多物体运动?
处理多物体运动,运动函数里面每次都要选取一个元素加事件。如果需要对多个物体进行同样的运动, 需要将运动对象作为参数传进来。
(四)任意值变化
咳咳。我们来给div加个1px的边框。boder :1px solid #000
然后来试试下面的代码
1 2 3 |
setInterval(function () { oDiv.style.width = oDiv.offsetWidth - 1 + "px"; }, 30) |
嗯,神奇的事情发生了!what?我设置的不是宽度在减吗?怎么尼玛增加了! 不对啊,大兄弟。
究竟哪里出了问题呢?
一起找找资料,看看文档,原来offset这一系列的属性都会存在,被其他属性干扰的问题。
好吧,既然不能用,那么我们就顺便把任意值变化给做了吧。
第一步:获取实际样式
使用offsetLeft..等获取样式时, 若设置了边框, padding, 等可以改变元素宽度高度的属性时会出现BUG..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * 获取实际样式函数 * @param {HTMLElement} element 需要寻找的样式的html节点 * @param {String]} attr 在对象中寻找的样式属性 * @returns {String} 获取到的属性 */ function getStyle(element, attr) { //IE写法 if (element.currentStyle) { return element.currentStyle[attr]; //标准 } else { return getComputedStyle(element, false)[attr]; } } |
第二步:改造原函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/** * 运动框架-4-任意值变化 * @param {HTMLElement} element 运动对象 * @param {string} attr 需要改变的属性。 * @param {number} iTarget 目标值 */ function startMove(element, attr, iTarget) { clearInterval(element.timer); element.timer = setInterval(function () { //因为速度要动态改变,所以必须放在定时器中 var iCurrent=0; iCurrent = parseInt(getStyle(element, attr));//实际样式大小 var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 if (iCurrent === iTarget) {//结束运动 clearInterval(element.timer); } else { element.style[attr] = iCurrent + iSpeed + "px"; } }, 30); } |
试一试,这样是不是就可以了呢?
还记得上面我们写的透明度变化吗? 再试试
果然还是不行, (废话,你见过透明度有”px”单位的么? - -白眼 )
第三步:透明度兼容处理
思考:需要对那些属性进行修改?
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 |
/** * 运动框架-5-兼容透明度 * @param {HTMLElement} element 运动对象 * @param {string} attr 需要改变的属性。 * @param {number} iTarget 目标值 */ function startMove(element, attr, iTarget) { clearInterval(element.timer); element.timer = setInterval(function () { //因为速度要动态改变,所以必须放在定时器中 var iCurrent = 0; if (attr === "opacity") { //为透明度时执行。 iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100); } else { //默认情况 iCurrent = parseInt(getStyle(element, attr)); //实际样式大小 } var iSpeed = (iTarget - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 if (iCurrent === iTarget) {//结束运动 clearInterval(element.timer); } else { if (attr === "opacity") { //为透明度时,执行 element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE element.style.opacity = (iCurrent + iSpeed) / 100; //标准 } else { //默认 element.style[attr] = iCurrent + iSpeed + "px"; } } }, 30); } |
到这里,这个运动框架就基本上完成了。但是,我们是追求完美的不是吗?
继续进化!
多物体的淡入淡出的时候,也有类似的问题。因为修改透明度的时候,是先用一个变量保存透明度,必须针对每个物体设立透明度值属性。
4.1 位置属性的bug
offsetWidth 或者 offsetHeight 等位置属性,一旦给他们加上 border。则会有诡异的现象出现。
例如 oDiv.style.width = oDiv.offsetWidth - 1 + ‘px‘; 如果给 oDiv 的width 为一百,border 为 1.则这个物体的 width是100px;offsetWidth 为102px;带入公式之后,即减一之后。100 = 102 - 1 ,反而等于101;即 物体本来要减小,事实却增大了。解决的方案就是,加减的时候,必须使用物体的内联样式。但是 火狐 和 IE 又有兼容模式。解决方案如下:
其中,getStyle函数,传入一个元素对象,和其 css 属性,获取的是元素的样式,即 witdh 100px;因此需要parseInt转换
5.任意值运动
通过 getStyle 函数,可以获取元素的样式,还可也通过 attr 制定需要修改的 css属性。这样就能是物体有不同的运动形式。
例如:
多物体的淡入淡出的时候,也有类似的问题。因为修改透明度的时候,是先用一个变量保存透明度,必须针对每个物体设立透明度值属性
offsetWidth 或者 offsetHeight 等位置属性,一旦给他们加上 border。则会有诡异的现象出现。
其中,getStyle函数,传入一个元素对象,和其 css 属性,获取的是元素的样式,即 witdh 100px;因此需要parseInt转换
任意值运动
通过 getStyle 函数,可以获取元素的样式,还可也通过 attr 制定需要修改的 css属性。这样就能是物体有不同的运动形式。
任意值完美版
上述版本,还不能处理透明度的任意值,因此需要增加额外的兼容hack。
链式动画(运动)
链式动画:顾名思义,就是在该次运动停止时,开始下一次运动。(我们的运动框架到目前为止,基本功能都能实现了。现在拓展。所谓链式运动,即运动接着运动。当运动停止的时候,如果回调一个函数。回调一个运动函数,就能出现这样的效果。因此传入一个函数作为回调函数。)
如何实现呢?
在当前属性到达目的地时iCurrent === iTarget,判断是否有回调函数存在,有则执行。
1 2 3 4 5 6 |
if (iCurrent === iTarget) {//结束运动 clearInterval(element.timer); if (func) { func();//回调函数 } }
|
链式动画完成!距离完美还差一步!
例如:
同时运动
思考:如何实现同时运动?
实现:
否则,继续执行代码,直到所有属性值等于目标值。
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 |
/** * 获取实际样式函数 * @param {HTMLElement} element 需要寻找的样式的html节点 * @param {String]} attr 在对象中寻找的样式属性 * @returns {String} 获取到的属性 */ function getStyle(element, attr) { //IE写法 if (element.currentStyle) { return element.currentStyle[attr]; //标准 } else { return getComputedStyle(element, false)[attr]; } } /** * 完美运动框架 * @param {HTMLElement} element 运动对象 * @param {JSON} json 属性:目标值 * @property {String} attr 属性值 * @config {Number} target 目标值 * @param {function} func 可选,回调函数,链式动画。 */ function startMove(element, json, func) { var flag = true; //假设所有运动到达终点. clearInterval(element.timer); element.timer = setInterval(function () { for (var attr in json) { //1.取当前的属性值。 var iCurrent = 0; if (attr === "opacity") { //为透明度时执行。 iCurrent = Math.round(parseFloat(getStyle(element, attr)) * 100); } else { //默认情况 iCurrent = parseInt(getStyle(element, attr)); //实际样式大小 } //2.算运动速度,动画缓冲效果 var iSpeed = (json[attr] - iCurrent) / 10; //(目标值-当前值)/缩放系数=速度 iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); //速度取整 //3.未到达目标值时,执行代码 if (iCurrent != json[attr]) { flag = false; //终止条件 if (attr === "opacity") { //为透明度时,执行 element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; //IE element.style.opacity = (iCurrent + iSpeed) / 100; //标准 } else { //默认 element.style[attr] = iCurrent + iSpeed + "px"; } } else { flag = true; } //4. 运动终止,是否回调 if (flag) { clearInterval(element.timer); if (func) { func(); } } } }, 30); } |
(目前为止,我们的运动框架还有个小缺点,就是不能同时该两个属性进行运动,比如同时更改宽和高。这个要求传入的属性是不同的几个值。则考虑传入一个 json用来保存需要更改的属性。)
例如:
上述代码,可以解决了同时运动的问题。但是还是有一个bug。比如,同时运动的某个属性,如果变化很小,马上就停止了,即关掉了定时器。那么会造成其他属性的变化也停止。因为这些属性都共用了一个定时器。因此需要判断,假设有三个人要来,然后一起去爬山。三个人有的先来,有的后来,只要三个人都到齐了,才出发。也就是只有三个属性都到了目标值,才关定时器。一开始,设立一个检查量,为真。假设所有人都到了,然后循环,只有有一个人没有到,检查就为假。直到所有的都到了,检测为真。则停止定时器。
再循环外定义一个 标志变量 bStop = true。用来表示所有属性到达目标值。等循环结束了,如果这个值是真的,则停止定时器。因为,每次运行定时器,都会初始化这个值。循环的过程中,只要有一个没有到,bStop就被设定为 false。如果某个到了,此时 iCur != json[attr],表示速度为0 后面执行的结果,也不会有变化。只有所有的都达到目标值。循环则不再改变 bStop的值。此时,只要下一次运行定时器。就是初始化 bStop为真。而循环因为都到了,所以速度为0 也就再也没有变化。循环结束,sBstop还是真,表示所有都到了。因此此时结束定时器。
最后附上完美运动框架,封装成 move.js 就可以调用了。
运动框架总结
框架 |
变化 |
startMove(element) |
运动 |
startMove(element,iTarget) |
匀速–>缓冲–>多物体 |
startMove(element,attr,iTargrt) |
任意值 |
startMove(element,attr,iTargrt,func) |
链式运动 |
startMove(element,json,func) |
多值(同时)–>完美运动框架 |
标签:
原文地址:http://www.cnblogs.com/bfwbfw/p/5950189.html