本文參考瞭我佛山人的Mootools1.2的源碼分析二十九 — Fx.CSS
/*
—
name: Fx.CSS
description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
license: MIT-style license.
requires: [Fx, Element.Style]
provides: Fx.CSS
源碼分析: 苦苦的苦瓜(http://hmking.blog.51cto.com)
…
*/
/**
* @Fx.CSS: 跟CSS有關的動畫的基類,這裡的動畫,主要是從一個開始值到結束值的變化效果
**/
Fx.CSS = new Class({
// 繼承自Fx
Extends: Fx,
// prepares the base from/to object
/**
* @method: prepare
* @param element – (object) 特效作用的元素對象
* @param property – (string) CSS屬性
* @param values – (mixed) 包含開始值和結束值的數組或一個單值(結束值)
* @returns: (object) – 包含from和to兩個鍵值的對象字面量
* @description: 動畫的開始和結束值的前期處理
* @notes: 此時from和to兩個鍵的值為數組類型
**/
prepare: function (element, property, values) {
// 把變量values數組化,因為values可能傳一個單值,也可能是一個數組
values = Array.from(values);
// 取特效的起始值和結束值,如果如果隻傳瞭一個值,則本值將作為結束值,CSS屬性的當前值為特效起始值
if (values[1] == null) {
values[1] = values[0];
values[0] = element.getStyle(property);
}
// 將數組中的項使用parse方法解釋
var parsed = values.map(this.parse);
// 返回from和to兩個鍵值的對象字面量
return { from: parsed[0], to: parsed[1] };
},
//parses a value into an array
/**
* @method: parse
* @param value – (mixed) CSS屬性值
* @returns: (array) – 數組項值為包含value和parser兩個鍵值的對象字面量,存儲解釋過的CSS屬性值和包含針對此屬性值的解釋器
* @description: 解析一個CSS屬性值為一個數組
**/
parse: function (value) {
// 使用lambad表達式,將value函數化之後再執行,這樣的好處是使傳的值可以是function,也可以是固定值
value = Function.from(value)();
// 數組化,如果是字符串類型,使用空格分隔成數組
value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
// 對數組逐項處理
return value.map(function (val) {
// 轉為字符類型
val = String(val);
var found = false;
Object.each(Fx.CSS.Parsers, function (parser, key) {
// 第一項時這裡為false繼續執行下面,找到合適的解釋器後found判斷不再為false,避免重復解釋
if (found) { return; }
// 嘗試使用解釋器解釋值
var parsed = parser.parse(val);
// 如果解釋成功,記錄解釋後的值和使用的解釋器(因為還要使用解釋器的compute和serve方法)
if (parsed || parsed === 0) {
found = {
value: parsed,
parser: parser
};
}
});
// 默認使用字符串值的解釋器
found = found || {
value: val,
parser: Fx.CSS.Parsers.String
};
return found;
});
},
// computes by a from and to prepared objects, using their parsers.
/**
* @method: compute
* @param from – (array) 解釋過的CSS屬性的起始值的數組
* @param to – (array) 解釋過的CSS屬性的結束值的數組
* @param delta – (mixed) 特效變化所需要的比例因子
* @returns: (array) 包含計算過的特效當前CSS屬性值信息的一個數組
* @description: 根據初始值,結束值和比例因子求目標值
**/
compute: function (from, to, delta) {
var computed = [];
// 取數項小的遍歷
(Math.min(from.length, to.length)).times(function (i) {
// 返回計算過的值和使用的解釋器
computed.push({
value: from[i].parser.compute(from[i].value, to[i].value, delta),
parser: from[i].parser
});
});
// 為typeOf提供精準類型值
computed.$family = Function.from('fx:css:value');
return computed;
},
// serves the value as settable
/**
* @method: serve
* @param value – (mixed) CSS屬性目標值,此參數可以是一個解釋過的CSS屬性值數組,也可以為一個CSS屬性值
* @param unit – (string 默認為 false) 計量單位(如: 'px', 'em', 或 '%').
* @returns: (array) 包含計算過的特效當前CSS屬性值信息的一個數組
* @description: 對計算過的CSS屬性值數組對象做最後的包裝處理,使其可應用於Element.setStyle方法
**/
serve: function (value, unit) {
// 如果值未經解釋,需要先解釋(比如單獨調用set方法)
if (typeOf(value) != 'fx:css:value') {
value = this.parse(value);
}
var returned = [];
value.each(function (bit) {
// 得到最終的使用值
returned = returned.concat(bit.parser.serve(bit.value, unit));
});
return returned;
},
// renders the change to an element
// 因為類本身是跟CSS有類,所以最終將計算出的數組通過setStyle反映到element的相應CSS屬性上
render: function (element, property, value, unit) {
element.setStyle(property, this.serve(value, unit));
},
// searches inside the page css to find the values for a selector
// 從當前頁面的樣式中查找指定選擇符的樣式設置
search: function (selector) {
// 模擬緩存,先從臨時對象中找相應鍵值,提高效率
if (Fx.CSS.Cache[selector]) { return Fx.CSS.Cache[selector]; }
var to = {},
selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
// 遍歷當前頁面的樣式表
Array.each(document.styleSheets, function (sheet, j) {
var href = sheet.href;
// 忽略跨域的外鏈樣式表
if (href && href.contains('://') && !href.contains(document.domain)) {
return;
}
// 樣式規則集
var rules = sheet.rules || sheet.cssRules;
// 遍歷每條規則
Array.each(rules, function (rule, i) {
if (!rule.style) { return; }
// 選擇符(類型選擇符的話會轉為小寫)
var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function (m) {
return m.toLowerCase();
}) : null;
// 匹配指定的樣式選擇符
if (!selectorText || !selectorTest.test(selectorText)) { return; }
// 樣式值分析
Object.each(Element.Styles, function (value, style) {
// 無值
if (!rule.style[style] || Element.ShortStyles[style]) { return; }
// 轉為字符串
value = String(rule.style[style]);
// 顏色值處理
to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
});
});
});
// 緩存
return Fx.CSS.Cache[selector] = to;
}
});
Fx.CSS.Cache = {};
// #region – 解釋器 –
// CSS中幾種值類型的解釋器,每個解釋器必須實現parse/compute/serve三個接口
Fx.CSS.Parsers = {
// 對顏色的解釋處理
Color: {
parse: function (value) {
// 如果是十六進制的顏色表示,處理成RGB數組
if (value.match(/^#[0-9a-f]{3,6}$/i)) {
return value.hexToRgb(true);
}
// 如果是RGB的顏色顯示,正則匹配出RGB數組,不匹配返回flase,以便引擎調用其它解釋器解釋
return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
},
compute: function (from, to, delta) {
// 對R、G和B分別計算目標值
return from.map(function (value, i) {
// 可以看到仍然使用靜態的compute方法
return Math.round(Fx.compute(from[i], to[i], delta));
});
},
serve: function (value) {
// 將R、G、B都轉成數值型
return value.map(Number);
}
},
// 數值類型的解釋處理
Number: {
// 轉為浮點數
parse: parseFloat,
// 跟Fx中的算法一樣
compute: Fx.compute,
serve: function (value, unit) {
// 加上單位,比如px,pt之類
return (unit) ? value + unit : value;
}
},
// 對字符類型的解釋處理
String: {
// 解釋器返回false,相當於parse : function(){return false;}
parse: Function.from(false),
// compute方法執行時返回第2個參數
compute: function (zero, one) {
return one;
},
// serve方法執行時返回第1個參數
serve: function (zero) {
return zero;
}
}
};
// #endregion
作者“苦苦的苦瓜”