• 编写基于Prototype的Javascript动画类


    在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, 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文件中分离出来,使程序结构更清晰可读。

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

  • 相关阅读:
    浅浅的分析下es6箭头函数
    css实现背景半透明文字不透明的效果
    五星评分,让我告诉你半颗星星怎么做
    微信小程序--成语猜猜看
    微信小程序开发中如何实现侧边栏的滑动效果?
    强力推荐微信小程序之简易计算器,很适合小白程序员
    swing _JFileChooser文件选择窗口
    file类简单操作
    序列化对象
    MessageBox_ swt
  • 原文地址:https://www.cnblogs.com/Max/p/631354.html
Copyright © 2020-2023  润新知