首页 > 网站 > WEB开发 > 正文

JavaScript实现动画插件

2024-04-27 14:19:35
字体:
来源:转载
供稿:网友

javaScript实现动画插件

在这之前,大家应该了解了缓动函数(Easing Functions)的概念:

动画的每一帧需要计算一次元素样式,如果样式改变则需要重绘屏幕。细一点讲,当我们每调用一次计时器函数,需要通过向缓动函数传入一些动画上下文变量,从而获取到元素的某个样式在当前帧合理的值。

我所了解的缓动函数实现方式有两种,一种是tbcd方式(Robert Penner's Easing Functons)

function(t,b,c,d){  return c*t/d + b;}

t: timestamp 以毫秒(ms)为单位,指从第一帧到当前帧所经历的时间b: beginning position,变量初始值c: change 变量改变量(即在整个动画过程中,变量将从 b 变到 b + c)d: duration 动画时间

另一种是mootools的单参数方式,由于我没了解过,这里就不说了,这里主要说一下第一种方式。

整个动画模块为Animation,其接受多个参数(元素, 动画样式, 持续时间[, 缓动函数名][, 回调函数]),是一个构造函数,调用方式为:

var animation = new Animation(test, {width: {value: "500px"}, 500, "sin", function(){  console.log("complete");});animation.stop();

其中,每个样式属性可单独指定持续时间与缓动函数名,但回调函数必须等到所有动画结束才调用。

Animaion模块定义如下:

  1 var Animation = function(){  2   3     var debug = false,          //如果debug,遇到异常将抛出  4     unit = {},               //样式存取函数,详见下方each函数  5     fx = {                   //缓动函数  6         linear: function(currentTime, initialDistance, totalDistance, duration){    //自带一个线性缓动函数  7             return initialDistance + (currentTime / duration * totalDistance);  8         }  9     }, 10     getTime = function(){                                                       //获取当前时间(ms或更精确) 11         return window.performance.now && performance.now() || new Date().getTime(); 12     }, 13     executorCanceler = window.cancelAnimationFrame,    //取消帧函数 14     executor = window.requestAnimationFrame                            //帧执行函数 15             || window.webkitRequestAnimationFrame 16             || window.msRequestAnimationFrame 17             || window.mozRequestAnimationFrame 18             || window.oRequestAnimationFrame 19             || function(){ 20             var callbacks = []; 21  22             !function frame(){ 23                 var oldTime = getTime(), 24                     tmp = callbacks; 25  26                 callbacks = []; 27  28                 for(var i = 0, length = tmp.length; i < length; i++){ 29                     tmp[i].callback(oldTime); 30                 } 31  32                 var currentTime = getTime(), 33                     delayTime = Math.max(16.66 - currentTime + oldTime, 0); 34  35                 setTimeout(frame, delayTime); 36             }(); 37  38             executorCanceler = function(id){ 39                 for(var i = 0, length = callbacks.length; i < length; i++){ 40                     if(callbacks[i].id === id) callbacks.splice(i, 1); 41                 } 42             } 43  44             return function(callback){ 45                 var context = {callback: callback, id: Math.random()}; 46                 callbacks.push(context); 47                 return context.id; 48             } 49     }(), 50     /* 51      * 为每个属性运行此函数,类似于启动一个线程(虽然不是真正的线程) 52      */ 53     animate = function(element, attribute, distances, duration, timingFunction, completeCallback){ 54         var oldTime = getTime(), 55                 animationPassedTime = 0, 56                 executorReference = executor(function anonymous(currentTimeStamp){ 57                     animationPassedTime = currentTimeStamp - oldTime; 58  59                     var computedValues = [];        //computedValues为缓动函数计算值,可能返回数值或者数组(按动画属性不同,比如rgb) 60  61                     if(animationPassedTime >= duration){ 62                         if(distances.length > 1){ 63                             for(var j = 0, length = distances.length; j < length; j++){ 64                                 computedValues.push(distances[j][0] + distances[j][1]); 65                             } 66                         } else { 67                             computedValues = distances[0][0] + distances[0][1]; 68                         } 69  70                         stop(); 71                     } else { 72                         if(distances.length > 1){ 73                             for(var i = 0, length = distances.length; i < length; i++){ 74                                 computedValues.push(fx[timingFunction](animationPassedTime, distances[i][0], distances[i][1], duration)); 75                             } 76                         } else { 77                             computedValues = fx[timingFunction](animationPassedTime, distances[0][0], distances[0][1], duration); 78                         } 79  80                         animationPassedTime = getTime() - oldTime; 81                         executorReference = executor(anonymous); 82                     } 83                     unit[attribute].setter(element, computedValues); 84                 }, Math.random()), 85                 completed = false, 86                 stop = function(){ 87                     executorCanceler(executorReference); 88                     completeCallback();      //执行回调函数 89                 }; 90  91         return { 92             stop: stop 93         } 94     }, 95     /* 96     * Animation 引用的函数,此函数返回一个包含所有动画属性的控制对象(如停止操作),因此可以采取函数调用或者new的方式创建一个动画对象 97     */ 98     init = function(element, animationVars, duration, timingFunction, callback){ 99 100         var animateQueue = {}, animationCount = 0, animationCompletedCount = 0, completeCallback = function(){101             return function(){      //每个animate完成后调用此函数,当计数器满调用callback102                 animationCompletedCount ++;103 104                 if(animationCount === animationCompletedCount){105                     typeof timingFunction === "function" ? timingFunction() : callback && callback();106                 }107             }108         }();109 110         if(!element.nodeType){111             if(debug)112                 throw "an htmlElement is required";113             return;114         }115 116         for(var attribute in animationVars){117             if(!(attribute in unit)){118                 if(debug){119                     throw "no attribute handler";120                 }121 122                 return;123             }124 125             try {126                 var initialDistance = unit[attribute].getter(element),127                         finalDistance = unit[attribute].getter(animationVars[attribute].value || animationVars[attribute]),128                         distances = [];129 130                 if(typeof initialDistance === "number"){131                     distances.push([initialDistance, finalDistance - initialDistance]);132                 } else {133                     for(var i = 0, length = initialDistance.length; i < length; i++){134                         distances.push([initialDistance[i], finalDistance[i] - initialDistance[i]]);135                     }136                 }137                 /*138                  * 可以为每个属性指定缓动函数与时间139                  */140                 animateQueue[attribute] = animate(element, attribute, distances, animationVars[attribute].duration || duration, animationVars[attribute].timingFunction || (typeof timingFunction === "string" ? timingFunction : false) || "linear", completeCallback);141             } catch (e) {142                 if(debug) {143                     throw "an error occurred: " + e.stack;144                 }145 146                 return;147             }148 149             animationCount ++;150         }151 152         animateQueue.stop = function() {153             for(var attribute in animateQueue) {154                 animateQueue[attribute].stop && animateQueue[attribute].stop();155             }156         }157 158         return animateQueue;159     };160 161     init.config = function(configVars){162         if(configVars){163             if(configVars.fx) {164                 for(var fxName in configVars.fx){165                     if(typeof configVars.fx[fxName] === "function"){166                         fx[fxName] = configVars.fx[fxName];167                     }168                 }169             }170 171             if(configVars.unit) {172                 for(var unitName in configVars.unit){173                     if(typeof configVars.unit[unitName] === "object"){174                         unit[unitName] = configVars.uni
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表