zoukankan      html  css  js  c++  java
  • 原生JavaScript运动功能系列(五):定时定点运动

    这篇博客剖析一个问题,就是怎么实现将元素指定时间运动到目标位置?前面的博客都是在处理运动行为,没有对运动时间做任何限定,只是因为清晰的分析运动行为和实现原理,要想一个动画函数具备健全的功能,并且可以随意使用,通过参数设定动画执行时间是非常有必要的一个设计。

    在前面任意一种运动函数内,实质上只有一个变量,这个变量就是运动距离(缓冲运动中添加了因数),当运动距离变大时,运动时间就会变长,动画停止条件是由坐标来决定的。当指定运动时间时,运动速度就会同时受到运动距离和运动时间的限制,动画停止条件也是由时间来控制。

    定时定点运动示例代码:

     1 //css
     2 div{
     3     width:100px;
     4     height:100px;
     5     background:red;
     6     position: absolute;
     7     left: 0px;
     8     opacity: 1;
     9 }
    10 #top{
    11     top: 100px;
    12 }
    13 #bottom{
    14     top: 300px;    
    15 }
    16 //html
    17 <div id="top"></div>
    18 <div id="bottom"></div>
    19 //js
    20 var oDivArray = document.getElementsByTagName('div');
    21 var timer = null;
    22 var targetObj = {
    23     width:400,
    24     height:400,
    25     opacity:50,
    26     left:300,
    27     top:200
    28 }
    29 oDivArray[0].onclick = function(){
    30     startMove(this,targetObj,3000,function(){
    31         startMove(oDivArray[1],targetObj,3000);
    32     });
    33 }
    34 function getStyle(obj,attr){
    35     if(window.getComputedStyle){
    36         return window.getComputedStyle(obj,false)[attr];
    37     }else{
    38         return obj.currentStyle[attr];
    39     }
    40 }
    41 //运动方法startMove的参数:
    42 //obj:运动物体;
    43 //json:目标位置,最终样式值(键值对的形式合成的目标位置对象)
    44 //speed:运动时间(指定运动的时间)
    45 //callback:回调函数
    46 function startMove(obj,json,speed,callback){
    47     //初始位置,移动距离,当前位置
    48     var initialPlace = {};
    49     //新的位置(每次运动的目标点)
    50     var nowPlace;
    51     //结束之前的定时器
    52     clearInterval(obj.timer);
    53     //获取当前时间戳
    54     var createTime = function(){
    55         return (+new Date);
    56     }
    57     //动画开始的时间戳
    58     var startTime = createTime();
    59     //初始位置对象
    60     for(var attr in json){
    61         if(attr == 'opacity'){
    62             initialPlace[attr] = Math.round(parseFloat(getStyle(obj,attr))*100);
    63         }else{
    64             initialPlace[attr] = parseInt(getStyle(obj,attr));
    65         }
    66     }
    67     //定时器
    68     obj.timer = setInterval(function(){
    69         //每次变化的时间
    70         //剩余时间 = Math.max(0,运动开始的时间 + 运动执行事件 - 当前时间) -- 当剩余时间为负数时,返回0
    71         var remaining = Math.max(0, startTime + speed - createTime());
    72         //剩余时间比 = 剩余时间 / 运动时间
    73         var temp = remaining / speed || 0;
    74         //当前时间比 = 1 - 剩余时间比 -- 即执行到某处时间节点
    75         var percent = 1 - temp;
    76         //循环运动到时间节点位置
    77         for(var  attr in json){
    78             nowPlace = (json[attr] - initialPlace[attr]) * percent + initialPlace[attr];
    79             if(attr == 'opacity'){
    80                 obj.style.opacity = nowPlace / 100;
    81             }else{
    82                 obj.style[attr] = nowPlace + 'px';
    83             }
    84         }
    85         //当前时间与运动时间比为1:1时,说明到达运动终点了,结束定时器,并判断是否有回调函数
    86         if(percent == 1){
    87             clearInterval(obj.timer);
    88             typeof callback == 'function' ? callback() : '';
    89         }
    90     },30);
    91 }

    示例还是基于链式运动的示例进行修改过来了,但是运动函数已经发生了质的变化。因为代码我都有些注释,就不详细解析了。提一个相对比较重要的点,这篇博客相对于前面的博客(运动系列),这篇博客的动画行为主要是受限定的运动时间来控制的,单次运动距离由运动实际运动时间比总(参数设置的时间)运动时间,来决定一次运动距离。通过时间比的方式来控制运动速度,和决定什么时候停止动画。

    然后还有一个参数easing没有实现,如果没有这个参数,运动行为就都是匀速执行了,请看下面的改进方式:

     1 //在js全局定义运动行为对象
     2 var easingObj = {
     3     linear: function( p ) {
     4         return p;
     5     },
     6     swing: function( p ) {
     7         return 0.5 - Math.cos( p*Math.PI ) / 2;
     8     },
     9     background:function(k) {
    10         if (k < (1 / 2.75)) {
    11             return 7.5625 * k * k; 
    12         } else if (k < (2 / 2.75)) {
    13             return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; 
    14         } else if (k < (2.5 / 2.75)) {
    15             return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; 
    16         } else { 
    17             return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; 
    18         } 
    19     }
    20 }
    21 //然后在startMove运动方法的形参上添加参数easing;参数位置:第四个参数(索引3)
    22 //然后设置默认运动行为或接收设置的运动行为
    23 //对象行为 -- 默认:swing(匀速)
    24 if(!easing){
    25     easing = easingObj.swing;
    26 }else{
    27     easing = easingObj[easing];
    28 }
    29 //将运动节点位置那行代码修改
    30 //实质是将percend用行为函数包裹起来
    31 nowPlace = (json[attr] - initialPlace[attr]) * easing(percent) + initialPlace[attr];

    最后调用代码:

    oDivArray[0].onclick = function(){
        startMove(this,targetObj,3000,"",function(){
            startMove(oDivArray[1],targetObj,3000,"background");
        });
    }

    这个方法就完全模仿了jQuery的动画函数animate(),后期我会将这个方法封装,并且会提供缓动函数(easings库)插件接口,缓动函数库手册地址:https://easings.net/zh-cn。运动行为库相对来说还是有些局限性的,所以接下来我会在前面的示例基础上手写弹力效果和撞击效果。一步一步来吧。

    相关技术博客参考:

  • 相关阅读:
    VS2010-MFC(对话框:模态对话框及其弹出过程)
    VS2010-MFC(对话框:设置对话框控件的Tab顺序)
    VS2010-MFC(对话框:为控件添加消息处理函数)
    VS2010-MFC(对话框:创建对话框类和添加控件变量)
    manacher算法模板
    [洛谷P1017] 进制转换
    [洛谷P1126] 机器人搬重物
    [洛谷P1032] 字串变换
    [POI2005]SAM-Toy Cars
    [洛谷P1528] 切蛋糕
  • 原文地址:https://www.cnblogs.com/ZheOneAndOnly/p/10407696.html
Copyright © 2011-2022 走看看