MooTools 1.4 源码分析 - Fx

Mootools1.4 - Fx源码分析,如果理解有误欢迎指正:

 

  1. /* 
  2. --- 
  3.  
  4. name: Fx 
  5.  
  6. description: Contains the basic animation logic to be extended by all other Fx Classes. 
  7.  
  8. license: MIT-style license. 
  9.  
  10. requires: [Chain, Events, Options] 
  11.  
  12. provides: Fx 
  13.  
  14. 源码分析: 苦苦的苦瓜(http://hmking.blog.51cto.com) 
  15. ... 
  16. */ 
  17.  
  18. (function () { 
  19.  
  20.     /** 
  21.     * @Fx: 本类一般不独立使用,它用来提供作为Fx系的类的基础功能类.所有其他的Fx系列的类都继承本类. 
  22.     **/ 
  23.     var Fx = this.Fx = new Class({ 
  24.  
  25.         Implements: [Chain, Events, Options], 
  26.  
  27.         // #region - constructor - 
  28.  
  29.         /** 
  30.         * @Events: 
  31.         * @event start - (function) 特效开始执行时触发 
  32.         * @event cancel - (function) 手动停止特效执行时触发 
  33.         * @event complete - (function) 特效执行完成后触发 
  34.         * @event chainComplete - (function) 当使用link可选项为'chain'时, 该事件在特效链执行完后触发 
  35.         * @event stop - (function) 特效执行完成前,执行stop方法时触发 
  36.         **/ 
  37.  
  38.         /** 
  39.         * @Optoins:  
  40.         * @option fps - (number: 默认为 60) 动画特效的秒帧数 
  41.         * @option unit - (string: 默认为 false) 计量单位(如: 'px', 'em', 或 '%'). 
  42.         * @option duration - (number: 默认为 500) 可以让你定义这个动画的持续时间。持续时间和速度是不一样的,因此如果你想让一个对象在一秒内移动100个像素, 
  43.         *       那么它将比一个每秒移动1000个像素的对象要慢。你可以输入一个数字(以毫秒为单位). 也可使用以下预定义字符串: 
  44.         *       'short' - 250ms 
  45.         *       'normal' - 500ms 
  46.         *       'long' - 1000ms 
  47.         * @option frames - (number) 设定动画特效执行的总帧数,默认为null自动匹配 
  48.         * @option frameSkip - (boolean: 默认为true) 设定动画特效当一帧执行的时间大于每帧之间的时间间隔,是否跳过这段时间所要执行的帧 
  49.         * @option link - (string: 默认为 ignore) 可为: 'ignore', 'cancel' 或 'chain' 
  50.         *       'ignore' - 当特效正在执行之中时,再次调用特效开始的方法将被忽略(和可选项'wait'为true时同义) 
  51.         *       'cancel' - 当特效正在执行之中时,再次调用特效开始的方法将立即取消当前执行的特效,开始执行新的特效 
  52.         *       'chain' - 当特效正在执行之中时,再次调用特效开始的方法将会把新的特效链接在当前执行的特效之后,依次执行各个特效 
  53.         * @option transition - (function: 默认为 Fx.Transitions.Sine.easeInOut) 特效的变换方程, 详见Fx.Transitions. 也可以使用如下格式的字符串: 
  54.         *       transition[:in][:out] - 例如: 'linear', 'quad:in', 'back:in', 'bounce:out', 'elastic:out', 'sine:in:out' 
  55.         **/ 
  56.         options: { 
  57.             /* 
  58.             onStart: nil, 
  59.             onCancel: nil, 
  60.             onComplete: nil, 
  61.             */ 
  62.             fps: 60, 
  63.             unit: false
  64.             duration: 500, 
  65.             frames: null
  66.             frameSkip: true
  67.             link: 'ignore' 
  68.         }, 
  69.  
  70.         initialize: function (options) { 
  71.             this.subject = this.subject || this
  72.             this.setOptions(options); 
  73.         }, 
  74.  
  75.         // #endregion 
  76.  
  77.         /** 
  78.         * @method: getTransition 
  79.         * @returns: (function) - 特效的变换方程 
  80.         * @description: 取得动画特效所要执行的特效方程 
  81.         **/ 
  82.         getTransition: function () { 
  83.             return function (p) { 
  84.                 return -(Math.cos(Math.PI * p) - 1) / 2; 
  85.             }; 
  86.         }, 
  87.  
  88.         /** 
  89.         * @method: step 
  90.         * @param now - (mixed) 特效值 
  91.         * @returns: (function) - 特效的变换方程 
  92.         * @description: 动画特效每一步执行的操作 
  93.         **/ 
  94.         step: function (now) { 
  95.             if (this.options.frameSkip) { 
  96.                 // 先取得当前时间减去上一帧执行时的时间,得到两帧之间的时间间隔,计算这段时间内按正常的帧间隔时间能执行的帧的数量 
  97.                 var diff = (this.time != null) ? (now - this.time) : 0, 
  98.                         frames = diff / this.frameInterval; 
  99.                 // 存储当前帧执行时的时间 
  100.                 this.time = now; 
  101.                 // 执行的帧数累加 
  102.                 this.frame += frames; 
  103.             } else { 
  104.                 this.frame++; 
  105.             } 
  106.  
  107.             // 判断当前帧是否为动画特效的最后一帧 
  108.             if (this.frame < this.frames) { 
  109.                 // 通过特效方程计算动画特效运行当前帧所要变化的比例因子 
  110.                 var delta = this.transition(this.frame / this.frames); 
  111.                 this.set(this.compute(this.from, this.to, delta)); 
  112.             } else { 
  113.                 // 动画特效执行完毕 
  114.                 this.frame = this.frames; 
  115.                 this.set(this.compute(this.from, this.to, 1)); 
  116.                 this.stop(); 
  117.             } 
  118.         }, 
  119.  
  120.         /** 
  121.         * @method: set 
  122.         * @param value - (mixed) 特效值 
  123.         * @description: 用于设置特效值.该方法在特效变换过程中每个'步进'都会调用; 也可以手工调用,留作派生类实现 
  124.         **/ 
  125.         set: function (now) { 
  126.             return now; 
  127.         }, 
  128.  
  129.         /** 
  130.         * @method: compute 
  131.         * @param from - (mixed) 特效的起始值 
  132.         * @param to - (mixed) 特效的结束值 
  133.         * @param delta - (mixed) 特效变化所需要的比例因子 
  134.         * @description: 根据初始值,结束值和比例因子求目标值 
  135.         **/ 
  136.         compute: function (from, to, delta) { 
  137.             return Fx.compute(from, to, delta); 
  138.         }, 
  139.  
  140.         /** 
  141.         * @method: check 
  142.         * @parameters - 与start方法参数一致 
  143.         * @returns: (boolean) - 如果start方法可以继续执行, 则返回 true ; 否则返回 false 
  144.         * @description: 判断当特效正在执行之中时,再次调用特效开始的方法(start)是否继续可以执行start方法 
  145.         **/ 
  146.         check: function () { 
  147.             // 如果特效没有运行,返回true 
  148.             if (!this.isRunning()) { return true; } 
  149.             switch (this.options.link) { 
  150.                 case 'cancel'// 不等待正在运行的特效,直接取消并重新开始 
  151.                     this.cancel(); 
  152.                     return true
  153.  
  154.                 case 'chain'// 等待当前特效运行结束后再继续运行新特效 
  155.                     this.chain(this.caller.pass(arguments, this)); 
  156.                     return false
  157.             } 
  158.             return false
  159.         }, 
  160.  
  161.         /** 
  162.         * @method: start 
  163.         * @param from - (mixed) 特效的起始值. 如果只给出一个参数,则本值将作为结束值 
  164.         * @param to - (mixed, 可选) 特效的结束值 
  165.         * @returns: (object) - 当前的Fx实例 
  166.         * @description: 开始执行特效变换(并触发'start'事件) 
  167.         **/ 
  168.         start: function (from, to) { 
  169.             // 检测start方法是否可以继续执行 
  170.             if (!this.check(from, to)) { return this; } 
  171.  
  172.             /** 
  173.             # 苦苦的苦瓜 
  174.             # 2011-09-25 
  175.             # 将用局部变量_options代替this.options 
  176.             **/ 
  177.             var _options = this.options; 
  178.  
  179.             this.from = from; 
  180.             this.to = to; 
  181.             this.frame = (_options.frameSkip) ? 0 : -1; 
  182.             this.time = null
  183.             // 取得特效执行的变换方程 
  184.             this.transition = this.getTransition(); 
  185.             var frames = _options.frames, 
  186.                     fps = _options.fps, 
  187.                     duration = _options.duration; 
  188.             // 可选参数duration既可以数字类型,也可以为字符串类型 
  189.             this.duration = Fx.Durations[duration] || duration.toInt(); 
  190.             // 取得动画特效每帧之间的时间间隔,毫秒为单位 
  191.             this.frameInterval = 1000 / fps; 
  192.             // 计算动画特效执行的总帧数 
  193.             this.frames = frames || Math.round(this.duration / this.frameInterval); 
  194.             // 触发'start'事件 
  195.             this.fireEvent('start'this.subject); 
  196.             pushInstance.call(this, fps); 
  197.             return this
  198.         }, 
  199.  
  200.         /** 
  201.         * @method: stop 
  202.         * @returns: (object) - 当前的Fx实例 
  203.         * @description: 停止一个特效的执行 
  204.         **/ 
  205.         stop: function () { 
  206.             if (this.isRunning()) { 
  207.                 this.time = null
  208.                 pullInstance.call(thisthis.options.fps); 
  209.                 if (this.frames == this.frame) { 
  210.                     this.fireEvent('complete'this.subject); 
  211.                     if (!this.callChain()) { 
  212.                         this.fireEvent('chainComplete'this.subject); 
  213.                     } 
  214.                 } else { 
  215.                     this.fireEvent('stop'this.subject); 
  216.                 } 
  217.             } 
  218.             return this
  219.         }, 
  220.  
  221.         /** 
  222.         * @method: cancel 
  223.         * @returns: (object) - 当前的Fx实例 
  224.         * @description: 取消一个特效的执行(并触发'cancel'事件) 
  225.         **/ 
  226.         cancel: function () { 
  227.             if (this.isRunning()) { 
  228.                 this.time = null
  229.                 pullInstance.call(thisthis.options.fps); 
  230.                 this.frame = this.frames; 
  231.                 this.fireEvent('cancel'this.subject).clearChain(); 
  232.             } 
  233.             return this
  234.         }, 
  235.  
  236.         /** 
  237.         * @method: pause 
  238.         * @returns: (object) - 当前的Fx实例 
  239.         * @description: 暂停当前执行的特效 
  240.         **/ 
  241.         pause: function () { 
  242.             if (this.isRunning()) { 
  243.                 this.time = null
  244.                 pullInstance.call(thisthis.options.fps); 
  245.             } 
  246.             return this
  247.         }, 
  248.  
  249.         /** 
  250.         * @method: pause 
  251.         * @return: (object) - 当前的Fx实例 
  252.         * @description: 恢复执行暂停中的特效 
  253.         * @remark: 只有对暂停中的特效执行本方法才有效果, 否则将忽略. 
  254.         **/ 
  255.         resume: function () { 
  256.             if ((this.frame < this.frames) && !this.isRunning()) { 
  257.                 pushInstance.call(thisthis.options.fps); 
  258.             } 
  259.             return this
  260.         }, 
  261.  
  262.         /** 
  263.         * @method: isRunning 
  264.         * @return: (boolean) 特效运行状态 
  265.         * @description: 检测特效是否正在运行 
  266.         **/ 
  267.         isRunning: function () { 
  268.             var list = instances[this.options.fps]; 
  269.             return list && list.contains(this); 
  270.         } 
  271.  
  272.     }); 
  273.  
  274.     Fx.compute = function (from, to, delta) { 
  275.         return (to - from) * delta + from; 
  276.     }; 
  277.  
  278.     // 预置特效间隔毫秒数,当可选参数duration为字符串时调用Fx.Durations对象得到特效间隔毫秒数 
  279.     Fx.Durations = { 'short': 250, 'normal': 500, 'long': 1000 }; 
  280.  
  281.     // global timers 
  282.     // instances对象字面量缓存所有的fx实例对象,它的所有键值对中的键就是对应一个fps值,值为一个包含设置了相同fps的动画特效实例的数组 
  283.     // timers对象缓存setInterval()方法返回的ID值 
  284.     var instances = {}, 
  285.             timers = {}; 
  286.  
  287.     /** 
  288.     * @method: loop 
  289.     * @description: 遍历动画特效实例数组,执行动画特效 
  290.     **/ 
  291.     var loop = function () { 
  292.         var now = Date.now(); 
  293.         for (var i = this.length; i--; ) { 
  294.             var instance = this[i]; 
  295.             if (instance) { instance.step(now); } 
  296.         } 
  297.     }; 
  298.  
  299.     /** 
  300.     * @method: pushInstance 
  301.     * @description: 遍历动画特效实例数组,执行动画特效 
  302.     **/ 
  303.     var pushInstance = function (fps) { 
  304.         // 取得缓存的与参数fps对应的动画特效数组,如果没有则instances对象新建一个键值存储这个数组 
  305.         var list = instances[fps] || (instances[fps] = []); 
  306.         // 缓存fx实例对象 
  307.         list.push(this); 
  308.         if (!timers[fps]) { 
  309.             // 设置定时器 
  310.             timers[fps] = loop.periodical(Math.round(1000 / fps), list); 
  311.         } 
  312.     }; 
  313.  
  314.     /** 
  315.     * @method: pullInstance 
  316.     * @param from - (number) 要停止的动画特效实例的秒帧数 
  317.     * @description: 停止运行一个动画特效实例 
  318.     **/ 
  319.     var pullInstance = function (fps) { 
  320.         // 取得缓存的与参数fps对应的动画特效数组 
  321.         var list = instances[fps]; 
  322.         if (list) { 
  323.             // 从数组中删除运行pullInstance函数的fx实例对象 
  324.             list.erase(this); 
  325.             if (!list.length && timers[fps]) { 
  326.                 // 如果数组为空,则删除instances对象中的这个数组 
  327.                 delete instances[fps]; 
  328.                 // 清除定时器 
  329.                 timers[fps] = clearInterval(timers[fps]); 
  330.             } 
  331.         } 
  332.     }; 
  333.  
  334. })(); 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章