Script.aculo.us开发系列(四):动画弹出菜单的实现-开发自己的动画
Posted on 2007-09-13 14:40 Hafeyang 阅读(1680) 评论(2) 编辑 收藏 举报在这篇文章中先分析一个案例,然后实现一个类似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 < 0) this.effectiveWidth = 0;
if (this.effectiveHeight < 0) this.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';
}
});
/*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 < 0) this.effectiveWidth = 0;
if (this.effectiveHeight < 0) this.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封装的艺术