JavaScript学习:缓冲运动的封装

逆战学习
整理一下如何进行缓冲运动的封装

运动的原理

改变运动物体其自身的属性,比如宽高,位置,透明度等等
属性–css属性

要完成这个缓冲运动的封装,首先要利用以下的方法和知识

1.获取任意的css属性值

获取要操纵的元素或者要运动的元素的位置和宽高,透明度;之后才好变化

currentStyle//--兼容IE
getComputedStyle//--该属性是兼容火狐谷歌,不兼容IE
//上面两种有单位,下面四种没有单位
offsetWidth//获取的是盒子最终的宽
offsetHeight//获取的是盒子最终的高
offsetLeft//获取自身左外边框到定位父级的左内边框的距离
offsetTop//获取自身上外边框到定位父级的上内边框的距离
//以上属性只能获取,无法修改 

2.设置物体的css属性值。

元素对象.style.css属性名 = 属性值
元素对象.style.cssText = "css属性代码"

3.定时器–快速的执行

一、简单的运动

有了以上的知识就可以做一个简单的运动;
当然让一个物体位置改变那么它的position就必须是absolute;
比如让一个div盒子水平方向运动;
只需要定时器不断获取它的位置然后加上一个数值赋值给它的位置
css代码

.box {
            width: 100px;
            height: 100px;
            background-color: orange;
            position: absolute;
            left: 0px;
            top: 0;
        }

html代码:

"box">

js代码如下

	const oBox = document.querySelector('.box');
	oBox.onclick = function () {
            //oBox.offsetLeft:获取当前盒子的位置
            //setInterval:间隔定时器(反复执行)
            //通过定时器不断的改变盒子的位置,运动的原理。
            setInterval(() => {
                oBox.style.left = oBox.offsetLeft + 10 + 'px';
            }, 1000 / 60);
        }

后面的代码都是基于这里修改;

二、问题处理和封装

如果运行了上面代码,就会发现有一些问题;
当然运动不可能无休止的进行下去,加入一个目标值。
设置参数并传入函数里面;
这时可以独立一个函数出来,不再是直接写在oBox.onclick这个点击事件里,简单的封装后让oBox调用

问题1:重复点击盒子,定时器会叠加,运动的速度越来越快。
问题2:这是匀速运动,缓冲运动,离目标越远,速度越大。越接近目标,速度越小

解决方式:关闭定时器,设置速度

	 function bufferMove(obj, attr, target) {//obj:元素对象   attr:css属性名。  target:目标属性值
            clearInterval(timer);//解决定时器叠加问题
            timer = setInterval(() => {
                //1.求速度
                //速度>0向上取整,速度<0向下取整。
                speed = (target - parseInt(getStyle(obj, attr))) / 10;//求速度  parseInt:去单位。
                speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

                //2.判断停止
                if (parseInt(getStyle(obj, attr)) === target) {//停止
                    clearInterval(timer);
                } else {//继续运动
                    oBox.style[attr] = parseInt(getStyle(obj, attr)) + speed + 'px';
                }
            }, 1000 / 60);
        }
        oBox.onclick = function () {
            bufferMove(oBox, 'width', 1000)
        }

上面的函数里面还有一个方法叫getStyle,是获取任意css属性值的一个兼容写法
代码如下:

 function getStyle(obj, attr) {//obj:元素对象,attr:css属性名
            if (window.getComputedStyle) {//条件用属性做,不存在就是undefined,标准
                return window.getComputedStyle(obj)[attr];
            } else {//非标准
                return obj.currentStyle[attr];//100px
            }
        }

到这里已经可以解决上下左右运动的问题了;但是如果我想改变其他的属性而不只是位置呢,我想改变透明度又该如何解决

问题3:任意属性的改变 --getStyle函数解决
问题4:任意属性的改变 --改变透明度—通过判断属性解决

问题三已经通过getStyle函数解决了,但是只是取到了元素和属性,那么透明度的改变就需要增加条件来判断取出的属性是不是透明度;
1.在定时器函数里增加一个当前值currentValue初始值为null占位置;
2.然后判断属性是否为opacity分别赋值给当前值;
3.opacity值区间为0-1;扩大100倍数方便与位置的数值差不多便于计算速度;
4.判断停止时注意将 opacity 值缩小100倍再赋给元素;
代码如下:

var currentValue = null;//1
    if (attr === 'opacity') {//opacity透明度  2
         currentValue = Math.round(getStyle(obj, attr) * 100); //  3
     } else {//其他css属性
         currentValue = parseInt(getStyle(obj, attr));
     }

	speed = (target - currentValue) / 10;//求速度  parseInt:去单位。
    speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

if (currentValue === target) {//停止
	  clearInterval(timer);
	} else {//继续运动
	   if (attr === 'opacity') {
	       obj.style.opacity = (currentValue + speed) / 100;//缩小100倍  4
	       obj.style.filter = 'alpha(opacity=' + (currentValue + speed) + ')';//IE
	   } else {
	       obj.style[attr] = currentValue + speed + 'px';
	   }
	}

做到这里单个物体的缓冲运动已经差不多做好了;
但如果你测多个物体的运动时就又出现bug,比较混乱;
而且我想要物体的宽度变了之后透明度或者高度也能运动起来;

问题5:多物体运动–多物体共用一个定时器出现问题。
问题6:物体进行链式运动(上一次运动结束,才开启下一次运动)

解决方法:
1.给定时器的返回值设置为当前 对象.值 — obj.timer
这样每一个物体的定时器都是不同的
2.链式运动,可以在函数调用时再调用自身
3.再给原函数增加参数fn 这样原函数参数为(obj,attr, target,fn)
4.判断fn是否执行,若不存在或传入参数不是函数就不执行。可以用逻辑运算符做判断条件 — fn && typeof fn === ‘function’ && fn();
代码如下:
给bufferMove函数下的定时器设置返回值

	obj.timer = setInterval(function () {
	}

然后给判断停止时里面的条件加一句执行回调函数的代码

if (currentValue === target) {//运动停止
 clearInterval(obj.timer);
  fn && typeof fn === 'function' && fn();//执行回调函数,如果fn存在,执行fn
   } 

现在还有些问题,当前只能够每次改变一个属性,如果想要多个目标和属性变化,则还需要优化。

问题7:多属性同时运动

解决方法:
1.把属性装入一个对象里面,for…in遍历取出,设这个对象名为 json
2.就是把定时器里面的代码放入for…in这个遍历中

function bufferMove(obj, json, fn) {  
            let speed = 0;
            clearInterval(obj.timer);//将定时器的返回值给obj元素的属性。
            obj.timer = setInterval(function () {
                for (var attr in json) {//之前定时器里面的代码
                //attr和之前表示的意义相同,目标值target变为json[attr]就可以

				}
				}, 1000 / 60);
        }

到了此时,运行代码,还是存在问题,某些属性没有达到目标值运动就停止了。
于是出现了新的问题问题,只要有一个到了目标点,运动停止了
当然这是不行的,需要所有的物体都到了目标点,才停止。

问题8:如果有一个到了目标点,运动停止了。

解决方法:
1.把定时器停止和回调函数执行的代码块提出遍历的代码块
2.假设一个标记设分为flag;初始值flag= true;
3.改变判断停止的条件,只要有一个没有到目标点就继续运动
4.给判断停止的条件末尾加一句 flag = false;
5.通过判断flag的布尔值来让定时器停止和回调函数执行;
代码:

function bufferMove(obj, json, fn) {
            let speed = 0;
            clearInterval(obj.timer);
            obj.timer = setInterval(function () {
                var flag = true; //假设标记,代表运动结束
                for (var attr in json) {//attr:css属性名
                    var currentValue = null;
                    if (attr === 'opacity') {
                        currentValue = Math.round(getStyle(obj, attr) * 100);  
                    } else {
                        currentValue = parseInt(getStyle(obj, attr));
                    } 
                    speed = (json[attr] - currentValue) / 10;
                    speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                    //3.判断停止
                    if (currentValue !== json[attr]) {//没到目标点,继续运动
                        if (attr === 'opacity') {
                            obj.style.opacity = (currentValue + speed) / 100;
                            obj.style.filter = 'alpha(opacity=' + (currentValue + speed) + ')';//IE
                        } else {
                            obj.style[attr] = currentValue + speed + 'px';
                        }
                        flag = false;//flag为flase表示运动还未停止
                    }
                }
                if (flag) {
                    clearInterval(obj.timer);
                    fn && typeof fn === 'function' && fn();//执行回调函数,如果fn存在,执行fn
                }

            }, 1000 / 60);
        }

最后,这个缓冲运动的封装就完成了,适合单物体,多物体的位置,宽高,透明度的运动。

三、总结:

这个缓冲运动的封装的参数为(obj,json,fn);
obj:所要操纵运动的元素;
json:运动的属性和目标值—例如

       {width:200,//width表示属性,后面数值表示目标值(运动的终点)
		height:300
 		}

fn:是否进行链式运动的函数

最终代码就是最后封装的那部分加上getStyle函数,可以把他们单独写入一个js文件,哪个时候需要就引入。

你可能感兴趣的:(JavaScript学习:缓冲运动的封装)