匀速运动
在原生javascript中实现运动的主要工具是定时器,通过设置固定的间隔时间,使元素在确定的间隔时间内实现距离的变化。而运动变化的主要表现形式是距离的变化
例如,定时器频率可如下列代码所示,设置为30ms。每30ms对s的值进行更新,使其增加一个步长step的距离,来实现视觉上的元素运动效果
setInterval(function(){s = s + step},30)
这里我们先使用之前一节的样式获取函数
/*** 获取对象的样式* @param obj 元素对象* @param type 样式名称* @returns {*} 样式属性*/function getStyle(ele,type){if(window.getCompuutedStyle){// 正常浏览器return getComputedStyle(obj,null)[type]}else{//IE 8return obj.currentStyle[type]}}
函数封装
/*** 单个对象单个匀速运动* @param ele 对象* @param type 变化属性,-left-width* @param target 目标值* @param time 时间*/function uniformSpeed(ele,type,target,time){// 当前的属性值, 已经移动的值let cur = parseFloat(getStyle(ele,type)),move =cur;if(target === cur) return ;// 这里设置每秒钟刷新60次,每次刷新时间为16.7秒let step = Math.floor((target-cur)/time*16.7*10000)/10000;// 设置动画定时器let timer = setInterval(()=>{move += step;// 设置清除条件if(target>cur ? move>=target : move<=target){ele.style[type] = target + 'px';clearInterval(timer)}else{ele.style[type] = move + 'px';}},16.6)}
缺点:
这个虽然能实现简单的动画效果,但是还是有很多的缺陷,比如不能实现多个属性同时变换,不能对opacity,颜色等样式进行变换;
加速运动
说到加速运动,必须要提到一个物理名词——加速度
加速度: 
实验用推论:Δs= aT^2 {Δs为连续相邻相等时间(T)内位移之差}
我们还是用之前的那个公式
setInterval(function(){s = s + step},30)
依靠上面的推断第n次20ms后与第一次的位移差的关系是
step = n * Δs
这里我们用20ms方便计算
/*** 单个对象单个加速运动* @param ele 对象* @param type 变化属性,-left-width* @param target 目标值* @param time 时间*/function addSpeed(ele,type,target,time){// 声明变量let cur = parseFloat(getStyle(ele,type)),move = 0,a = (target-cur)/time/time*2, //加速度step = a*20*20, // 距离差 Δs= aT^2move = cur, // 移动的距离times = 0; // 循环多少次if(target === cur) return ;// 设置动画定时器let timer = setInterval(()=>{times ++;move += times*step -1/2*step;// 设置清除条件if(target>cur ? move>=target : move<=target){ele.style[type] = target + 'px';clearInterval(timer)}else{ele.style[type] = move + 'px';}},20)}
减速运动
对于减速运动来说我们可以设置他的初始速度稍大,在最后一步大于目标值时设置速度为0,并清除定时器
function descSpeed(ele,type,target,time) {let cur = parseFloat(getStyle(ele,type)),a = Math.floor((target-cur)/time/time*2*100000)/100000,step = a*20*20,move = cur,times = Math.floor((time/20+3)*100000)/100000; // 如果想要初始速度大一些可以设置+nif(target === cur) return ;// 设置动画定时器let timer = setInterval(()=>{move += times*step -1/2*step;times --;// 设置清除条件if(target>cur ? move>=target : move<=target){ele.style[type] = target + 'px';clearInterval(timer)}else{ele.style[type] = move + 'px';}},20)}
缓存运动
缓冲运动就是当运动到目的地时速度干好是0,那么可以设置上面的n为0或者1;
加减速运动
半段运动时,做加速运动。到达指定点时,做减速运动,最终到达终点停止,
这里我就偷懒了一下,直接前半段调用加速运动,后半段调用减速运动;
// 加速运动——增加了callback函数和callbak的参数options// 如果没有options则调用自身的函数的前几个参数,并且在清除定时器时调用回调函数// function addSpeed(ele,type,target,time,callback,options)// 减速运动// function descSpeed(ele,type,target,time,callback,options)// 加减速运动function changeSpeed(ele,type,target,time) {addSpeed(ele,type,target/2,time/2,descSpeed,[ele,type,target,time/2])}
这里的回调函数的传参也可以使用 对象的方法 {fn:callback,options:[ele,type,target,time/2]]}
往复运动
定义:往复运动相当于加减速运动的升级版。元素先加速后减速,当减速到0时,元素并不停止,而是做反向的先加速后减速运动,如此反复
之前做加减速运动没有单独写一个,现在又得写,还需要再元素到达终点的时候改变他的目标值刷新值,重新设置下定时器,不行,于是又想个办法偷个懒
/** 往复运动* @param ele* @param type* @param target* @param time*/function returnSpeed(ele,type,target,time) {// flag true为正向运动 false反向let flag = false;// 获取初始值let cur = parseFloat(getStyle(ele,type));let change = function(){flag = !flag;if(flag){// 正向运动,先加速再减速,最后再调用函数本身addSpeed(ele,type,target/2,time/2,descSpeed,[ele,type,target,time/2,change])}else {addSpeed(ele,type,target/2,time/2,descSpeed,[ele,type,cur,time/2,change])}};change()}
封装一下
function animation(obj) {if(obj.el === void 0){throw new Error ('You have to enter element')}// 适配器let newObj = {el: obj.el ,type : obj.type || 'default',time : obj.time || 1000,style : obj.style || {},callback: obj.callback};// 执行状态let typeState = {getStyle:(obj,name)=>{if(window.getComputedStyle){// //正常浏览器的方式,具有getComputedStyle()方法return getComputedStyle(obj,null)[name]}else{//IE8的方式,没有getComputedStyle()方法return obj.currentStyle[name];}},default : function(ele,type,target,time,callback){// 当前的属性值, 已经变化的值let cur = parseFloat(typeState.getStyle(ele,type)),move =cur;if(target === cur) return ;// 这里设置每秒钟刷新60次,每次刷新时间为16.7秒let step = Math.floor((target-cur)/time*16.7*10000)/10000;// 设置动画定时器let timer = setInterval(()=>{move += step;// 设置清除条件if(target>cur ? move>=target : move<=target){ele.style[type] = target + 'px';clearInterval(timer);console.log(callback);callback && callback.fn && callback.fn(callback.arg)}else{ele.style[type] = move + 'px';}},16.6)},east_in : function () {},east_out:function () {},east_in_ou:function () {},toggle:function () {}};// 样式状态let style = {};// 遍历所有的并执行Object.keys(newObj.style).forEach(function (k){typeState[newObj.type](newObj.el ,k,newObj.style[k] ,newObj.time,newObj.callback)});}// testlet test = document.getElementsByClassName('test')[0];animation({el: test,style:{width:500,height:600,left: 200},callback: {fn:function () {animation({el: test,style:{width:100,height:100,},})},arg:[]}});
这个封装是真的烂,待我再学一阵子再来完成它吧…….
