在AJAX如火如荼的今天,相信大家对Prototype这个Javascript类库应该都有所耳闻,它也的确使编写Javascript变得更简单。关于Prototype的文章,《Prototype简介》、《Prototype源码》诸如此类数不胜数;所以本文不会再做这几方面的介绍,并假设读者对Prototype有一定了解。
网页动画与原理
提到网页动画,大家首先想起应该Flash。不知道大家没有开发过Flash动画,故我想对此作一个简单的介绍(在我读大学的时候,对Flash也曾有过痴迷,所以也略懂一二)。Flash的动画主要分两类:渐变动画和逐帧动画。
- 渐类动画——用户在时间轴上创建开始的关键帧和结束的关键帧,开发环境(Macromedia Profassional Flash 8等)会根据以上所创建的关键帧的颜色、位置和形状等,在计算出中间的过渡帧并添加到相应的时间轴上。这适用于创建简单的动画。
- 逐帧动画——用户在时间轴的每帧上创建关键帧,并在其中绘制相应的图按。这适用于创建复杂的动画。
在Javascript中由于没有绘图API(应用程序接口),故只可以使用DOM+CSS改变元素的外观。而通过每隔一段时间调用一次改变元素外观的函数,实现类似Flash的渐类动画。
具体实现
因为不同的Javascript动画实现的基本原理都相同,所以可以创建一个基类将其抽象出来。代码如下:
var
Animation
=
Class.create();
Animation.prototype = {
/* ------------------------------------------------------------------------
| 用途:
| 构造函数
|
| 参数:
| element 将要实现动画效果的元素
| fps 每秒播放帧数
------------------------------------------------------------------------ */
initialize: function (element, fps) {
this .element = $(element);
this .interval = Math.round( 1000 / fps);
this .isPlaying = false ;
this .currentFrame = 1 ;
// 创建一个用于存储中间状态的临时对象
this .temp = { } ;
} ,
/* ------------------------------------------------------------------------
| 用途:
| 子类覆盖该方法,实现自定义的动画补间
------------------------------------------------------------------------ */
_createTweens: function (original, transformed, frames) { } ,
/* ------------------------------------------------------------------------
| 用途:
| 创建动画补间
|
| 参数:
| original 开始状态
| transformed 结束状态
| frames 动画帧数
------------------------------------------------------------------------ */
createTweens: function (original, transformed, frames) {
if ( this .isPlaying) {
this .stop();
}
this ._createTweens(original, transformed, frames);
this .original = original;
this .transformed = transformed;
this .frames = frames;
// 将开始状态拷贝到临时对象
Object.extend( this .temp, original);
} ,
/* ------------------------------------------------------------------------
| 用途:
| 判断临时对象状态是否超出结束状态
|
| 参数:
| prop 状态属性名称
------------------------------------------------------------------------ */
_isOverstep: function (prop) {
if ( this .original[prop] < this .transformed[prop]) {
return this .temp[prop] > this .transformed[prop];
}
return this .temp[prop] < this .transformed[prop];
} ,
_prepare: function () { } ,
_draw: function (frame) { } ,
_drawFrame: function () {
if ( this .isPlaying) {
if ( this .currentFrame < this .frames) {
this ._prepare();
this ._draw( this .temp);
this .currentFrame ++ ;
} else {
// 最后一帧绘制结束状态
this ._draw( this .transformed);
this .stop();
}
}
} ,
_play: function () { } ,
play: function () {
if ( ! this .isPlaying) {
this ._play();
this .isPlaying = true ;
this .timer = setInterval( this ._drawFrame.bind( this ), this .interval);
}
} ,
_stop: function () { } ,
stop: function () {
if ( this .isPlaying) {
this ._stop();
// 回到开始状态
this .isPlaying = false ;
this .currentFrame = 1 ;
Object.extend( this .temp, this .original);
clearInterval( this .timer);
}
} ,
_pause: function () { } ,
pause: function () {
if ( this .isPlaying) {
this ._pause();
this .isPlaying = false ;
clearInterval( this .timer);
}
}
}
清单1 Animation.js
Animation.prototype = {
/* ------------------------------------------------------------------------
| 用途:
| 构造函数
|
| 参数:
| element 将要实现动画效果的元素
| fps 每秒播放帧数
------------------------------------------------------------------------ */
initialize: function (element, fps) {
this .element = $(element);
this .interval = Math.round( 1000 / fps);
this .isPlaying = false ;
this .currentFrame = 1 ;
// 创建一个用于存储中间状态的临时对象
this .temp = { } ;
} ,
/* ------------------------------------------------------------------------
| 用途:
| 子类覆盖该方法,实现自定义的动画补间
------------------------------------------------------------------------ */
_createTweens: function (original, transformed, frames) { } ,
/* ------------------------------------------------------------------------
| 用途:
| 创建动画补间
|
| 参数:
| original 开始状态
| transformed 结束状态
| frames 动画帧数
------------------------------------------------------------------------ */
createTweens: function (original, transformed, frames) {
if ( this .isPlaying) {
this .stop();
}
this ._createTweens(original, transformed, frames);
this .original = original;
this .transformed = transformed;
this .frames = frames;
// 将开始状态拷贝到临时对象
Object.extend( this .temp, original);
} ,
/* ------------------------------------------------------------------------
| 用途:
| 判断临时对象状态是否超出结束状态
|
| 参数:
| prop 状态属性名称
------------------------------------------------------------------------ */
_isOverstep: function (prop) {
if ( this .original[prop] < this .transformed[prop]) {
return this .temp[prop] > this .transformed[prop];
}
return this .temp[prop] < this .transformed[prop];
} ,
_prepare: function () { } ,
_draw: function (frame) { } ,
_drawFrame: function () {
if ( this .isPlaying) {
if ( this .currentFrame < this .frames) {
this ._prepare();
this ._draw( this .temp);
this .currentFrame ++ ;
} else {
// 最后一帧绘制结束状态
this ._draw( this .transformed);
this .stop();
}
}
} ,
_play: function () { } ,
play: function () {
if ( ! this .isPlaying) {
this ._play();
this .isPlaying = true ;
this .timer = setInterval( this ._drawFrame.bind( this ), this .interval);
}
} ,
_stop: function () { } ,
stop: function () {
if ( this .isPlaying) {
this ._stop();
// 回到开始状态
this .isPlaying = false ;
this .currentFrame = 1 ;
Object.extend( this .temp, this .original);
clearInterval( this .timer);
}
} ,
_pause: function () { } ,
pause: function () {
if ( this .isPlaying) {
this ._pause();
this .isPlaying = false ;
clearInterval( this .timer);
}
}
}
Animation类实现了一些公用的管理内部状态的操作,如播放动画、停止动画和暂停动画等。接下来,创建特定的动画变得相当容易了,下面让我们来看一个形状和位置渐变的动画实现,代码如下:
var
ShapeAnimation
=
Class.create();
ShapeAnimation.prototype = Object.extend( new Animation(), {
/* ------------------------------------------------------------------------
| 用途:
| 覆盖父类的空白实现,计算每帧的变化量
------------------------------------------------------------------------ */
_createTweens: function (original, transformed, frames) {
this .xSpan = Math.round((transformed.x - original.x) / frames);
this .ySpan = Math.round((transformed.y - original.y) / frames);
this .wSpan = Math.round((transformed.w - original.w) / frames);
this .hSpan = Math.round((transformed.h - original.h) / frames);
} ,
/* ------------------------------------------------------------------------
| 用途:
| 覆盖父类的空白实现,计算当前的状态。如果超出结束状态,保持结束状态不变
------------------------------------------------------------------------ */
_prepare: function () {
this .temp.x = this ._isOverstep('x') ? this .transformed.x : this .temp.x + this .xSpan;
this .temp.y = this ._isOverstep('r') ? this .transformed.y : this .temp.y + this .ySpan;
this .temp.w = this ._isOverstep('w') ? this .transformed.w : this .temp.w +
ShapeAnimation.prototype = Object.extend( new Animation(), {
/* ------------------------------------------------------------------------
| 用途:
| 覆盖父类的空白实现,计算每帧的变化量
------------------------------------------------------------------------ */
_createTweens: function (original, transformed, frames) {
this .xSpan = Math.round((transformed.x - original.x) / frames);
this .ySpan = Math.round((transformed.y - original.y) / frames);
this .wSpan = Math.round((transformed.w - original.w) / frames);
this .hSpan = Math.round((transformed.h - original.h) / frames);
} ,
/* ------------------------------------------------------------------------
| 用途:
| 覆盖父类的空白实现,计算当前的状态。如果超出结束状态,保持结束状态不变
------------------------------------------------------------------------ */
_prepare: function () {
this .temp.x = this ._isOverstep('x') ? this .transformed.x : this .temp.x + this .xSpan;
this .temp.y = this ._isOverstep('r') ? this .transformed.y : this .temp.y + this .ySpan;
this .temp.w = this ._isOverstep('w') ? this .transformed.w : this .temp.w +
发表评论
评论