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完成后调用此函数,当计数器满调用callback
102                 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.unit[unitName];
175                     }
176                 }
177             }
178
179             if(configVars.debug) {
180                 debug = configVars.debug || false;
181             }
182         }
183     };
184
185     init.each = function(array, handler){
186         if(typeof handler === "function"){
187             for(var i = 0, length = array.length; i < length; i++){
188                 handler.call(array[i], i, array);
189             }
190         }
191     };
192
193     /*
194     * 赠送几个单位存取函数(暂时实现行内样式读取,单位px -。-)
195     */
196     init.each("width, height, left, right, top, bottom, marginLeft, marginTop".split(/\s*,\s*/), function(index, array){
197         var attributeName = this;
198         unit[attributeName] = {
199             getter: function(element){
200                 return parseInt((element.nodeType && element.style[attributeName] || element)["match"](/\d+/)[0]);
201             },
202             setter: function(element, value){
203                 element.style[attributeName] = value + "px";
204             }
205         }
206     });
207
208     return init;
209
210 }();

测试如下(需引入Animation):

详见:http://runjs.cn/code/lgrfeykn

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <title></title>
 5     <script type="text/javascript">
 6
 7
 8         function init(){
 9
10             Animation.config({            //可以在这里设置或扩充功能
11                 debug: true,
12                 fps: 60,
13                 fx: {
14                     easeOutElastic: function(t,b,c,d){
15                         var s=1.70158;var p=0;var a=c;
16                         if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
17                         if (a < Math.abs(c)) { a=c; var s=p/4; }
18                         else var s = p/(2*Math.PI) * Math.asin (c/a);
19                         return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
20                     }
21                 },
22                 unit: {
23                     backgroundColor: {        //
24                         getter: function(element){
25                             var backgroundColor = (element.nodeType && element.style.backgroundColor || element)["match"](/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
26                             return [parseInt(backgroundColor[1]), parseInt(backgroundColor[2]), parseInt(backgroundColor[3])];
27                         },
28                         setter: function(element, value){
29                             element.style.backgroundColor = "rgb(" + parseInt(value[0]) + ", " + parseInt(value[1]) + ", " + parseInt(value[2]) + ")";
30                         }
31                     }
32                 }
33             });
34
35
36
37             var animation = new Animation(test, {width: {value: "100px"}, height: {value: "100px"}, marginLeft: {value: "50px"}, marginTop: {value: "50px"}, backgroundColor: {value: "rgb(203,215,255)"}}, 1000, "easeOutElastic", function(){
38                     console.log("complete");
39             });
40
41         }
42
43     </script>
44 </head>
45 <body onload="init();">
46
47     <div id="test" style="width: 200px; height: 200px; background: rgb(255,104,228);margin-left: 0; margin-top: 0"></div>
48
49 </body>
50 </html>
时间: 2024-12-16 02:44:25

JavaScript实现动画插件的相关文章

八款强大的jQuery图片滑块动画插件

jQuery是一款相当轻巧的JavaScript框架,目前几乎每一个WEB项目都在使用jQuery,因为jQuery插件实在太丰富,尤其是 一些图片滑块插件和jQuery焦点图插件,更是多如牛毛,很多初学者只需稍微修改就可以使用.本文精选了8款比较强大的jQuery图片滑块动画插件, 希望对读者有所帮助. 1.jQuery可自动播放动画的焦点图插件 这是一款基于jQuery的可自动播放动画的焦点图插件,各种元素悬浮在图片上,并且可以随意组合播放动画,非常适合一些产品的展示和宣传. 在线演示源码下

HTML5 SVG图形轮廓线条绘制动画插件-vivus

Vivus是一款可以执行SVG路径动画的轻量级Javascript库.Vivus可以绘制SVG图形的外观.通过该svg路径动画插件,你可以使用同步或异步的方式来执行SVG图像路径的动画.Vivus提供各种不同的动画参数来让你定制你自己的SVG路径动画.类似的动画效果还有:html5 svg线条动态绘制文字轮廓边框动画 和 html5 svg线条动态绘制iphone边框动画效果. 在线演示:http://www.htmleaf.com/Demo/201501261280.html 下载地址:htt

#JS 数字滚动累加动画插件

数字滚动累加动画插件  NumScroll 1.使用前先引入jquery2.前端学习群:739574382 下载地址 https://github.com/chaorenzeng/jquery.numscroll.js.git 快速使用 1.引入jquery和jquery.numscroll.js <script src='http://code.jquery.com/jquery-2.1.1.min.js' type='text/javascript'></script> <

反混淆、反编译unity3d动画插件DFTweenLite得到源码

我为什么要得到这个源码,因为有洁癖! 对于Itween性能差,LeanTween 和 HOTween的 免费, 个人还是比较喜欢 Daikon Forge 出品的东西, 因为有我个人很欣赏的 DFGUI 产品.确实很好用!!! DFTweenLite 是免费的, DFTweenPro是收费的. 功能上基本上不差啥.  免费没有源代码, Dll 还进行了混淆. 没有办法,上网找解决办法. 还真有: de4dot主要用来反混淆 用法:dll文件直接拖到de4dot.exe会生成另外一个  文件名+c

[转]7个高性能JavaScript代码高亮插件

对于喜欢写技术博客的同学来说,一定对代码高亮组件非常熟悉.一款优秀的JavaScript代码高亮插件,将会帮助你渲染任何一种编程语言,包括一些关键字的着色,以及每行代码的缩进等.今天我们要来分享一些高性能的JavaScript代码高亮插件,这些JavaScript代码高亮插件将非常有效地帮你实现在网页上的代码编辑和展示. 1.SyntaxHighlighter – 最优秀的JavaScript代码高亮插件 SyntaxHighlighter 是一款完全基于JavaScript的代码高亮插件,Sy

Unity CCTween UGUI 动画插件

在这简单的介绍一个 CCTween 动画插件的使用 因为GIF 制作软件不太好(网上随便下载的)所以导致效果不太好,有时间我重新制作一下 这是一下简单的效果 下面介绍怎么使用 首先 先下载 CCTween  导入工程 点击下载 Unity5.0的包 如果使用的Unity 版本不够5.0的 就点击下边的连接 下载源码 点击下载 源码 GitGub地址 欢迎大神来更新 好了下面正式开始介绍使用了 #------------ 这是使用的一些变量 以及类型 -----------------------

10 款强大的JavaScript图表图形插件推荐

转自:http://www.iteye.com/news/24535 网上有很多用于绘制图表图形的免费JavaScript插件和图表库,这类插件大量出现的原因,一是人们不再依赖于Flash,二是浏览器和精密的计算使呈现实时数据变得容易,而且各种向量绘图技术像VML.SVG和Canvas的发展也使之成为可能. 本文推荐10款强大的绘制图表图形的JavaScript插件.其中一些插件需要主流浏览器的支持,而另外一些经过整合后,也能在不同的平台和老版本的浏览器上工作.有些工具是独立的框架,大部分支持常

反混淆、反编译unity3d动画插件DFTweenLite得到源代码

出处:http://blog.csdn.net/u010019717 author:孙广东      时间:2015.3.17   23:00 我为什么要得到这个源代码.由于有洁癖! 对于Itween性能差,LeanTween 和 HOTween的 免费. 个人还是比較喜欢 Daikon Forge 出品的东西. 由于有我个人非常赞赏的 DFGUI 产品.确实非常好用! ! . DFTweenLite 是免费的. DFTweenPro是收费的. 功能上基本上不差啥. 免费没有源代码, Dll 还

7个高性能JavaScript代码高亮插件

本文由码农网 – 小峰原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 对于喜欢写技术博客的同学来说,一定对代码高亮组件非常熟悉.一款优秀的JavaScript代码高亮插件,将会帮助你渲染任何一种编程语言,包括一些关键字的着色,以及每行代码的缩进等.今天我们要来分享一些高性能的JavaScript代码高亮插件,这些JavaScript代码高亮插件将非常有效地帮你实现在网页上的代码编辑和展示. 1.SyntaxHighlighter – 最优秀的JavaScript代码高亮插件 Syn