本文参考了我佛山人的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