script.aculo.us代码分析(一)

String.prototype.parseColor()
string=rgb(12,34,56)、#444(只有三位数),把string转化成#xxxxxx。之所以这么表达,是要表达清这个函数是实例函数还是静态函数。很多资料上讲来讲去,我们还是不清楚这个是个什么类型的函数。这样一写不就清楚了吗。

Element.collectTextNodes = function(element)
把element的所有文本内容全部收集起来。

Element.collectTextNodesIgnoreClass = function(element, className)
收集element中类名不是className的节点中的所有文本,并连成一片。与element.innerText差不多。

Element.setContentZoom = function(element, percent)
通过设置fontsize=x%em来达到缩放内容的目的。

Element.getInlineOpacity = function(element)
设置透明度,不兼容ie。可能是故意不兼容的。用的是:element.style.opacity。

Element.forceRerendering = function(element)
强制刷新element的内容。原理就是加一个空格文本节点再删除这个文本节点。

  1. //创建一个全局变量。
  2. var Effect = {
  3.  //定义一个错误提示文本
  4.   _elementDoesNotExistError: {
  5.     name: 'ElementDoesNotExistError',
  6.     message: 'The specified DOM element does not exist, but is required for this effect to operate'
  7.   },
  8.  //顾名思意:Transitions是转换的意思,它定义一些转换函数。
  9.   Transitions: {
  10.     linear: Prototype.K,
  11.     sinoidal: function(pos) {
  12.       return (-Math.cos(pos*Math.PI)/2) + 0.5;
  13.     },
  14.     reverse: function(pos) {
  15.       return 1-pos;
  16.     },
  17.     flicker: function(pos) {
  18.       var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
  19.       return pos > 1 ? 1 : pos;
  20.     },
  21.     wobble: function(pos) {
  22.       return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
  23.     },
  24.     pulse: function(pos, pulses) { 
  25.       pulses = pulses || 5; 
  26.       return (
  27.         ((pos % (1/pulses)) * pulses).round() == 0 ? 
  28.               ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
  29.           1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
  30.         );
  31.     },
  32.     spring: function(pos) { 
  33.       return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
  34.     },
  35.     none: function(pos) {
  36.       return 0;
  37.     },
  38.     full: function(pos) {
  39.       return 1;
  40.     }
  41.   },
  42.   DefaultOptions: {
  43.     duration:   1.0,   //以秒为单位
  44.     fps:        100,   //每秒的桢数,最小只能是66kps。为什么是这样,这是因为队列中触发间隔时15ms。
  45.     sync:       false//为true则为异步动画。
  46.     from:       0.0,  //这个玩意起始百分数
  47.     to:         1.0,  //终点百分数
  48.     delay:      0.0,  //动画在起动后延迟的时间。
  49.     queue:      'parallel'  //要加入的动画队列。
  50.   },
  51.  
  52.  //为element中的每一个字符都加上标记,用span括起来。
  53.   tagifyText: function(element) {
  54.     var tagifyStyle = 'position:relative';
  55.     if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
  56.     
  57.     element = $(element);
  58.   //用element.childNodes的方法取得所有节点,正是方便啊。
  59.     $A(element.childNodes).each( function(child) {
  60.    //nodeType=1是元素节点,3为文本节点。2是属性节点。一般都只用到1,2。
  61.       if (child.nodeType==3) {
  62.         child.nodeValue.toArray().each( function(character) {
  63.           element.insertBefore(
  64.             new Element('span', {style: tagifyStyle}).update(
  65.               character == ' ' ? String.fromCharCode(160) : character), 
  66.               child);
  67.         });
  68.         Element.remove(child);
  69.       }
  70.     });
  71.   },
  72.  
  73.  //element可以是一个元素数组,也可是一个元素节点,如果是单个元素,则会自动获取此元素下面的所有子节点,针对子节点施加特效。
  74.   multiple: function(element, effect) {
  75.     var elements;
  76.     if (((typeof element == 'object') || 
  77.         Object.isFunction(element)) && 
  78.        (element.length))
  79.       elements = element;
  80.     else
  81.       elements = $(element).childNodes;
  82.     
  83.   //此函数还允计有第三个参数,用于传入options。
  84.     var options = Object.extend({
  85.       speed: 0.1,
  86.       delay: 0.0
  87.     }, arguments[2] || { });
  88.     var masterDelay = options.delay;
  89.   
  90.   //为数组中的每个元素都创建动画,并且,依次设定delay。delay按index成线性增长。
  91.     $A(elements).each(function(element, index) {
  92.       new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
  93.     });
  94.   },
  95.  //这个JSON为下面的toggle函数准备的。
  96.   PAIRS: {
  97.     'slide':  ['SlideDown','SlideUp'],
  98.     'blind':  ['BlindDown','BlindUp'],
  99.     'appear': ['Appear','Fade']
  100.   },
  101.  //用于反转特效,能反转三类特效,这三类特效都有一个特点,要么让元素可见,要么让元素隐藏。用法如下:
  102.  //Effect.toggle(element)当元素可见时则调用fade,如果不可见则调用appear。就这么容易。
  103.   //可以传入第三个参数options。
  104.   toggle: function(element, effect) {
  105.     element = $(element);
  106.     effect = (effect || 'appear').toLowerCase();
  107.     var options = Object.extend({
  108.    //关于特效所加入队列的设置:position是加上位置,scope是队列名,limit是特效在队列中的极限位置。
  109.       queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
  110.     }, arguments[2] || { });
  111.     Effect[element.visible() ? 
  112.       Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  113.   }
  114. };
  115. //设置默认转换函数。
  116. Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
  117. //特效队列
  118. Effect.ScopedQueue = Class.create(Enumerable, {
  119.   //构造函数
  120.   initialize: function() {
  121.   //用于放特效对象的数组
  122.     this.effects  = [];
  123.   //定时器的句柄
  124.     this.interval = null;    
  125.   },
  126.  //要从Enumerable继承就非得要实现这个_each函数
  127.   _each: function(iterator) {
  128.     this.effects._each(iterator);
  129.   },
  130.  //将特效对象加入到this.effects中
  131.   add: function(effect) {
  132.   //返回自70/1/1 0:0:0至今的毫秒数
  133.     var timestamp = new Date().getTime();
  134.     
  135.   
  136.     var position = Object.isString(effect.options.queue) ? 
  137.       effect.options.queue : effect.options.queue.position;
  138.     
  139.     switch(position) {
  140.       case 'front':
  141.         // 让那些没有开始的特效在这个特效之后发生。
  142.         this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
  143.             e.startOn  += effect.finishOn;
  144.             e.finishOn += effect.finishOn;
  145.           });
  146.         break;
  147.       case 'with-last':
  148.     //让特效与队列中最后一个特效同时发生。
  149.         timestamp = this.effects.pluck('startOn').max() || timestamp;
  150.         break;
  151.       case 'end':
  152.         // 在队列最后一个特效之后发生,这个pluck函数好用啦。
  153.         timestamp = this.effects.pluck('finishOn').max() || timestamp;
  154.         break;
  155.     }
  156.     
  157.   //设特效的起始时间
  158.     effect.startOn  += timestamp;
  159.     effect.finishOn += timestamp;
  160.   //如果没有极限位置设置或者数组大小没有超过极限位置,则在队列中加入此特效
  161.     if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
  162.       this.effects.push(effect);
  163.     
  164.   //如果没有启动定时器,则启动定时器,并把句柄返回给this.interval属性。
  165.     if (!this.interval)
  166.       this.interval = setInterval(this.loop.bind(this), 15);
  167.   },
  168.  //移除特效
  169.   remove: function(effect) {
  170.   //reject的作用是把条件函数返回假时的成员的数组
  171.     this.effects = this.effects.reject(function(e) { return e==effect });
  172.     if (this.effects.length == 0) {
  173.       clearInterval(this.interval);
  174.       this.interval = null;
  175.     }
  176.   },
  177.  //定时器每次被触发,都执行的玩意,每触发一次就给特效数组循环一次,哪个的特效的时间到了,就让它工作去了。
  178.   loop: function() {
  179.     var timePos = new Date().getTime();
  180.     for(var i=0, len=this.effects.length;i<len;i++) 
  181.       this.effects[i] && this.effects[i].loop(timePos);
  182.   }
  183. });
  184. //特效队列的容器,因为一个页面中,可能为了管理许多的特效,而将一些特效放在一个特定的队列中。也方便管理嘛。
  185. Effect.Queues = {
  186.   instances: $H(),
  187.   get: function(queueName) {
  188.     if (!Object.isString(queueName)) return queueName;
  189.     
  190.     return this.instances.get(queueName) ||
  191.       this.instances.set(queueName, new Effect.ScopedQueue());
  192.   }
  193. };
  194. //页面的全局队列,默认情况下,如果一个特效没有定义其所属队列的名称,则把它们放到Effect.Queue中。
  195. Effect.Queue = Effect.Queues.get('global');
  196. //传说中的特效类的基类,所有特效都是从它继承过去的。这个东西是关键所在了。
  197. Effect.Base = Class.create({
  198.  //position的取值、意义前面有讲到了,可取值为:front、with-last、end。
  199.   position: null,
  200.  //这个函数是关键的关键,只要去后面的代码中看一看,一个特效的关键代码在它的initialize中,initialize的思路无非就是先设一下options,然后再setup一下,setup说到底就是设一些某些特效特有的属性,以供update函数中使用。
  201.   start: function(options) {
  202.   //产生字符串形式的事件触发代码
  203.     function codeForEvent(options,eventName){
  204.       return (
  205.         (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
  206.         (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
  207.       );
  208.     }
  209.     //如果传入options为false,则设其pos求取函数为线性函数
  210.     if (options && options.transition === false) options.transition = Effect.Transitions.linear;
  211.     this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
  212.     this.currentFrame = 0;  //当前桢
  213.     this.state        = 'idle';   //当前状态,有三种状态:idle、running、finished
  214.     this.startOn      = this.options.delay*1000;        //开始时间
  215.     this.finishOn     = this.startOn+(this.options.duration*1000);   //结束时间
  216.     this.fromToDelta  = this.options.to-this.options.from;       //变化的差值:from-to
  217.     this.totalTime    = this.finishOn-this.startOn;           //总共运行的时间,以毫秒为单位,不同于duration是以秒为单位。
  218.     this.totalFrames  = this.options.fps*this.options.duration;     //总共的桢数
  219.     
  220.   //创建render成员函数,一切的关键都在这儿了,用它来更新视图、界面。它做了哪些事呢?
  221.     //如果状态是:idle,则执行beforeSetup、setUp、afterSetup,如果是running的话,就通过transition来求pos,然后是beforeUpdate、update、afterUpdate。
  222.   //如果状态为finished,自然是啥都不执行。canel的实质就是把状态变量变成finished而己。这样做确实存在很多问题。因为中途退出跟正常结束的标志是一样的。事实上,这是两个不同的状态。框架没有对此进行细分。因此,大伙在网上查到的那个教程,里面提到canel后还执行那个afterstart的事件这个bug。这其实还只是一般分后果,所以啊,不研究代码,你就不晓得这个后果有哪些,说也说不清楚,网上也没有这个资料,这年头,还得靠自己。
  223.     eval('this.render = function(pos){ '+
  224.       'if (this.state=="idle"){this.state="running";'+
  225.       codeForEvent(this.options,'beforeSetup')+
  226.       (this.setup ? 'this.setup();':'')+ 
  227.       codeForEvent(this.options,'afterSetup')+
  228.       '};if (this.state=="running"){'+
  229.       'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
  230.       'this.position=pos;'+
  231.       codeForEvent(this.options,'beforeUpdate')+
  232.       (this.update ? 'this.update(pos);':'')+
  233.       codeForEvent(this.options,'afterUpdate')+
  234.       '}}');
  235.     
  236.     this.event('beforeStart');
  237.     if (!this.options.sync)
  238.       Effect.Queues.get(Object.isString(this.options.queue) ? 
  239.         'global' : this.options.queue.scope).add(this);
  240.   },
  241.   loop: function(timePos) {
  242.    //如果是最后一桢
  243.     if (timePos >= this.startOn) {
  244.       if (timePos >= this.finishOn) {
  245.         this.render(1.0);
  246.         this.cancel();
  247.         this.event('beforeFinish');
  248.         if (this.finish) this.finish(); 
  249.         this.event('afterFinish');
  250.         return;  
  251.       }
  252.       var pos   = (timePos - this.startOn) / this.totalTime,
  253.           frame = (pos * this.totalFrames).round();
  254.       if (frame > this.currentFrame) {
  255.     //呈现当前视图
  256.         this.render(pos);
  257.     //这个当前桢数是摆看的,没有真正用于计算,真正用于计算的只有pos。
  258.         this.currentFrame = frame;
  259.       }
  260.     }
  261.   },
  262.  //取消特效
  263.   cancel: function() {
  264.     if (!this.options.sync)
  265.       Effect.Queues.get(Object.isString(this.options.queue) ? 
  266.         'global' : this.options.queue.scope).remove(this);
  267.     this.state = 'finished';
  268.   },
  269.  
  270.  //触发事件处理函数
  271.   event: function(eventName) {
  272.     if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
  273.     if (this.options[eventName]) this.options[eventName](this);
  274.   },
  275.  //相当于tostring。
  276.   inspect: function() {
  277.     var data = $H();
  278.     for(property in this)
  279.       if (!Object.isFunction(this[property])) data.set(property, this[property]);
  280.     return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  281.   }
  282. });
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值