逆战学习
整理一下如何进行缓冲运动的封装
改变运动物体其自身的属性,比如宽高,位置,透明度等等
属性–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文件,哪个时候需要就引入。