Flex的動畫效果與變換!(二)

weixin_30488085發表於2020-04-06
原文出自

在上篇文章《Flex的動畫效果與變換(一)》中講到了使用Flex系統裡面自帶的一些動來效果的使用,但很多開發者都並不滿足Flex裡提供的簡單的漸變大小,透明,移動,遮罩等的效果,如果是Flash的開發者的話,更不用說了,在Flash,多數人都是隨意的製作一些動畫效果等,而且形態多變。但是不是Flex裡就不能實現呢?肯定不是,在Flex裡也可以自定義動畫效果,只不過就是沒有Flash裡面那麼簡單隨意了。不過熟悉了之後,也會覺得在Flex裡製作動畫也不是什麼難事,不多說了,轉入正題!

在這裡我先介紹一下Flex裡面的動畫效果機制,在Flex裡面要使用動畫效果的話,先要建立一個效果標籤,之後在元件裡(如TextInput)寫上效果觸發器,但可能會有人問,如果程式裡我就只定義一個移動效果
<mx:Move>,之後我程式裡面有5個元件,每個元件的動畫效果都指向這個Move效果,那麼它是不是元件一執行了效果後,元件二再觸發效果,是不是元件一的效果會消失才會到元件二里播放?其它不是,雖然我們只定義了一個Move,但我們定義的只是Move效果的工廠,這裡就用到了設計模式中的“工廠方法”模式,其實5個元件都可以同時執行效果,而5個效果都是不同的一個例項,彼此獨立。所謂工廠方法模式,就好比是一家衣服制造工廠,A走進這家工廠說要一件衣服,工廠就製作一件合適A的Size的衣服,B進去,就會生產合適B的衣服,但A與B的衣服都是一樣的。就好等於物件導向中的類與物件的關係一樣。(我可能說多了-_-)
效果執行的時候,其實執行的不是Move這個物件,而是MoveInstance這個物件,Move只是工廠,既然一個動畫效果就主要分這兩大部份,我們就先建造一個工廠吧!

在Flex裡面所有的效果的工廠都是繼承自 mx.effects.Effect 這個類,我們也不能搞特殊,我們自定義的效果也要繼承那個類,先看以下整個工廠類的程式碼:

 1 package com.jiangzone.flex.effects {
 2     import mx.effects.Effect;
 3     import mx.effects.EffectInstance;
 4    
 5     public class MyEffect extends Effect {
 6         private var _color:Number = 0xFF0000;
 7                
 8                 public function set color(value:Number):void {
 9                          _color = value;
10                 }
11 
12         public function MyEffect(newTarget:Object = null) {
13             super(newTarget);
14             instanceClass = MyEffectInstance;
15         }
16        
17         override public function getAffectedProperties( ):Array {
18             return [];
19         }
20        
21         override protected function initInstance(instance:EffectInstance):void {
22             super.initInstance(instance);
23                         MyEffectInstance(instance).color = _color;
24         }
25     }
26 }



大家看看上面的程式碼,其中先看建構函式,建構函式要接收一個預設為空的Object物件
public function MyEffect(newTarget:Object = null)
之後在該建構函式裡面呼叫父類的建構函式,並且將instanceClass這個屬性設定為你的該效果的例項類,因為這個類是工廠類,所以要知道你這個工廠生產什麼產品,即上面說的“衣服”,所以這裡我們將其命名為MyEffectInstance,注意:在Flex中的所有效果例項類都是在工廠類後面加Instance,也不是一定,只是規範而已。還有注意,下面一會定義的例項類的類名一定要跟這裡的一致。
大家還會看到,上面的程式碼中,複寫(override)了二個方法:getAffectedProperties( )與initInstance(instance:EffectInstance)
這兩個方法都是要複寫的,先說說getAffectedProperties( )這個方法,這個方法是獲取被改變的屬性值,怎麼說呢,比如說,你做的動畫效果如果要用到元件物件的一些屬性的話,就要返回這些屬性的名字,如:你的效果是對元件做旋轉的話,則:

1 override public function getAffectedProperties( ):Array {
2     return ["rotation"];
3 }


反正你做的效果需要對元件修改什麼屬性的話,都在這個方法裡返回名字,修改多個屬性的話就往陣列裡加就是了。
後面就是這個方法了initInstance,該方法接收一個instance:EffectInstance引數,也就是效果例項類啦,因為每個效果例項類都要繼承EffectInstance類,所以這個方法裡的引數寫的是父類,在裡面要做其它的話,需要將 instance 轉換為你相應的效果類。在這個方法裡面,也是要呼叫父類的同名方法:super.initInstance(instance);
基本上,一個工廠類就寫好了,但這樣只是最簡單的寫法,試想想,每個人穿衣服的Size不同,喜歡的顏色也不同,所以,是不是可以由使用者來定義他們想要的效果的顏色等屬性呢?當然,你對衣服有什麼要求,都是向工廠提出的,沒有人會對衣服說吧?所以,這些可設定的屬性也是定義在工廠類裡面,所以下面,我們為該衣服可定製顏色為例,在工廠類裡面加入如下程式碼:

1 private var _color:Number = 0xFF0000;
2 public function set color(value:Number):void {
3         _color = value;
4 }


你想執行時的效果可以設定不同的顏色的話,就可以直接設定MyEffect的color屬性,之後將這個屬性傳給效果例項類:

1 override protected function initInstance(instance:EffectInstance):void {
2     super.initInstance(instance);
3         MyEffectInstance(instance).color = _color;
4 }


這些對效果例項類的設定,都是要定在initInstance方法裡了,你想對執行時的效果設定什麼屬性的話,都要先告訴工廠類,之後工廠類在這個方法裡面轉嫁給例項類,這樣,同一個效果,可以執行不同的顏色。但前提是你後面要寫的例項類要有color這個屬性。
現在已做好了工廠類了,下面要做效果例項類了,先貼出完整程式碼:

 1 package com.jiangzone.flex.effects {
 2     import mx.effects.EffectInstance;
 3     import flash.display.Shape;
 4     import flash.events.Event;
 5    
 6     public class MyEffectInstance extends EffectInstance {
 7                
 8         private var _color:Number;
 9         private var shape:Shape;
10        
11         public function set color(value:Number):void {
12             _color = value;
13         }
14        
15         public function MyEffectInstance(newTarget:Object) {
16             super(newTarget);
17         }
18        
19         override public function play( ):void {
20             super.play( );
21             drawShape();
22         }
23        
24         private function drawShape():void{
25             shape = new Shape();
26             shape.graphics.beginFill(_color);
27             shape.graphics.drawRect(target.width * -0.5,target.height * -0.5,target.width,target.height);
28             shape.graphics.endFill();
29             shape.x = target.x + target.width * 0.5;
30             shape.y = target.y + target.height * 0.5;
31             target.parent.rawChildren.addChild(shape);
32             target.addEventListener(Event.ENTER_FRAME,onEnterFrame);
33         }
34        
35         private function onEnterFrame(e:Event):void{
36             shape.scaleX += 0.1;
37             shape.scaleY += 0.1;
38             shape.alpha -= 0.05;
39             if(shape.alpha <= 0){
40                 target.parent.rawChildren.removeChild(shape);
41                 target.removeEventListener(Event.ENTER_FRAME,onEnterFrame);
42             }
43         }
44     }
45 }



我們看到,每一個動畫效果例項類,都要繼承自EffectInstance這個類,建構函式也是需要接收一個Object,這個Object其實就是你要應用到的元件物件,這個會是系統自動傳遞的,接收了Object後還要用該Object 呼叫父類的建構函式:super(newTarget);
之後還有一件必做的事,就是重寫play()這個方法:override public function play( ):void
是不是對play()很熟悉?因為第一篇文章中,就用到這個方法來發動效果的播放的,所以,你需要做的動畫程式設計都是在這個方法裡。但還是要先呼叫父類的同名方法,super.play();之後的,就是你想怎麼畫就怎麼畫啦。我將畫一個與要應用效果的元件一樣大小的矩型,之後該矩形會放大並透明,效果都寫在drawShape()方法裡了。看到這個方法裡面的程式碼,是不是跟Flash裡的一樣了?
這裡再貼上MXML程式碼:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <mx:Application layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml"
 3                                                         xmlns:pf="com.jiangzone.flex.effects.*">
 4         <pf:MyEffect id="myEffect" color="0xFFFFFF" />
 5         <mx:VBox x="100" y="43">
 6                 <mx:TextInput focusInEffect="{myEffect}" />
 7                 <mx:TextInput focusInEffect="{myEffect}" />
 8                 <mx:TextInput focusInEffect="{myEffect}" />
 9                 <mx:TextInput focusInEffect="{myEffect}" />
10         </mx:VBox>
11 </mx:Application>


這裡先看看最終效果:


在這裡,我用了ENTER_FRAME的寫法,但是如果不用ENTER_FRAME方式製作動畫的話,還有另外一種方法的,那就是Tween了,Tween是以“時間”為準,而ENTER_FRAME是以“幀”為準,其實到這裡,一個基本的Flex自定義動畫效果就完成了,但擴充套件一下的,還可以用Tween來實現,而且建議用Tween來寫動畫效果,易控制,清淅一點。用Tween實現的話,效果與寫法都是差不多的,要用Tween就要將效果例項類繼承自TweenEffectInstance這個類,並重寫它的onTweenUpdate( )方法與onTweenEnd( )方法,這種Tween效果的寫法,將會比ENTER_FRAME的寫法方便,因為它根據的是時間,所以,你可以指定效果播放的時間,並且當播放完畢會自動呼叫onTweenEnd()方法,你可以在該方法裡寫一些處理操作,如釋放資源等等
由於編幅關係,就不在這裡詳細介紹TweenEffectInstence了,就簡單貼出該類的寫法與註釋吧:

 1 package com.jiangzone.flex.effects {
 2     import mx.effects.effectClasses.TweenEffectInstance;
 3     import flash.display.Shape;
 4     import flash.events.Event;
 5     import mx.effects.Tween;
 6    
 7     public class MyEffectInstance extends TweenEffectInstance {
 8                
 9         private var _color:Number;
10         private var shape:Shape;
11        
12         public function set color(value:Number):void {
13             _color = value;
14         }
15        
16         //建構函式
17         public function MyEffectInstance(newTarget:Object) {
18             super(newTarget);
19         }
20        
21         //同樣的要重寫play()方法與呼叫父類同名方法
22         override public function play( ):void {
23             super.play();
24             drawShape();        //先建立一個矩形
25             /*注意:用Tween效果寫法的話,就一定要建立一個Tween物件
26             第一個引數是偵聽器,即偵聽Update與End的,這兩個方法都在這個類裡,
27             所以這裡就寫this,第二和第三個引數都是一個陣列
28             第二個引數是初始值陣列,第三個是結果值陣列,都要一一對應,第四個是變化時間
29             這裡的是[1,1]分別是初始時的scale比例與alpha,[3,0]就是最終結果數值
30             系統會自動在1000毫秒裡平分這些值來得到漸變效果
31             並將每一次數值的改變時呼叫Update方法,結束後呼叫End方法
32                     你也可以將時間的引數釋出到工廠類屬性裡,可以方便設定播放時間,像Flex自帶效果一樣
33                          */
34             new Tween(this,[1,1],[3,0],1000);
35         }
36        
37         override public function onTweenUpdate(value:Object):void{
38             //這裡將改變的數值應用到元件物件中。注意:也要與上面的數值陣列相對應。
39             shape.scaleX = Number(value[0]);
40             shape.scaleY = Number(value[0]);
41             shape.alpha = Number(value[1]);
42         }
43        
44         override public function onTweenEnd(value:Object):void {
45             //當播放完時會自動呼叫該方法,這裡就做刪除該矩形的操作吧
46             target.parent.rawChildren.removeChild(shape);
47         }
48        
49         private function drawShape():void{
50             shape = new Shape();
51             shape.graphics.beginFill(_color);
52             shape.graphics.drawRect(target.width * -0.5,target.height * -0.5,target.width,target.height);
53             shape.graphics.endFill();
54             shape.x = target.x + target.width * 0.5;
55             shape.y = target.y + target.height * 0.5;
56             target.parent.rawChildren.addChild(shape);
57         }
58     }
59 }

轉載於:https://www.cnblogs.com/zhangliang0115/archive/2012/02/18/2357054.html

相關文章