随笔-34  评论-1965  文章-0  trackbacks-0

这段时间身体欠佳,经常头晕。医生说并无大碍,可我服了药也不见有多少好转。因此我很久没有更新Blog了。

针对大家关于Struts 2的问题,我正着手开发一个应用程序例子。这个例子以ASP.NET的“Personal Web Site Stater Kit”应用程序作为蓝本,采用“Spring 2 + Hiberante 3 + Struct 2”架构(姑且称之:-)),并且会以“prototype+DWR”为基础实现AJAX。

在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类实现了一些公用的管理内部状态的操作,如播放动画、停止动画和暂停动画等。接下来,创建特定的动画变得相当容易了,下面让我们来看一个形状和位置渐变的动画实现,代码如下:

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 + this .wSpan;
       
this .temp.h = this ._isOverstep('h') ? this .transformed.h : this .temp.h + this .hSpan;
   }
,
   
   
/* ------------------------------------------------------------------------
    | 用途:
    |    覆盖父类的空白实现,刷新元素外观
    ------------------------------------------------------------------------
*/

   _draw:
function (frame) {
       
var x = frame.x + 'px';
       
var y = frame.y + 'px';
       
var w = frame.w + 'px';
       
var h = frame.h + 'px';        
       
       Element.setStyle(
this .element, { left: x, top: y, width: w, height: h } );
   }

   
}
);
清单2 ShapeAnimation.js

ShapeAnimation类继承Animation类,并覆盖了其中的某些方法。最后,让我们创建HTML文件,测试一下这个ShapeAnimation是否可以正确工作。代码如下:

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html xmlns ="http://www.w3.org/1999/xhtml" >
< head >
   
< title > ShapeAnimation Test </ title >

   
< script type ="text/javascript" src ="prototype-1.4.0.js" ></ script >
   
< script type ="text/javascript" src ="Animation.js" ></ script >
   
< script type ="text/javascript" src ="ShapeAnimation.js" ></ script >

   
< script type ="text/javascript" >  
       
var animation;  
        
        Event.observe(window, 'load', init,
false );
        
       
function init() {
           
var clip = $('clip');
           
var pos = Position.cumulativeOffset(clip);            
            
            animation
= new ShapeAnimation(clip, 12 );
            animation.createTweens( { x: pos[
0 ], y: pos[ 1 ], w: 100 , h: 75 },
                                    { x:
100 , y: 100 , w: 200 , h: 200 },
                                   
24 );
        }
        
       
function play() {            
            animation.play();            
        }
        
       
function stop() {
            animation.stop();     
        }
        
       
function pause() {
            animation.pause();    
        }
   
</ script >
</ head >
< body >
   
< input type ="button" onclick ="play()" value ="Play" />
   
< input type ="button" onclick ="stop()" value ="Stop" />
   
< input type ="button" onclick ="pause()" value ="Pause" />< br />
   
< br />
   
< img src ="thumb.jpg" alt ="Thumb" id ="clip" style ="left: 13px; position: absolute; top: 52px;" />
</ body >
</ html >
清单3 ShapeAnimationTest.htm

分别在IE或Firefox中打开ShapeAnimationTest.htm,播击“Play”、“Stop”和“Pause”按钮工作正常。

举一反三

上述例子,我创建了形状动画类。有了Animation类作为基类,当然我可以容易地创建更多的动画类。下面我再举一个裁剪动画示例。代码如下:

var ClipAnimation = Class.create();
ClipAnimation.prototype
= Object.extend( new Animation(), {
  
   _createTweens:
function (original, transformed, frames) {
       
this .tSpan = Math.round((transformed.t - original.t) / frames);
       
this .rSpan = Math.round((transformed.r - original.r) / frames);
       
this .bSpan = Math.round((transformed.b - original.b) / frames);
       
this .lSpan = Math.round((transformed.l - original.l) / frames);
   }
,
   
   _prepare:
function ()
       
this .temp.t = this ._isOverstep('t') ? this .transformed.t : this .temp.t + this .tSpan;
       
this .temp.r = this ._isOverstep('r') ? this .transformed.r : this .temp.r + this .rSpan;
       
this .temp.b = this ._isOverstep('b') ? this .transformed.b : this .temp.b + this .bSpan;
       
this .temp.l = this ._isOverstep('l') ? this .transformed.l : this .temp.l + this .lSpan;
   }
,
   
   _draw:
function (frame) {
       
var clipStyle = 'rect(' + frame.t + 'px ';
       clipStyle
= clipStyle + frame.r + 'px ';
       clipStyle
= clipStyle + frame.b + 'px ';
       clipStyle
= clipStyle + frame.l + 'px)';    
       
       Element.setStyle(
this .element, { clip: clipStyle } );
   }

   
}
);
清单4 ClipAnimation.js

测试文件代码如下:

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html xmlns ="http://www.w3.org/1999/xhtml" >
< head >
   
< title > Untitled Page </ title >

   
< script type ="text/javascript" src ="prototype-1.4.0.js" ></ script >
   
< script type ="text/javascript" src ="Animation.js" ></ script >
   
< script type ="text/javascript" src ="ClipAnimation.js" ></ script >

   
< script type ="text/javascript" >  
       
var animation;  
        
        Event.observe(window, 'load', init,
false );
        
       
function init() {
           
var clip = $('clip');
           
var pos = Position.cumulativeOffset(clip);
           
var dimensions = Element.getDimensions(clip);
            
            animation
= new ClipAnimation(clip, 12 );
            animation.createTweens( { t:
0 , r: dimensions.width, b: Element.getHeight(clip), l: 0 },
                                    { t:
0 , r: dimensions.width, b: 0 , l: 0 },
                                   
24 );
        }
        
       
function play() {            
            animation.play();
        }
        
       
function stop() {
            animation.stop();     
        }
        
       
function pause() {
            animation.pause();    
        }
   
</ script >
</ head >
< body >
   
< input type ="button" onclick ="play()" value ="Play" />
   
< input type ="button" onclick ="stop()" value ="Stop" />
   
< input type ="button" onclick ="pause()" value ="Pause" />< br />
   
< br />
   
< img src ="thumb.jpg" alt ="Thumb" id ="clip" style ="left: 13px; position: absolute; top: 52px;" />
</ body >
</ html >
清单5 ClipAnimationTest.htm

总结

Prototype实现了部分的面向对象,对常用的操作提供了方便的封装。这样我们可以编写具有更高可重性的Javascript代码,将实现重HTML文件中分离出来,使程序结构更清晰可读。

点击以下链接下载示例代码

posted on 2007-01-26 15:06 Max 阅读(5628) 评论(6)  编辑  收藏 所属分类: 方法与技巧(Tips & tricks)

评论:
# re: 编写基于Prototype的Javascript动画类 2007-01-26 16:47 | Brian
可以用SVG绘制矢量图  回复  更多评论
  
# re: 编写基于Prototype的Javascript动画类 2007-01-26 17:16 | Max
@Brian
虽然可以使用SVG绘制矢量图,但目前还没有浏览器内置支持SVG,所以要使用它的话,必须安装插件。  回复  更多评论
  
# re: 编写基于Prototype的Javascript动画类 2007-08-09 10:09 | liaolliso
firefox支持的   回复  更多评论
  
# re: 编写基于Prototype的Javascript动画类 2008-06-03 12:10 | ysaas
firefox浏览器好垃圾的,特别是对中文网站,显示样式都不正确  回复  更多评论
  
# re: 编写基于Prototype的Javascript动画类 2008-09-19 13:10 | go tu
@ysaas

要不就是你垃圾FF怎么不支持中文网站啦,
显示样式都不正确都是写那个网站的人太垃圾了。  回复  更多评论
  
# re: 编写基于Prototype的Javascript动画类 2008-09-23 14:26 | fcrpg2008
@go tu
说得好!!
Firefox目前是业界支持到CSS3.0的标准浏览器,你说的看到很多中文网站界面一踏糊涂,那是写网站的人失败。  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: