Mootools1.4 - Fx源碼分析,如果理解有誤歡迎指正:
- /*
- ---
- name: Fx
- description: Contains the basic animation logic to be extended by all other Fx Classes.
- license: MIT-style license.
- requires: [Chain, Events, Options]
- provides: Fx
- 源碼分析: 苦苦的苦瓜(http://hmking.blog.51cto.com)
- ...
- */
- (function () {
- /**
- * @Fx: 本類一般不獨立使用,它用來提供作爲Fx系的類的基礎功能類.所有其他的Fx系列的類都繼承本類.
- **/
- var Fx = this.Fx = new Class({
- Implements: [Chain, Events, Options],
- // #region - constructor -
- /**
- * @Events:
- * @event start - (function) 特效開始執行時觸發
- * @event cancel - (function) 手動停止特效執行時觸發
- * @event complete - (function) 特效執行完成後觸發
- * @event chainComplete - (function) 當使用link可選項爲'chain'時, 該事件在特效鏈執行完後觸發
- * @event stop - (function) 特效執行完成前,執行stop方法時觸發
- **/
- /**
- * @Optoins:
- * @option fps - (number: 默認爲 60) 動畫特效的秒幀數
- * @option unit - (string: 默認爲 false) 計量單位(如: 'px', 'em', 或 '%').
- * @option duration - (number: 默認爲 500) 可以讓你定義這個動畫的持續時間。持續時間和速度是不一樣的,因此如果你想讓一個對象在一秒內移動100個像素,
- * 那麼它將比一個每秒移動1000個像素的對象要慢。你可以輸入一個數字(以毫秒爲單位). 也可使用以下預定義字符串:
- * 'short' - 250ms
- * 'normal' - 500ms
- * 'long' - 1000ms
- * @option frames - (number) 設定動畫特效執行的總幀數,默認爲null自動匹配
- * @option frameSkip - (boolean: 默認爲true) 設定動畫特效當一幀執行的時間大於每幀之間的時間間隔,是否跳過這段時間所要執行的幀
- * @option link - (string: 默認爲 ignore) 可爲: 'ignore', 'cancel' 或 'chain'
- * 'ignore' - 當特效正在執行之中時,再次調用特效開始的方法將被忽略(和可選項'wait'爲true時同義)
- * 'cancel' - 當特效正在執行之中時,再次調用特效開始的方法將立即取消當前執行的特效,開始執行新的特效
- * 'chain' - 當特效正在執行之中時,再次調用特效開始的方法將會把新的特效鏈接在當前執行的特效之後,依次執行各個特效
- * @option transition - (function: 默認爲 Fx.Transitions.Sine.easeInOut) 特效的變換方程, 詳見Fx.Transitions. 也可以使用如下格式的字符串:
- * transition[:in][:out] - 例如: 'linear', 'quad:in', 'back:in', 'bounce:out', 'elastic:out', 'sine:in:out'
- **/
- options: {
- /*
- onStart: nil,
- onCancel: nil,
- onComplete: nil,
- */
- fps: 60,
- unit: false,
- duration: 500,
- frames: null,
- frameSkip: true,
- link: 'ignore'
- },
- initialize: function (options) {
- this.subject = this.subject || this;
- this.setOptions(options);
- },
- // #endregion
- /**
- * @method: getTransition
- * @returns: (function) - 特效的變換方程
- * @description: 取得動畫特效所要執行的特效方程
- **/
- getTransition: function () {
- return function (p) {
- return -(Math.cos(Math.PI * p) - 1) / 2;
- };
- },
- /**
- * @method: step
- * @param now - (mixed) 特效值
- * @returns: (function) - 特效的變換方程
- * @description: 動畫特效每一步執行的操作
- **/
- step: function (now) {
- if (this.options.frameSkip) {
- // 先取得當前時間減去上一幀執行時的時間,得到兩幀之間的時間間隔,計算這段時間內按正常的幀間隔時間能執行的幀的數量
- var diff = (this.time != null) ? (now - this.time) : 0,
- frames = diff / this.frameInterval;
- // 存儲當前幀執行時的時間
- this.time = now;
- // 執行的幀數累加
- this.frame += frames;
- } else {
- this.frame++;
- }
- // 判斷當前幀是否爲動畫特效的最後一幀
- if (this.frame < this.frames) {
- // 通過特效方程計算動畫特效運行當前幀所要變化的比例因子
- var delta = this.transition(this.frame / this.frames);
- this.set(this.compute(this.from, this.to, delta));
- } else {
- // 動畫特效執行完畢
- this.frame = this.frames;
- this.set(this.compute(this.from, this.to, 1));
- this.stop();
- }
- },
- /**
- * @method: set
- * @param value - (mixed) 特效值
- * @description: 用於設置特效值.該方法在特效變換過程中每個'步進'都會調用; 也可以手工調用,留作派生類實現
- **/
- set: function (now) {
- return now;
- },
- /**
- * @method: compute
- * @param from - (mixed) 特效的起始值
- * @param to - (mixed) 特效的結束值
- * @param delta - (mixed) 特效變化所需要的比例因子
- * @description: 根據初始值,結束值和比例因子求目標值
- **/
- compute: function (from, to, delta) {
- return Fx.compute(from, to, delta);
- },
- /**
- * @method: check
- * @parameters - 與start方法參數一致
- * @returns: (boolean) - 如果start方法可以繼續執行, 則返回 true ; 否則返回 false
- * @description: 判斷當特效正在執行之中時,再次調用特效開始的方法(start)是否繼續可以執行start方法
- **/
- check: function () {
- // 如果特效沒有運行,返回true
- if (!this.isRunning()) { return true; }
- switch (this.options.link) {
- case 'cancel': // 不等待正在運行的特效,直接取消並重新開始
- this.cancel();
- return true;
- case 'chain': // 等待當前特效運行結束後再繼續運行新特效
- this.chain(this.caller.pass(arguments, this));
- return false;
- }
- return false;
- },
- /**
- * @method: start
- * @param from - (mixed) 特效的起始值. 如果只給出一個參數,則本值將作爲結束值
- * @param to - (mixed, 可選) 特效的結束值
- * @returns: (object) - 當前的Fx實例
- * @description: 開始執行特效變換(並觸發'start'事件)
- **/
- start: function (from, to) {
- // 檢測start方法是否可以繼續執行
- if (!this.check(from, to)) { return this; }
- /**
- # 苦苦的苦瓜
- # 2011-09-25
- # 將用局部變量_options代替this.options
- **/
- var _options = this.options;
- this.from = from;
- this.to = to;
- this.frame = (_options.frameSkip) ? 0 : -1;
- this.time = null;
- // 取得特效執行的變換方程
- this.transition = this.getTransition();
- var frames = _options.frames,
- fps = _options.fps,
- duration = _options.duration;
- // 可選參數duration既可以數字類型,也可以爲字符串類型
- this.duration = Fx.Durations[duration] || duration.toInt();
- // 取得動畫特效每幀之間的時間間隔,毫秒爲單位
- this.frameInterval = 1000 / fps;
- // 計算動畫特效執行的總幀數
- this.frames = frames || Math.round(this.duration / this.frameInterval);
- // 觸發'start'事件
- this.fireEvent('start', this.subject);
- pushInstance.call(this, fps);
- return this;
- },
- /**
- * @method: stop
- * @returns: (object) - 當前的Fx實例
- * @description: 停止一個特效的執行
- **/
- stop: function () {
- if (this.isRunning()) {
- this.time = null;
- pullInstance.call(this, this.options.fps);
- if (this.frames == this.frame) {
- this.fireEvent('complete', this.subject);
- if (!this.callChain()) {
- this.fireEvent('chainComplete', this.subject);
- }
- } else {
- this.fireEvent('stop', this.subject);
- }
- }
- return this;
- },
- /**
- * @method: cancel
- * @returns: (object) - 當前的Fx實例
- * @description: 取消一個特效的執行(並觸發'cancel'事件)
- **/
- cancel: function () {
- if (this.isRunning()) {
- this.time = null;
- pullInstance.call(this, this.options.fps);
- this.frame = this.frames;
- this.fireEvent('cancel', this.subject).clearChain();
- }
- return this;
- },
- /**
- * @method: pause
- * @returns: (object) - 當前的Fx實例
- * @description: 暫停當前執行的特效
- **/
- pause: function () {
- if (this.isRunning()) {
- this.time = null;
- pullInstance.call(this, this.options.fps);
- }
- return this;
- },
- /**
- * @method: pause
- * @return: (object) - 當前的Fx實例
- * @description: 恢復執行暫停中的特效
- * @remark: 只有對暫停中的特效執行本方法纔有效果, 否則將忽略.
- **/
- resume: function () {
- if ((this.frame < this.frames) && !this.isRunning()) {
- pushInstance.call(this, this.options.fps);
- }
- return this;
- },
- /**
- * @method: isRunning
- * @return: (boolean) 特效運行狀態
- * @description: 檢測特效是否正在運行
- **/
- isRunning: function () {
- var list = instances[this.options.fps];
- return list && list.contains(this);
- }
- });
- Fx.compute = function (from, to, delta) {
- return (to - from) * delta + from;
- };
- // 預置特效間隔毫秒數,當可選參數duration爲字符串時調用Fx.Durations對象得到特效間隔毫秒數
- Fx.Durations = { 'short': 250, 'normal': 500, 'long': 1000 };
- // global timers
- // instances對象字面量緩存所有的fx實例對象,它的所有鍵值對中的鍵就是對應一個fps值,值爲一個包含設置了相同fps的動畫特效實例的數組
- // timers對象緩存setInterval()方法返回的ID值
- var instances = {},
- timers = {};
- /**
- * @method: loop
- * @description: 遍歷動畫特效實例數組,執行動畫特效
- **/
- var loop = function () {
- var now = Date.now();
- for (var i = this.length; i--; ) {
- var instance = this[i];
- if (instance) { instance.step(now); }
- }
- };
- /**
- * @method: pushInstance
- * @description: 遍歷動畫特效實例數組,執行動畫特效
- **/
- var pushInstance = function (fps) {
- // 取得緩存的與參數fps對應的動畫特效數組,如果沒有則instances對象新建一個鍵值存儲這個數組
- var list = instances[fps] || (instances[fps] = []);
- // 緩存fx實例對象
- list.push(this);
- if (!timers[fps]) {
- // 設置定時器
- timers[fps] = loop.periodical(Math.round(1000 / fps), list);
- }
- };
- /**
- * @method: pullInstance
- * @param from - (number) 要停止的動畫特效實例的秒幀數
- * @description: 停止運行一個動畫特效實例
- **/
- var pullInstance = function (fps) {
- // 取得緩存的與參數fps對應的動畫特效數組
- var list = instances[fps];
- if (list) {
- // 從數組中刪除運行pullInstance函數的fx實例對象
- list.erase(this);
- if (!list.length && timers[fps]) {
- // 如果數組爲空,則刪除instances對象中的這個數組
- delete instances[fps];
- // 清除定時器
- timers[fps] = clearInterval(timers[fps]);
- }
- }
- };
- })();