匀速运动

 在原生javascript中实现运动的主要工具是定时器,通过设置固定的间隔时间,使元素在确定的间隔时间内实现距离的变化。而运动变化的主要表现形式是距离的变化
 例如,定时器频率可如下列代码所示,设置为30ms。每30ms对s的值进行更新,使其增加一个步长step的距离,来实现视觉上的元素运动效果

  1. setInterval(function(){
  2. s = s + step
  3. },30)

这里我们先使用之前一节的样式获取函数

  1. /**
  2. * 获取对象的样式
  3. * @param obj 元素对象
  4. * @param type 样式名称
  5. * @returns {*} 样式属性
  6. */
  7. function getStyle(ele,type){
  8. if(window.getCompuutedStyle){
  9. // 正常浏览器
  10. return getComputedStyle(obj,null)[type]
  11. }else{
  12. //IE 8
  13. return obj.currentStyle[type]
  14. }
  15. }

函数封装

  1. /**
  2. * 单个对象单个匀速运动
  3. * @param ele 对象
  4. * @param type 变化属性,-left-width
  5. * @param target 目标值
  6. * @param time 时间
  7. */
  8. function uniformSpeed(ele,type,target,time){
  9. // 当前的属性值, 已经移动的值
  10. let cur = parseFloat(getStyle(ele,type)),
  11. move =cur;
  12. if(target === cur) return ;
  13. // 这里设置每秒钟刷新60次,每次刷新时间为16.7秒
  14. let step = Math.floor((target-cur)/time*16.7*10000)/10000;
  15. // 设置动画定时器
  16. let timer = setInterval(()=>{
  17. move += step;
  18. // 设置清除条件
  19. if(target>cur ? move>=target : move<=target){
  20. ele.style[type] = target + 'px';
  21. clearInterval(timer)
  22. }else{
  23. ele.style[type] = move + 'px';
  24. }
  25. },16.6)
  26. }

缺点:

这个虽然能实现简单的动画效果,但是还是有很多的缺陷,比如不能实现多个属性同时变换,不能对opacity,颜色等样式进行变换;

加速运动


说到加速运动,必须要提到一个物理名词——加速度
加速度动画函数 - 图1
实验用推论:Δs= aT^2 {Δs为连续相邻相等时间(T)内位移之差}

我们还是用之前的那个公式

  1. setInterval(function(){
  2. s = s + step
  3. },30)

依靠上面的推断第n次20ms后与第一次的位移差的关系是
step = n * Δs

这里我们用20ms方便计算

  1. /**
  2. * 单个对象单个加速运动
  3. * @param ele 对象
  4. * @param type 变化属性,-left-width
  5. * @param target 目标值
  6. * @param time 时间
  7. */
  8. function addSpeed(ele,type,target,time){
  9. // 声明变量
  10. let cur = parseFloat(getStyle(ele,type)),
  11. move = 0,
  12. a = (target-cur)/time/time*2, //加速度
  13. step = a*20*20, // 距离差 Δs= aT^2
  14. move = cur, // 移动的距离
  15. times = 0; // 循环多少次
  16. if(target === cur) return ;
  17. // 设置动画定时器
  18. let timer = setInterval(()=>{
  19. times ++;
  20. move += times*step -1/2*step;
  21. // 设置清除条件
  22. if(target>cur ? move>=target : move<=target){
  23. ele.style[type] = target + 'px';
  24. clearInterval(timer)
  25. }else{
  26. ele.style[type] = move + 'px';
  27. }
  28. },20)
  29. }

减速运动

对于减速运动来说我们可以设置他的初始速度稍大,在最后一步大于目标值时设置速度为0,并清除定时器

  1. function descSpeed(ele,type,target,time) {
  2. let cur = parseFloat(getStyle(ele,type)),
  3. a = Math.floor((target-cur)/time/time*2*100000)/100000,
  4. step = a*20*20,
  5. move = cur,
  6. times = Math.floor((time/20+3)*100000)/100000; // 如果想要初始速度大一些可以设置+n
  7. if(target === cur) return ;
  8. // 设置动画定时器
  9. let timer = setInterval(()=>{
  10. move += times*step -1/2*step;
  11. times --;
  12. // 设置清除条件
  13. if(target>cur ? move>=target : move<=target){
  14. ele.style[type] = target + 'px';
  15. clearInterval(timer)
  16. }else{
  17. ele.style[type] = move + 'px';
  18. }
  19. },20)
  20. }

缓存运动

缓冲运动就是当运动到目的地时速度干好是0,那么可以设置上面的n为0或者1;

加减速运动

半段运动时,做加速运动。到达指定点时,做减速运动,最终到达终点停止,
这里我就偷懒了一下,直接前半段调用加速运动,后半段调用减速运动;

  1. // 加速运动——增加了callback函数和callbak的参数options
  2. // 如果没有options则调用自身的函数的前几个参数,并且在清除定时器时调用回调函数
  3. // function addSpeed(ele,type,target,time,callback,options)
  4. // 减速运动
  5. // function descSpeed(ele,type,target,time,callback,options)
  6. // 加减速运动
  7. function changeSpeed(ele,type,target,time) {
  8. addSpeed(ele,type,target/2,time/2,descSpeed,[ele,type,target,time/2])
  9. }

这里的回调函数的传参也可以使用 对象的方法 {fn:callback,options:[ele,type,target,time/2]]}

往复运动

定义:往复运动相当于加减速运动的升级版。元素先加速后减速,当减速到0时,元素并不停止,而是做反向的先加速后减速运动,如此反复
之前做加减速运动没有单独写一个,现在又得写,还需要再元素到达终点的时候改变他的目标值刷新值,重新设置下定时器,不行,于是又想个办法偷个懒

  1. /** 往复运动
  2. * @param ele
  3. * @param type
  4. * @param target
  5. * @param time
  6. */
  7. function returnSpeed(ele,type,target,time) {
  8. // flag true为正向运动 false反向
  9. let flag = false;
  10. // 获取初始值
  11. let cur = parseFloat(getStyle(ele,type));
  12. let change = function(){
  13. flag = !flag;
  14. if(flag){
  15. // 正向运动,先加速再减速,最后再调用函数本身
  16. addSpeed(ele,type,target/2,time/2,descSpeed,[ele,type,target,time/2,change])
  17. }else {
  18. addSpeed(ele,type,target/2,time/2,descSpeed,[ele,type,cur,time/2,change])
  19. }
  20. };
  21. change()
  22. }

封装一下

  1. function animation(obj) {
  2. if(obj.el === void 0){
  3. throw new Error ('You have to enter element')
  4. }
  5. // 适配器
  6. let newObj = {
  7. el: obj.el ,
  8. type : obj.type || 'default',
  9. time : obj.time || 1000,
  10. style : obj.style || {},
  11. callback: obj.callback
  12. };
  13. // 执行状态
  14. let typeState = {
  15. getStyle:(obj,name)=>{
  16. if(window.getComputedStyle){
  17. // //正常浏览器的方式,具有getComputedStyle()方法
  18. return getComputedStyle(obj,null)[name]
  19. }else{
  20. //IE8的方式,没有getComputedStyle()方法
  21. return obj.currentStyle[name];
  22. }
  23. },
  24. default : function(ele,type,target,time,callback){
  25. // 当前的属性值, 已经变化的值
  26. let cur = parseFloat(typeState.getStyle(ele,type)),
  27. move =cur;
  28. if(target === cur) return ;
  29. // 这里设置每秒钟刷新60次,每次刷新时间为16.7秒
  30. let step = Math.floor((target-cur)/time*16.7*10000)/10000;
  31. // 设置动画定时器
  32. let timer = setInterval(()=>{
  33. move += step;
  34. // 设置清除条件
  35. if(target>cur ? move>=target : move<=target){
  36. ele.style[type] = target + 'px';
  37. clearInterval(timer);
  38. console.log(callback);
  39. callback && callback.fn && callback.fn(callback.arg)
  40. }else{
  41. ele.style[type] = move + 'px';
  42. }
  43. },16.6)
  44. },
  45. east_in : function () {
  46. },
  47. east_out:function () {
  48. },
  49. east_in_ou:function () {
  50. },
  51. toggle:function () {
  52. }
  53. };
  54. // 样式状态
  55. let style = {
  56. };
  57. // 遍历所有的并执行
  58. Object.keys(newObj.style).forEach(function (k){
  59. typeState[newObj.type](newObj.el ,k,newObj.style[k] ,newObj.time,newObj.callback)
  60. });
  61. }
  62. // test
  63. let test = document.getElementsByClassName('test')[0];
  64. animation({
  65. el: test,
  66. style:{
  67. width:500,
  68. height:600,
  69. left: 200
  70. },
  71. callback: {
  72. fn:function () {
  73. animation({
  74. el: test,
  75. style:{
  76. width:100,
  77. height:100,
  78. },
  79. })
  80. },
  81. arg:[]
  82. }
  83. });

这个封装是真的烂,待我再学一阵子再来完成它吧…….