本文參考了我佛山人的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.c***ules;
- // 遍歷每條規則
- 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