博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Script.aculo.us开发系列(五):Prototype封装的艺术

Posted on 2007-09-13 16:46  Hafeyang  阅读(1689)  评论(0编辑  收藏  举报

将自己写的一些东西分装起来是一件很有学问的事情,特别是用javascript这门并不是很支持面向对象的语言,这篇文章将介绍封装上篇文章中提到的那个动画.文章之叫做"Prototype封装的艺术",是因为是利用Prototype这个框架封装自己的类.

可能用到的

首先是原型的概念,原型prototype是区别于Prototype这个框架的,可以利用原型动态的给予类新的方法和属性.

Prototype框架中Function扩展的bind()方法和bindAsEventListener() 方法,他们的使用方法是:functionName.bindAsEventListener(obj) ,其中obj是指向调用该函数的对象,通俗的说,要在函数中使用this.来访问类中的成员就可以使用该方法,同时functionName(oEvent)接受当前的事件对象

<script type="text/javascript" src="lib/prototype.js"></script>
<script type="text/javascript" src="lib/scriptaculous.js?load=effects"></script>
<script type="text/javascript" src="lib/Effect.MoveAndResizeTo.js"></script>
<script type="text/javascript">
function init()
{
    
new AmazonPop("divSource","divTarget",{duration:0.1});
}

Event.observe(window,'load',init);

var AmazonPop=Class.create();//create()方法其实是在调用initialize()方法
AmazonPop.prototype={
    initialize:
function (source,target)//构造函数
    {
        
this.Source=source;//this 相当于容器,记录属性和方法
        this.Target=target;
        
this.options=arguments[2]||{zIndex:10,duration:0.2};//选项的默认值
        
        
//Create the Animaiton Layer
        var animation=document.createElement("div");
        animation.id
="animation";
        document.body.appendChild(animation);
        
this.Animation="animation";
        
        $(
"animation").setStyle({'border':'solid 1px #cbcbcb','position':'absolute'});
        $(
"animation").setStyle({'z-index':this.options.zIndex});
        Position.clone(source,
this.Animation);//Position.clone(),直接将source的坐标大小复制给Animation
        
        
this._calculate(null);//计算source的坐标和大小
        
        
this.isBinded=false;//方法是否绑定上
        this.inRunning=false;//动画正在"播放"
                
        $(target).hide();
        $(
this.Animation).hide();
        
/*监听Source的MouseOver事件,注意handleSourceMouseOver.bindAsEventListener(this)*/
        Event.observe($(
this.Source),'mouseover',this.handleSourceMouseOver.bindAsEventListener(this));        
        Event.observe(window,'resize',
this._calculate.bindAsEventListener(this));//Caculate Again when Window is resized!
        Event.observe($(this.Source),"mouseout",this.handleTargetMouse.bindAsEventListener(this));
    }
,
    _calculate:
function (oEvent)
    
{
        
this.targetTop=Position.cumulativeOffset($(this.Target))[1];
        
this.targetLeft=Position.cumulativeOffset($(this.Target))[0];
        
this.targetWidth=$(this.Target).getWidth();
        
this.targetHeight=$(this.Target).getHeight();
        
        
this.sourceTop=Position.cumulativeOffset($(this.Source))[1];
        
this.sourceLeft=Position.cumulativeOffset($(this.Source))[0];
        
this.sourceWidth=$(this.Source).getWidth();
        
this.sourceHeight=$(this.Source).getHeight();    
    }
,
    handleSourceMouseOver:
function (oEvent)//接受oEvent参数
    {
        
if(!this.isRunning&& !$(this.Target).visible())
        
{
            $(
this.Animation).show();
            
this.isRunning=true;
            
/*调用Effect.MoveAndResizeTo效果*/
            
new Effect.MoveAndResizeTo("animation",this.targetTop,this.targetLeft,this.targetWidth,this.targetHeight,
                
{
                    duration:
this.options.duration,
                    afterFinish:
this.ShowFinished.bind(this)//ShowFinished.bind(this),意味着在ShowFinished函数中可以用this引用当前对象AmazonPop
                }

            );        
        }
    
        
    }
,
    ShowFinished:
function(obj)
    
{
        $(
this.Target).show();
        $(
this.Animation).hide();
        Event.observe($(
this.Target),"mouseover",this.handleTargetMouseOver.bindAsEventListener(this));
        
this.isRunning=false;
        
    }
,
    handleTargetMouseOver:
function(oEvent)
    
{
        
if(!this.isBinded&& !this.isRunning)
        
{
    
            Event.observe($(
this.Target),"mouseover",this.handleTargetMouse.bindAsEventListener(this));
            Event.observe($(
this.Target),"mousemove",this.handleTargetMouse.bindAsEventListener(this));
            Event.observe($(
this.Target),"mouseout",this.handleTargetMouse.bindAsEventListener(this));
            
            
this.isBinded=true;
        }

    }
,
    handleTargetMouse:
function(oEvent)
    
{
        
if(!this.isRunning)
        
{
            
            
if(Position.within($(this.Target),oEvent.clientX,oEvent.clientY))
            
{
                
return ;
            }

            
            $(
this.Animation).show();
            
this.isRunning=true;
            
new Effect.MoveAndResizeTo(this.Animation,this.sourceTop,this.sourceLeft,this.sourceWidth,this.sourceHeight,{duration:this.options.duration,afterFinish:this.HideFinished.bind(this)});    
        }

    }
,
    HideFinished:
function (obj)
    
{
        $(
this.Target).hide();
        $(
this.Animation).hide();
        
this.isRunning=false;
    }


}
;

</script>

(希望我写的代码不让大家觉得很难阅读.)

把代码贴出来的原因更多的是我的一些体会,有些开发者可能认为,对与一种效果,在页面中应用就行,没有必要封装成类.其实封装成类的目的一是提高其复用性,还由一个最重要的问题,那就是封装能够存储类中所需要的变量

假如在上述情况中,要在handleTargetMove()中查看isRunning值,除非所有的属性都定义成全局变量,否则在这种处理事件的代码中无法访问到isRunning属性,这也是我先前一直都想开发这个效果的时候一直没有突破的问题.

关于Effect的介绍到这里告一段落,最后送各位一个小礼物,用 Script.aculo.us开发的Accordion