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. })(); 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章