案例16——没特效还玩毛个Flash

小S元旦献礼来啦,这次带来了一些比较好玩也常见的Flash特效给各位新人们学习。这次评选10大原创教程,我的几个帖子也入围啦,感觉很开心,多谢一直支持我的朋友们~
     在经过了上一个案例的“洗涤”之后很多爱卿都表示难度太大,看不懂,云里雾里的,那这次我就让难度来个180度大转弯,回到基础,舒服的代码结构来吧。说到Flash特效,第一时间想到的就是补间动画(Tween),在FLASH CS工具中已自带补间动画,你可以在时间轴上方便地添加,不过有时候会很难满足我们的特定需求。在Flash Builder或者其他的一些纯代码IDE中我们不能够像CS工具一样在时间轴上右键添加一个补间动画,那只能通过代码编程的方式来实现,常用的手段就是使用Timer或者侦听ENTER_FRAME时间来做到。不过,相信列位应该对“tweenlite”的大名也有所耳闻,它是一个第三方的类库,提供了我们简便地实现补间动画与缓动的解决方案,通过使用它可以方便快捷地作出漂亮的特效。
    安装方法:首先你要做的是去TweenLite官方网站点击右上角的按钮下载AS3版本的TweenLite类库,下载完毕后得到一个压缩包,其中我们将用到greensock.swc文件。在FB工程目录下新建一个叫做lib目录(名字随便你取),复制粘贴greensock.swc文件到此目录下,之后,右键点击我们的项目目录选择“属性”(Property)
<ignore_js_op>案例16——没特效还玩毛个Flash_第1张图片 
在弹出的属性窗口中选中ActionScript Build Path(ActionScript 构建路径)中,点击右边的Add SWC Folder(添加SWC目录),在输入框中输入我们放置greensock.swc文件的目录lib,点击OK,后我们可以在Build Path libraries窗口下看到我们添加进构建路径的swc,如下图所示:
<ignore_js_op>案例16——没特效还玩毛个Flash_第2张图片 
做完了这一切后FB就把TweenLite类库导进了工程中,我们此时就可以使用TweenLite类库中提供的方法了。
   TweenLite函数以及使用方法:
    详见TweenLite中文详解
    了解了前提知识后让我们使用强大的TweenLite类库来做一些酷酷的特效吧。
减血效果
     减血效果是在格斗游戏中常见的效果(效果展示:点我观看
实现的关键点在于建立两层血条,一层红色一层白色,白色在底层,红色盖在白色上面,它们的宽度高度都一样。当损血时让红色血条的宽度立马减少,之后让白色的血条用TweenLite使之缓缓地减少宽度。让我们来一步步地做吧。
    首先为了方便用户交互,我需要一个按钮类:
MyButton.as:

  1. package com.zuisg.we
  2. {
  3.         import flash.display.Sprite;
  4.         import flash.events.MouseEvent;
  5.         import flash.text.TextField;
  6.         
  7.         public class MyButton extends Sprite
  8.         {
  9.                 public function MyButton( label:String, width:Number, height:Number, UpColor:uint=0x999999, overColor:uint=0x000099 )
  10.                 {
  11.                         var Label:TextField = new TextField();
  12.                         Label.selectable = false;
  13.                         Label.htmlText = label;
  14.                         Label.width = Label.textWidth + 4;
  15.                         Label.height = Label.textHeight + 4;
  16.                         Label.mouseEnabled = false;
  17.                         
  18.                         updateBtn( UpColor, width, height );
  19.                         Label.x = Label.y = 5;
  20.                         this.addChild( Label );
  21.                         this.addEventListener(MouseEvent.ROLL_OVER, function(event:MouseEvent):void{
  22.                                 updateBtn( overColor, width, height);
  23.                         });
  24.                         this.addEventListener(MouseEvent.ROLL_OUT, function(event:MouseEvent):void{
  25.                                 updateBtn( UpColor, width, height);
  26.                         });
  27.                         this.buttonMode = true;
  28.                         this.useHandCursor = true;
  29.                 }
  30.                 
  31.                 private function updateBtn( color:uint, w:Number, h:Number ):void{
  32.                         this.graphics.clear();
  33.                         this.graphics.lineStyle(1);
  34.                         this.graphics.beginFill( color );
  35.                         this.graphics.drawRect( 0, 0, w, h );
  36.                         this.graphics.endFill();
  37.                 }
  38.         }
  39. }
复制代码

一个非常简单按钮类不是吗?我们可以设置按钮的普通状态颜色与鼠标移上去之后显示的颜色。再看主应用程序:
LostLifeEffects.as:

  1. package
  2. {
  3.         import com.greensock.TweenLite;
  4.         import com.zuisg.we.MyButton;
  5.         
  6.         import flash.display.Shape;
  7.         import flash.display.Sprite;
  8.         import flash.events.MouseEvent;
  9.         import flash.text.TextField;
  10.         import flash.text.TextFieldType;
  11.         
  12.         public class LostLifeEffects extends Sprite
  13.         {
  14.                 private var redOne:Shape = new Shape();
  15.                 private var whiteOne:Shape = new Shape();
  16.                 private var w:Number = 200;
  17.                 private var h:Number = 50;
  18.                 private var currentHP:Number;
  19.                 private var hurtNum:TextField;
  20.                 
  21.                 public function LostLifeEffects()
  22.                 {
  23.                         super();
  24.                         initView();
  25.                         initBtn();
  26.                         initText();
  27.                 }
  28.                 
  29.                 private function initView():void{
  30.                         currentHP = w;
  31.                         this.graphics.lineStyle(1);
  32.                         this.graphics.drawRect( 50, 200, w, h );
  33.                         initBar( whiteOne, 0xffffff, w, h );
  34.                         initBar( redOne, 0xff0000, w, h );
  35.                 }
  36.                 
  37.                 private function initBtn():void{
  38.                         var attackBtn:MyButton = new MyButton( "攻击", 50, 30 );
  39.                         addChild( attackBtn );
  40.                         attackBtn.x = 50;
  41.                         attackBtn.y = 50;
  42.                         attackBtn.addEventListener(MouseEvent.CLICK, attack);
  43.                         
  44.                         var resetBtn:MyButton = new MyButton( "重置", 50, 30 );
  45.                         addChild( resetBtn );
  46.                         resetBtn.x = 50;
  47.                         resetBtn.y = 100;
  48.                         resetBtn.addEventListener(MouseEvent.CLICK, reset);
  49.                 }
  50.                 
  51.                 private function initText():void{
  52.                         hurtNum = new TextField();
  53.                         hurtNum.type = TextFieldType.INPUT;
  54.                         hurtNum.restrict = "0-9";
  55.                         hurtNum.x = 150;
  56.                         hurtNum.y = 100;
  57.                         hurtNum.height = 20;
  58.                         hurtNum.width = 40;
  59.                         hurtNum.border = true;
  60.                         addChild( hurtNum );
  61.                         
  62.                         var description:TextField = new TextField();
  63.                         description.text = "请输入一次扣除的血量:0-200";
  64.                         description.selectable = false;
  65.                         description.x = 120;
  66.                         description.y = 60;
  67.                         description.height = description.textHeight + 4;
  68.                         description.width = description.textWidth + 4;
  69.                         addChild( description );
  70.                 }
  71.                 
  72.                 private function initBar( bar:Shape, color:uint, width:Number, height:Number ):void{
  73.                         bar.graphics.beginFill( color );
  74.                         bar.graphics.drawRect(0, 0, width, height);
  75.                         bar.graphics.endFill();
  76.                         addChild( bar );
  77.                         bar.x = 50;
  78.                         bar.y = 200;
  79.                 }
  80.                 
  81.                 private function updateHPBar( reduceNum:Number ):void{
  82.                         currentHP -= reduceNum;
  83.                         if( currentHP <= 0 )currentHP = 0;
  84.                         redOne.width = currentHP;//红色血条宽度立即减少
  85.                         TweenLite.to( whiteOne, 2, {width:currentHP} );//白色血条宽度缓慢减少
  86.                 }
  87.                 
  88.                 private function attack(event:MouseEvent):void{
  89.                         updateHPBar( Number(hurtNum.text) );
  90.                 }
  91.                 
  92.                 private function reset(event:MouseEvent):void{
  93.                         currentHP = redOne.width = whiteOne.width = w;
  94.                 }
  95.         }
  96. }
复制代码

代码代表我的心,就按我刚才讲的,红色血条得摆在白色血条的上面把它挡住,所以红色血条的addChild要写在白色血条addChild语句之后,你还必须保证它们的宽度一致。当减血时红色血条宽度立即减少,白色血条则缓慢地减少,使用TweenLite.to方法。这个方法第一个参数是缓动对象,我设置为白色血条;第二个参数是持续时间,设置为2秒;第三个参数是改变的属性,我设置width属性,冒号后面设置了要改变到的值为currentHP。怎么样,很简单吧?TweenLite很好用吧?!
攻击魔法特效
在RPG游戏的回合制战斗画面中经常能看见这样的效果(效果展示:点我观看
     在这个例子里面存在三个动画元素:玩家人物,敌人,魔法;在案例10中我们提到过一种用代码制作动画的方式,就是在美术提供给我们一张素材图片后我们通过切割图片来获取动画每一帧所需图片,之后把它们拼起来形成一个动画。我手头上得到的图片素材如下:
<ignore_js_op>案例16——没特效还玩毛个Flash_第3张图片<ignore_js_op>案例16——没特效还玩毛个Flash_第4张图片 

2010-12-30 14:22:12 上传
下载附件 (162.12 KB)
 


    为了能够快速用代码制作动画,避免重写重复代码,我把制作动画的代码写成了两个类:动画对象类(AnimationObject)以及动画工厂类(AnimationFactory)。

 

AnimationObject.as:

  1. package com.zuisg.we
  2. {
  3.         import flash.display.Bitmap;
  4.         import flash.display.BitmapData;
  5.         import flash.events.Event;
  6.         import flash.events.TimerEvent;
  7.         import flash.utils.Timer;
  8.         /**
  9.          * 动画对象
  10.          * @author S_eVent
  11.          * 
  12.          */        
  13.         public class AnimationObject extends Bitmap
  14.         {
  15.                 public static const PLAY_OVER_EVENT:String = "play over event";
  16.                 
  17.                 public var imgList:Array = new Array();
  18.                 private var timer:Timer = new Timer( 100 );
  19.                 private var currentFrame:int = 0;
  20.                 private var _stopIndex:int = 0;
  21.                 
  22.                 public function AnimationObject(bitmapData:BitmapData=null, pixelSnapping:String="auto", smoothing:Boolean=false)
  23.                 {
  24.                         super(bitmapData, pixelSnapping, smoothing);
  25.                         timer.addEventListener(TimerEvent.TIMER, onTimer);
  26.                 }
  27.                 /**
  28.                  * 播放动画 
  29.                  * 
  30.                  */                
  31.                 public function play():void{
  32.                         timer.reset();
  33.                         timer.start();
  34.                 }
  35.                 /**
  36.                  * 停止动画 
  37.                  * 
  38.                  */                
  39.                 public function stop():void{
  40.                         timer.stop();
  41.                 }
  42.                 /**
  43.                  * 设置动画帧率 
  44.                  * @param value 多少时间切换一副图片,单位毫秒
  45.                  * 
  46.                  */                
  47.                 public function set frameRate( value:int ):void{
  48.                         timer.delay = value;
  49.                 }
  50.                 
  51.                 /**
  52.                  * 设置动画播放次数 ,若为0,则不断运行,默认为0
  53.                  * @param value 动画播放次数,单位:次
  54.                  * 
  55.                  */                
  56.                 public function set repeatCount( value:int ):void{
  57.                         timer.repeatCount = value * imgList.length;
  58.                 }
  59.                 
  60.                 /**
  61.                  * 设置静止时动画所处帧 
  62.                  * @param value
  63.                  * 
  64.                  */                
  65.                 public function set stopIndex( value:int ):void{
  66.                         _stopIndex = value;
  67.                         currentFrame = _stopIndex;
  68.                         this.bitmapData = imgList[_stopIndex];
  69.                 }
  70.                 
  71.                 private function onTimer(event:TimerEvent):void{
  72.                         this.bitmapData = imgList[currentFrame];
  73.                         currentFrame ++;
  74.                         if( currentFrame > imgList.length - 1 ){
  75.                                 currentFrame = _stopIndex;
  76.                                 this.bitmapData = imgList[_stopIndex];
  77.                                 dispatchEvent( new Event(PLAY_OVER_EVENT) );
  78.                         }
  79.                 }
  80.         }
  81. }
复制代码

其中的imgList数组存放动画各帧的图片,当调用play方法时timer开始计时,每一次Timer事件触发时都会改变AnimationObject的bitmapData使外观改变以形成动画效果。你也可以通过stopIndex设置动画对象AnimationObject静止时的外观。在Timer事件处理函数中我们会检查当前播放帧是否已是imgList数组的最后一张图片,若是,则表示一次动画播放已结束,会把AnimationObject的bitmapData置回静止时的外观,并抛出一个PLAY_OVER_EVENT事件。为啥要抛事件呢?为了某些动画在播放一次后能被正确地从舞台上移除啊,就像我们此案例中的魔法效果。(此类的代码在案例10中32楼也有写到)
       接下来写一个动画工厂类:
AnimationFactory.as:

  1. package com.zuisg.we
  2. {
  3.         import flash.display.BitmapData;
  4.         import flash.geom.Point;
  5.         import flash.geom.Rectangle;
  6.         
  7.         /**
  8.          * 动画工厂 
  9.          * @author S_eVent
  10.          * 
  11.          */
  12.         public class AnimationFactory
  13.         {
  14.                 
  15.                 /**
  16.                  * 切图 
  17.                  * @param source         切割源
  18.                  * @param width                   切块宽度
  19.                  * @param height         切块高度
  20.                  * @param row                 欲切割行
  21.                  * @param col                欲切割列
  22.                  * @return                         
  23.                  * 
  24.                  */                
  25.                 public static function Cut( source:BitmapData, width:Number, height:Number, row:int, col:int ):BitmapData{
  26.                         var result:BitmapData = new BitmapData( width, height );
  27.                         result.copyPixels( source, 
  28.                                 new Rectangle( col * width, row * height, width, height ), new Point( 0, 0 ) );
  29.                         return result;
  30.                 }
  31.                 /**
  32.                  * 制作动画 
  33.                  * @param source                动画图片源
  34.                  * @param row                        切割图片行
  35.                  * @param length                切割图片行中的数量
  36.                  * @param width                        切割图片宽度
  37.                  * @param height                切割图片高度
  38.                  * @param frameRate                动画帧频
  39.                  * @return 
  40.                  * 
  41.                  */        
  42.                 public static function ProductAnimation( source:BitmapData, row:int, length:int, 
  43.                                                                                                  width:Number, height:Number, frameRate:int=100 ):AnimationObject{
  44.                         var result:AnimationObject = new AnimationObject();
  45.                         for( var i:int=0; i<length; i++ ){
  46.                                 result.imgList[i] = Cut( source, width, height, row, i );
  47.                         }
  48.                         result.frameRate = frameRate;
  49.                         return result;
  50.                 }
  51.         }
  52. }
复制代码

这是一个工具类,其中只有静态的方法,所以实例化它没有任何意义,我们需要的只是用它内部的方法而已。这两个方法的原理若是不懂,再回头去研究下案例10吧。
    好了,开始在主应用文件里面做我们的案例吧。

  1. package
  2. {
  3.         import com.greensock.TweenLite;
  4.         import com.zuisg.we.AnimationFactory;
  5.         import com.zuisg.we.AnimationObject;
  6.         import com.zuisg.we.MyButton;
  7.         
  8.         import flash.display.Bitmap;
  9.         import flash.display.BitmapData;
  10.         import flash.display.Sprite;
  11.         import flash.events.Event;
  12.         import flash.events.MouseEvent;
  13.         import flash.events.TimerEvent;
  14.         import flash.utils.Timer;
  15.         
  16.         public class attackEffect extends Sprite
  17.         {
  18.                 [Embed(source="assets/Fire.png")]
  19.                 private var Fire:Class;
  20.                 [Embed(source="assets/Actor1.png")]
  21.                 private var Actor:Class;
  22.                 private var magicAnimation:AnimationObject; //动画效果
  23.                 private var player:AnimationObject; //玩家形象
  24.                 private var enermy:AnimationObject; //敌人形象
  25.                 private var shakeTimer:Timer = new Timer( 50, 3 );//屏幕震动计时器
  26.                 
  27.                 private var bakground:Sprite = new Sprite();
  28.                 
  29.                 public function attackEffect()
  30.                 {
  31.                         super();
  32.                         initBg();
  33.                         initAnimation();
  34.                         initBtn();
  35.                         shakeTimer.addEventListener(TimerEvent.TIMER, onTimer); //侦听震动计时器,每次触发后移动背景
  36.                         shakeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onComplete);//侦听计时完成,触发后停止震动计时器
  37.                 }
  38.                 
  39.                 private function initBg():void{
  40.                         bakground.graphics.beginFill(0x000000);
  41.                         bakground.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
  42.                         bakground.graphics.endFill();
  43.                         addChild( bakground );
  44.                 }
  45.                 
  46.                 private function initAnimation():void{
  47.                         initMagicAnimation();
  48.                         initActor();
  49.                 }
  50.                 
  51.                 private function initMagicAnimation():void{
  52.                         var fireBMD:BitmapData = ( new Fire() as Bitmap ).bitmapData;
  53.                         magicAnimation = AnimationFactory.ProductAnimation( fireBMD, 0, 5, 192, 192 );
  54.                         //第0行的5张图片还不够用,从第1行再拿一张图片进来
  55.                         magicAnimation.imgList.push( AnimationFactory.Cut( fireBMD, 192, 192, 1, 0 ) );
  56.                         magicAnimation.repeatCount = 1;//设置动画只播放一次
  57.                         magicAnimation.stopIndex = 0;//设置动画停止外观为第0帧
  58.                         magicAnimation.width = magicAnimation.height = 64;
  59.                 }
  60.                 
  61.                 private function initActor():void{
  62.                         var actorBMD:BitmapData = ( new Actor() as Bitmap ).bitmapData;
  63.                         player = AnimationFactory.ProductAnimation( actorBMD, 2, 3, 32, 32 );
  64.                         bakground.addChild( player );
  65.                         player.x = 20;
  66.                         player.y = 50;
  67.                         player.play();
  68.                         
  69.                         enermy = AnimationFactory.ProductAnimation( actorBMD, 5, 3, 32, 32 );
  70.                         bakground.addChild( enermy );
  71.                         enermy.x = 400;
  72.                         enermy.y = 50;
  73.                         enermy.play();
  74.                 }
  75.                 
  76.                 private function initBtn():void{
  77.                         var btn:MyButton = new MyButton( "Fuck him!", 90, 20 );
  78.                         addChild( btn );
  79.                         btn.x = 80;
  80.                         btn.y = 20;
  81.                         btn.addEventListener(MouseEvent.CLICK, attack);
  82.                 }
  83.                 
  84.                 private function attack( event:MouseEvent ):void{
  85.                         magicAnimation.x = player.x ;
  86.                         magicAnimation.y = player.y - 16 ;
  87.                         if( !bakground.contains(magicAnimation) ){
  88.                                 bakground.addChild( magicAnimation );
  89.                         }
  90.                         if( !magicAnimation.hasEventListener( AnimationObject.PLAY_OVER_EVENT ) ){
  91.                                 magicAnimation.addEventListener( AnimationObject.PLAY_OVER_EVENT, onPlayOver );
  92.                         }
  93.                         flying();
  94.                 }
  95.                 
  96.                 private function flying():void{
  97.                         TweenLite.to( magicAnimation, 2, { x:enermy.x - 16, onComplete:Explode } );
  98.                 }
  99.                 
  100.                 private function Explode():void{
  101.                         magicAnimation.play();
  102.                         shake();//震动屏幕
  103.                 }
  104.                 
  105.                 private function onPlayOver( event:Event ):void{
  106.                         magicAnimation.stop();
  107.                         bakground.removeChild(magicAnimation);
  108.                 }
  109.                 
  110.                 private function shake():void{
  111.                         shakeTimer.reset();
  112.                         shakeTimer.start();
  113.                 }
  114.                 
  115.                 private function onTimer( event:TimerEvent ):void{
  116.                         if( shakeTimer.currentCount == 1 ){
  117.                                 bakground.x -= 5;
  118.                         }else if( shakeTimer.currentCount == 2 ){
  119.                                 bakground.x += 10;
  120.                         }
  121.                 }
  122.                 
  123.                 private function onComplete( event:TimerEvent ):void{
  124.                         bakground.x -= 5;
  125.                         shakeTimer.stop();//停止震动
  126.                 }
  127.         }
  128. }
复制代码

这里涉及到了对动画对象以及动画工厂的使用,有了它们两,做动画不过是几句代码的事情,我们代码中用到的动画都是根据需要从上面给出的两个图片素材中切割出来的。两个人物是一停不停地在原地动的,所以不必设置它的repeatCount,调用一次play方法后就会不停地播放动画了。魔法效果则播放一次即可。
    当点击攻击按钮,即“Fuck him!”按钮后我们的主人公就会发出一团火去攻击他的老板,因为他老板拖欠工资造成了他的愤怒之火。此时我们把魔法效果对象添加到舞台上,然后调用fly方法让魔法飞出去。在fly方法中依旧使用TweenLite.to方法制作缓动效果,让魔法效果对象的x移动到敌人位置;我们还监听了TweenLite的onComplete事件,当TweenLite缓动效果播放完成后会触发此事件,用Explor函数作为事件侦听函数,在此函数中我们让火焰爆破,即播放魔法效果对象中的动画。此外,为了表现敌人受伤的效果,做了一个震动效果,震动效果的制作并不复杂,用一个Timer来左右移动背景即可。

 

淡入淡出效果
     相当容易实现的一个效果,只需要在ROLL_OVER的时候将ROLL_OVER到的对象alpha缓动改变到1,在ROLL_OUT的时候将ROLL_OUT出的对象alpha缓动改变到非1的值就可以了,使用Tweenlite.to方法非常容易做到。url=http://www.iamsevent.com/upload/HighLightEffect.swf
残影
     还记得之前案例中的烟花特效吗?当初那个是基于像素的残影效果,现在来看看基于复制显示对象的,你需要做的,仅仅是复制5次同一个对象放在一起,然后点击某一位置后并不是把全部残影都同一时间移动到鼠标点击位置,而是将它们的出发时间错开,它们的出发时间依次相隔0.1秒。

  1. public class ShadowEffect extends Sprite
  2.         {
  3.                 private var role:Sprite;
  4.                 private var shadowNum:int = 5;
  5.                 private var shadows:Array;
  6.                 private var tweens:Array = new Array();
  7.                 private var timeLine:TimelineLite;
  8.                 
  9.                 public function ShadowEffect()
  10.                 {
  11.                         shadows = new Array( shadowNum );
  12.                         initView();
  13.                         stage.addEventListener( MouseEvent.CLICK, onClick );
  14.                 }
  15.                 
  16.                 private function initView():void{
  17.                         role = new Sprite();
  18.                         role.graphics.beginFill(0);
  19.                         role.graphics.drawCircle(0, 0, 20);
  20.                         role.graphics.endFill();
  21.                         var w:Number = role.width;
  22.                         var h:Number = role.height;
  23.                         //创建残影
  24.                         for( var i:int=1; i<=shadowNum; i++ ){
  25.                                 var bmd:BitmapData = new BitmapData( w, h, true, 0 );
  26.                                 bmd.draw( role, new Matrix(1,0,0,1, w/2, h/2 ) );
  27.                                 var bm:Bitmap = new Bitmap( bmd );
  28.                                 bm.alpha = i / shadowNum;//残影透明度依次增加
  29.                                 bm.x = 100;//一开始叠加在一起
  30.                                 bm.y = 100;
  31.                                 addChildAt(bm, 0);
  32.                                 shadows[i] = bm;
  33.                         }
  34.                 }
  35.                 
  36.                 private function moveTo( x:Number, y:Number ):void{
  37.                         
  38.                         //移动时注意顺序,先移动透明度最大的,然后每隔0.1秒移动下一个残影
  39.                         for( var i:int=shadowNum; i>=1; i-- ){
  40.                                 
  41.                                 TweenLite.delayedCall( (shadowNum - i) * 0.1, tempFuc, [ shadows[i], x, y ] );
  42.                         }
  43.                 }
  44.                 
  45.                 private function tempFuc( target:Object, x:Number, y:Number ):void{
  46.                         TweenLite.to( target, 0.5, {x:x, y:y} );
  47.                 }
  48.                 
  49.                 private function onClick(event:MouseEvent):void{
  50.                         moveTo( stage.mouseX, stage.mouseY );
  51.                 }
  52.         }
复制代码

最终结果如下:http://www.iamsevent.com/upload/ShadowEffect.swf)
扭曲
     列位仙家也许在电视中看到过穿梭时光隧道时时光隧道的模样,一般都是一个扭曲了的图形,那么在Flash里面应该怎样做到这样的效果呢?先看示例:
http://www.iamsevent.com/upload/WarpingEffect.swf

      实现思路:要让图形产生扭曲,则需要让每一个像素都产生“扭曲”。看下面一幅图:
<ignore_js_op>案例16——没特效还玩毛个Flash_第5张图片 
     对于一个像素a,要让它产生按圆心“扭曲”的效果,就需要让它的颜色值等于按圆心旋转角度A后所到达的b处的颜色。遍历位图中所有像素并对它们进行像素偏移后即可得到扭曲效果。一起来看看源码:

  1. public class WarpingEffect extends Sprite
  2.         {
  3.                 private var tf:TextField = new TextField();//源文本
  4.                 private var mirror:BitmapData;//文本镜像位图数据
  5.                 private var mirrorBMP:Bitmap;//文本镜像位图
  6.                 private var radius:Number = 300;//扭曲半径
  7.                 private var center:Point = new Point( 200, 200 );//扭曲中心
  8.                 private var referenceBMD:BitmapData;//参考位图数据
  9.                 
  10.                 public function WarpingEffect()
  11.                 {
  12.                         initText();
  13.                         generateMirror();
  14.                         warp( 5 );
  15.                 }
  16.                 
  17.                 private function initText():void
  18.                 {
  19.                         tf.width = 400;
  20.                         tf.height = 400;
  21.                         tf.wordWrap = true;
  22.                         tf.multiline = true;
  23.                         tf.border = true;
  24.                         tf.defaultTextFormat = new TextFormat("Arial", 24);
  25.                         for(var i:int = 0; i < 340; i++)
  26.                         {
  27.                                 tf.appendText(String.fromCharCode(65 + Math.random() * 25));
  28.                         }
  29.                         
  30.                 }
  31.                 
  32.                 private function generateMirror():void
  33.                 {
  34.                         referenceBMD = new BitmapData( tf.width, tf.height, true, 0 );
  35.                         referenceBMD.draw( tf );//为了保证在像素颜色取样过程中不会产生差错,我们需要这个参考数据
  36.                         mirror = new BitmapData( tf.width, tf.height, true, 0 );
  37.                         mirror.draw( tf );
  38.                         
  39.                         mirrorBMP = new Bitmap(mirror);
  40.                         addChild( mirrorBMP );
  41.                         mirrorBMP.x = 200;
  42.                         mirrorBMP.y = 100;
  43.                 }
  44.                 
  45.                                 
  46.                 /**
  47.                  * 扭曲图像 
  48.                  * @param twist        扭曲程度
  49.                  * 
  50.                  */                
  51.                 private function warp( twist:Number ):void
  52.                 {
  53.                         mirror.lock();
  54.                         
  55.                         var maxW:Number = mirrorBMP.x + mirrorBMP.width;
  56.                         var maxH:Number = mirrorBMP.y + mirrorBMP.height;
  57.                         
  58.                         var dist:Number;//当前计算点与扭曲中心点直接的距离
  59.                         var dx:Number;//当前计算点和扭曲中心点的x距离
  60.                         var dy:Number;//当前计算点和扭曲中心点的x距离
  61.                         var angle:Number;//当前计算点和扭曲中心点的夹角
  62.                         var newPos:Point;//扭曲后的新颜色所在像素位置
  63.                         var newColor:uint;//扭曲后的新颜色
  64.                         
  65.                         for( var i:int=0; i<mirror.width; i++ )
  66.                         {
  67.                                 for( var j:int=0; j<mirror.height; j++ )
  68.                                 {
  69.                                         dist = distance( new Point(i,j), center );
  70.                                         if( dist < radius )//只有在扭曲圆范围内的像素才会被扭曲
  71.                                         {
  72.                                                 dx = i - center.x;
  73.                                                 dy = j - center.y;
  74.                                                 angle = Math.atan2( dy, dx );
  75.                                                 //对夹角进行偏移,使用弧度介于0和twist之间,在偏移圆的最外围和圆心处不偏移
  76.                                                 angle += Math.sin( dist / radius * Math.PI ) * twist;                                                                                                newPos = new Point( center.x + Math.cos( angle ) * dist,
  77.                                                         center.y + Math.sin( angle ) * dist );
  78.                                                                                                //由于mirror设置每个像素的过程是顺序执行而不是并行执行的,所以
  79.                                                 //随着晚被设置的像素得到的颜色值将会产生错误,因此我们不能从mirror
  80.                                                 //自身进行像素取样,而应该到参考数据源中取样
  81.                                                 newColor = referenceBMD.getPixel32( newPos.x, newPos.y );                                                                                mirror.setPixel32( i, j, newColor );
  82.                                         }
  83.                                         
  84.                                 }
  85.                         }
  86.                         mirror.unlock();
  87.                         
  88.                 }
  89.                 
  90.                 private function distance( pos1:Point, pos2:Point ):Number
  91.                 {
  92.                         var xDist:Number = pos1.x - pos2.x;
  93.                         var yDist:Number = pos1.y - pos2.y;
  94.                         return Math.sqrt( xDist * xDist + yDist * yDist );
  95.                 }
  96.         }
复制代码

这样做仅适合做到静态图像的扭曲,如果要把图片做成一个扭曲动画,推荐使用PixelBender工具来实现,因为它对像素的处理是出奇的高效。具体做法可参考《Flash Actionscript3.0高级动画编程》第九章——Pixel Bender。

 

倒影
网站上随处可见的倒影效果,网上也存在很多版本的源码下载,看看效果演示:http://www.iamsevent.com/upload/ReflectEffect.swf
     首先我们需要了解一下渐变的绘制方法。绘制渐变主要依靠绘图API中的graphics.beginGradientFill()方法。此方法的签名如下:
public function beginGradientFill(type:String, colors:Array, alphas:Array, ratios:Array, matrix:Matrix = null, spreadMethod:String = "pad", interpolationMethod:String = "rgb", focalPointRatio:Number = 0):void 
参数很多,如果我们只是简单地绘制一个渐变的话就只需要用到前5个参数即可:
type:String — 用于指定要使用哪种渐变类型的 GradientType 类的值:GradientType.LINEAR 或 GradientType.RADIAL。PS:后者一般用在圆形填充,我们这里只填充一个矩形,所以选择第一个类型。 
  
colors:Array — 要在渐变中使用的 RGB 十六进制颜色值数组(例如,红色为 0xFF0000,蓝色为 0x0000FF,等等)。 可以至多指定 15 种颜色。 对于每种颜色,请确保在 alphas 和 ratios 参数中指定对应的值。  
  
alphas:Array — colors 数组中对应颜色的 alpha 值数组;有效值为 0 到 1。 如果值小于 0,则默认值为 0。 如果值大于 1,则默认值为 1。  
  
ratios:Array — 颜色分布比例的数组;有效值为 0 到 255。 该值定义 100% 采样的颜色所在位置的宽度百分比。 值 0 表示渐变框中的左侧位置,255 表示渐变框中的右侧位置。PS:具体解释还是看帮助文档吧

matrix:Matrix (default = null) — 一个由 flash.geom.Matrix 类定义的转换矩阵。 flash.geom.Matrix 类包括 createGradientBox() 方法,通过该方法可以方便地设置矩阵,以便与 beginGradientFill() 方法一起使用。 

下面我们来看看一个简单的渐变填充例子:

  1. var _mask:Shape = new Shape();
  2. var G:Graphics = _mask.graphics;
  3. G.clear();
  4. var m:Matrix = new Matrix();
  5. m.createGradientBox( 150, 100, Math.PI / 2 );
  6. G.beginGradientFill(GradientType.LINEAR, [0x0000ff,0x0000ff], [0,1], [0,255], m);
  7. G.drawRect( 0, 0,150,100 );
  8. G.endFill();
复制代码

首先我们创建了一个Matrix矩阵并使用createGradientBox方法指定了在接下来进行的渐变填充的范围以及方向,默认填充方向是从左至右,我们给createGradientBox方法的第三个参数设置了Math.PI / 2即90度,就是让它填充的方向改为由上自下。接下来在beginGradientFill方法中我们设置了渐变填充所需的参数:线性绘制类型;填充色由0x0000ff蓝色变到0x0000ff蓝色;透明度由0变到1;绘制矩阵为m。最后我们使用drawRect方法绘制一个矩形。最终运行结果如下图所示:
<ignore_js_op>案例16——没特效还玩毛个Flash_第6张图片 
看起来是不是有点倒影的那种“越远越模糊”的感觉?
但是我们的倒影总不可能只映射一个由绘图API画出来的简单的形状,我们需要映射一个图像。对于一个图像我们要想绘制其全貌,首选的办法就是bitmapData.draw()方法。但是绘制出来后并不具备“越远越模糊”的感觉,这时就需要使用一个绘制了渐变填充的图像作为图像的遮障(mask),看看代码如何实现:

  1. var m:Matrix = new Matrix();
  2. var bmd:BitmapData = new BitmapData( target.width, target.height, true, 0 );
  3. m.scale(target.scaleX, target.scaleY);//缩放BitmapData将draw的目标大小以匹配显示对象当前宽高
  4. bmd.draw( target );
  5. _shandow = new Bitmap( bmd );
  6. //必须将遮障与被遮障双方都设置为缓存为位图,不然无法实现遮障
  7. _shandow.cacheAsBitmap = true;
  8. _mask.cacheAsBitmap = true;
  9. addChild( _shandow );
  10. addChild( _mask );
  11. var G:Graphics = _mask.graphics;
  12. G.clear();
  13. var m:Matrix = new Matrix();
  14. m.createGradientBox( _shandow.width, _shandow.height, Math.PI / 2 );
  15. G.beginGradientFill(GradientType.LINEAR, [0x0000ff,0x0000ff], [0,1], [0,255], m);
  16. G.drawRect( 0, 0, _shandow.width, _shandow.height );
  17. G.endFill();
  18. _shandow.mask = _mask;
复制代码

这里值得注意的是必须设置遮障与被遮障双发的cacheAsBitmap为true,不然无法实现遮障效果。而且在使用bitmapData对象draw目标的时候需要使用一个缩放后的矩阵作为参数,为什么呢?假设我将画的对象原始尺寸为100*100,现在我把它尺寸设置为50*50后用一个bitmapData对象去draw它,此时draw的参考图像仍然是100*100的尺寸,因此最终draw出来的结果图像将只有原先的一半。所以需要使用一个缩放后的矩阵作为参数来匹配绘画结果与当前绘画目标尺寸一致。最后我们只需要把绘制出来的倒影倒置过来然后放到被倒影目标的下边缘处即可。

  1. scaleY = -1;//倒置
  2. x = target.x;
  3. y = target.y + target.height + _shandow.height;//置于下边缘处
复制代码

在我给出的源码包中已经写好了一个Reflect类,列位爱卿可以拿去使用,你可以直接设置倒影的target后调用render()方法把倒影绘制出来,也可以通过设置distance来改变倒影的长度。enjoy it~!

全部源码: <ignore_js_op> src.rar (389.62 KB, 下载次数: 504) 
持续更新中……

你可能感兴趣的:(Flash)