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

在这篇文章中先分析一个案例,然后实现一个类似Amazon或者DangDang上面的动画弹出菜单,谈谈如何利用Script.aculo.us的Effect开发自定义动画,先看看运行效果 http://www1.qcxy.hb.cn/qphy/Script_Aculo_Us/AmazonPopMenu.html

 

Effect.MoveAndResizeTo = Class.create();//Prototype中创建类的"招式"
/*Object.extend()本来是扩展类功能的函数,事实上已经构成了继承,Object.extend(Effect.MoveAndResizeTo.prototype, Effect.Base.prototype)将Effect.Base的所有方法和属性(或者说是原型prototype)扩展到Effect.MoveAndResizeTo.prototype,这样MoveAndResizeTo就有了Base的所有方法和属性,在此基础上在重载initialize()方法和update(),这样自定义动画就生成了*/
Object.extend(Object.extend(Effect.MoveAndResizeTo.prototype, Effect.Base.prototype), 
{
  initialize: 
function(element, toTop, toLeft, toWidth, toHeight) {//initialize()其实是构造函数

    
this.element      = $(element);
    
this.toTop        = toTop;
    
this.toLeft       = toLeft;
    
this.toWidth      = toWidth;
    
this.toHeight     = toHeight;

    
/*计算元素的坐标和大小,注意默认值的实现,这些都是每个开发者都应该注意的细节*/
    
this.originalTop  = parseFloat(Element.getStyle(this.element,'top')  || 0);
    
this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || 0);
    
this.originalWidth  = parseFloat(Element.getStyle(this.element,'width')  || 0);
    
this.originalHeight = parseFloat(Element.getStyle(this.element,'height') || 0);

    
this.effectiveTop = this.toTop;
    
this.effectiveLeft = this.toLeft;
    
/*准确的计算元素的宽度和高度,要考虑两种不同的盒模型,详细可以参考http://hi.baidu.com/fysy/blog/item/97b671f0239b4aaea50f525b.html*/
    
this.effectiveWidth = this.toWidth 
                        
- parseFloat(Element.getStyle(this.element,'margin-left') || 0
                        
- parseFloat(Element.getStyle(this.element,'margin-right') || 0
                        
- (document.compatMode == 'BackCompat' ? 0 :parseFloat(Element.getStyle(this.element,'padding-left') || 0
                            
+ parseFloat(Element.getStyle(this.element,'padding-right') || 0
                            
+ parseFloat(Element.getStyle(this.element,'border-left-width') || 0)
                            
+ parseFloat(Element.getStyle(this.element,'border-right-width') || 0));


    
this.effectiveHeight = this.toHeight
                        
- parseFloat(Element.getStyle(this.element,'margin-top') || 0
                        
- parseFloat(Element.getStyle(this.element,'margin-bottom') || 0
                        
- (document.compatMode == 'BackCompat' ? 0 :parseFloat(Element.getStyle(this.element,'padding-top') || 0
                            
+ parseFloat(Element.getStyle(this.element,'padding-bottom') || 0
                            
+ parseFloat(Element.getStyle(this.element,'border-top-width') || 0)
                            
+ parseFloat(Element.getStyle(this.element,'border-bottom-width') || 0));
/*选项的实现,arguments[5]返回第六个参数,如果没有则为空的Json,默认的选项无非是把这里的Json改改*/
    
this.options = arguments[5|| {};

    
if (this.effectiveWidth < 0this.effectiveWidth = 0;
    
if (this.effectiveHeight < 0this.effectiveHeight = 0;

    
if (this.originalTop == this.effectiveTop &&
        
this.originalLeft == this.effectiveLeft &&
        
this.originalWidth == this.effectiveWidth &&
        
this.originalHeight == this.effectiveHeight) {

      
// no need to start!
      return;
    }

/*在构造函数的最后别忘了加上这么一行,Base不是有一些选项么,起码得设置一下吧.它好像C#中的base.functionName()*/
    
this.start(this.options);

  }
,
  
/*重写update()方法,多次提到每帧都会调用这个方法重写视图,接受一个参数position 这个position是transition中的属性指向的方法返回的值,介于0到1之间,这样rensition的选项可以在任何动画中使用了*/
  update: 
function(position) {
    topd  
= this.effectiveTop * (position) + this.originalTop * (1 - position);
    leftd 
= this.effectiveLeft * (position) + this.originalLeft * (1 - position);
    widthd  
= this.effectiveWidth * (position) + this.originalWidth * (1 - position);
    heightd 
= this.effectiveHeight * (position) + this.originalHeight * (1 - position);
    
this.setPosition(topd, leftd, widthd, heightd);
  }
,
  setPosition: 
function(topd, leftd, widthd, heightd) {

    
this.element.style.top = topd+'px';
    
this.element.style.left = leftd+'px';
    
this.element.style.width = widthd+'px';
    
this.element.style.height = heightd+'px';
  }

}
);

这个例子是官方网站里提供的,总结一下,无非是重载一下构造函数,和自己实现update()方法.

去过Amazon或者DangDang的朋友一定记得那酷酷的弹出类别菜单,我可是很早就垂涎欲滴了,以前自己去实现过,很由难度,也看过他们的源代码,很晦涩,且非常长.但是看到上面的动画是否看到了些希望的曙光了.

仔细分析一下,在页面上会有一大一小的两个div,鼠标放在小的div上,就出现一个框,从小的div变到大的div,最后大的div显示出来,同时在鼠标离开这两个div的时候需要反着"放"这段动画.

 那段动画就是一个层应用上面的动画效果.下面只是用Prototype框架去封装它了,所以下篇文章的标题是Prototype封装的艺术