Tag Archive for 'TweenEngine'

Tween Engine Class -AS3.0

flash 를 사용하면서 가장 많이 사용하는 부분은 아마도 트윈 부분일 것이다. 오브젝트의 속성값을 변경하면서 수많은 느낌의 모션을 만들수 있었다.
기존에 포함되어 있는 Tween 클래스는 많은 사용자들이 이런  다양한 트윈을 손쉽게 구현할 수 있게 해준다. 하지만, 생각보다 많은 기능들이 포함되어있어서 덩치도 클뿐더러 구현하는 코드 자체도 길어지게 되었다.
또한 AS3.0에서는 garbage collection 의 동작으로 인해 참조값이 없는 객체에 대해서는 collector 에 의해 메모리 수집의 대상이 되기 때문에 트윈클래스의 인스턴스를 지역변수로 참조하여 사용하게 되면 뜻하지 않게 트윈이 완료되기 이전에 객체가 사라지는 문제가 발생할 수도 있다..
따라서 이런 문제를 해결하기 위해 지역변수보다 좀더 높은 클래스 단계의 scope 의 참조를 사용하게 되는데 이럴경우 불필요하게 많은 참조 변수들을 클래스가 가지게되여 메모리를 소비하게 된다.

그리고 가장 빈번히 사용하게 되는 트윈의 경우 일일이 이벤트 핸들러 함수를 구현하기가 여간 번거로운게 아니다. 코드자체의 길이도 길어질 뿐만 아니라 가독성 측면에서도 떨어지는게 사실이다.

이런 문제를 조금이나마 해결하고자 이전 버전의 Tween 클래스를 기반으로 AS3.0 으로 컨버팅하였다.

기본적은 틀은 이전에 AS2.0 으로 구현한 로직과 큰 차이는 없다.
참조값을 유지하기 위해 전역속성으로 트윈을 적용하였고 객체를 생성하지 않아도 되기 때문에 한결 코드도 간결해졌다.

***** update(2007.10.30) ******

pause 와 resume 기능을 추가
원하는 오브젝트의 트윈에 적용가능함

pause(), pauseAll() 나 resume(), resumeAll() 를 사용하여 전체또는 부분적인 트윈을 제어할 수 있다.

<TweenEngine class>

package com.dstrict.ub.utils.transitions{
 
 import flash.display.DisplayObject;
 import flash.events.*;
 import flash.utils.Dictionary;
 /**
  DisplayObject tween class
  트윈을 적용할 속성의 갯수에 관계없이 일괄적으로 적용가능
  마지막 parameter 로 정해진 형식의 이벤트 오브젝트 적용시 이벤트 시작과 끝을 tracking 할 수 있음. 
 
  @example code
  <code>
 
   function onTweenStart(param):void{
    trace("onTweenStart----&gt;"+param);
   }
 
   function onTweenFinish(param):void{
    trace("onTweenFinish----&gt;"+param);
   }
 
TweenEngine.start(circle,{x:400},Regular.easeOut,30,{onStart:onTweenStart,
onStartParams:["circle"],onFinish:onTweenFinish,onFinishParams:["circle"]});
 </code>
 
 */
 
 public class TweenEngine {
   private static var _referContainer : Dictionary=new Dictionary(true);
  /**
   @param targetObj : DisplayObject , tween 적용할 오브젝트
   @param tweenProperty : Object , 속성오브젝트 ex. {x:100,y:100,alpha:1}
   @param easing : Function , 이징함수
   @param duration : int , 지속프레임
   @param rest : Obejct , [optional]  event object
   ex. {onStart:onTweenStart,onStartParams:[],onFinish:onTweenFinish,onFinishParams:[]}
  */
  public static function start(targetObj:*,tweenProperty:Object,easing:Function,duration:int,...rest):void{
   var time:int=1;
   var beginning:Array=new Array();
   var change:Array=new Array();
   var isCreated : Boolean=false;
   var displayObj : DisplayObject;
 
   TweenEngine.stop(targetObj);
 
   if(!(targetObj is DisplayObject)){
      displayObj = new Shape();
      isCreated=true;
    }else{
      displayObj = targetObj;
     }
 
   for(var i:* in tweenProperty){
       beginning.push(targetObj[i]);
       change.push(tweenProperty[i]-targetObj[i]);
   }
 
   if(rest.length &amp;&amp; rest[0].onStart){
    var eventStartObject:Object=new Object();
    eventStartObject.onStart=rest[0].onStart;
    eventStartObject.onStartParams=rest[0].onStartParams;
 
    //onStart event
    eventStartObject.onStart.apply(eventStartObject,eventStartObject.onStartParams);
   }
 
   //Nested function
   var update:Function=function(){
    var objIdx:int=0;
      for(var i:* in tweenProperty){
       targetObj[i]=easing(time,beginning[objIdx],change[objIdx],duration);
       objIdx++;
      }
      time++;
      if(time&gt;duration){
      delete _referContainer[displayObj];
       displayObj.removeEventListener(Event.ENTER_FRAME,update);
       if(isCreated) displayObj=null;
 
       if(rest.length &amp;&amp; rest[0].onFinish){
        var eventFinishObject:Object=new Object();
        eventFinishObject.onFinish=rest[0].onFinish;
        eventFinishObject.onFinishParams=rest[0].onFinishParams;
 
        //onFinish event
        eventFinishObject.onFinish.apply(eventFinishObject,eventFinishObject.onFinishParams);
       }
     }
   }
     _referContainer[displayObj] = update;
      displayObj.addEventListener(Event.ENTER_FRAME,update);
  }
 
  //Update 2007.10.30
  /**
   * Pause a tweening for a given object.
   */
  public static function  pause(targetObj : *) : void {
   if(targetObj.hasEventListener(Event.ENTER_FRAME)) {
    targetObj.removeEventListener(Event.ENTER_FRAME, _referContainer[targetObj]);
   }
  }
 
  /**
   * Pause all tweenings on the engine.
   */
 public static function pauseAll() : void {
   for(var item:* in _referContainer) {
    if(item.hasEventListener(Event.ENTER_FRAME)) {
     item.removeEventListener(Event.ENTER_FRAME, _referContainer[item]);
    }
   }
  }
 
  /**
   * Resume a tweening from a given object.
   */
  public static function resume(targetObj : *) : void {
   if(_referContainer[targetObj] != null) {
    targetObj.addEventListener(Event.ENTER_FRAME, _referContainer[targetObj]);
   }
  }
 
  /**
   * Resume all tweenings on the engine.
   */
  public static function resumeAll() : void {
   for(var item:* in _referContainer) {
    if(_referContainer[item] != null) {
     item.addEventListener(Event.ENTER_FRAME, _referContainer[item]);
    }
   }
  }
 
  /**
   *  Stop a tweening for a given object
   */
  public static function stop(targetObj : *) : void {
   if(targetObj.hasEventListener(Event.ENTER_FRAME)) {
    targetObj.removeEventListener(Event.ENTER_FRAME, _referContainer[targetObj]);
    delete _referContainer[targetObj];
   }
  }
 
  /**
   * Remove all tweenings from the engine.
   */
  public static function stopAll() : void {
   for(var item:* in _referContainer) {
    if(item.hasEventListener(Event.ENTER_FRAME)) {
     item.removeEventListener(Event.ENTER_FRAME, _referContainer[item]);
     delete _referContainer[item];
    }
   }
  }
 
 }
}
 
import com.dstrict.ub.utils.transitions.*;
import fl.motion.easing.*;
 
TweenEngine.start(circle,{x:400,y:300},Cubic.easeInOut,20,{onStart:onTweenStart,onStartParams:["circle start"],
onFinish:onTweenFinish,onFinishParams:["circle finish"]});
 
//----&gt; 마지막 오브젝트 값은 이벤트 핸들러를 참조하는 값으로 옵션사항이다., 사용시 이벤트 오브젝에서 함수이름을 onStart , onFinish 로
            이벤트 파라미터를 onStartParams,onFinishParams 로 키값을 사용해야만 한다.
 
 function onTweenStart(param):void{
   trace("onTweenStart----&gt;"+param);
}
 
function onTweenFinish(param):void{
  trace("onTweenFinish----&gt;"+param);
}
 
//stop tween
stage.addEventListener(MouseEvent.CLICK,onStop);
function onStop(evt:MouseEvent){
 TweenEngine.stop(circle);
}

download sample

ColorMatrixFilter TweenEngine

ColorMatrixFilter 클래스를 이용하면  이미지의 각 픽셀의 RGBA 색상 및 알파 값에 4 x 5 행렬 변환을 적용하여 saturation 또는 contrast 또는  brightness 를 적용할수 있다. 물론 각각의 ColorMatrixFilter 에 적용되는 matrix 속성값을 다르게 설정하여야 한다.

이 예제에서 활용한 ColorMatrix 는 Mario Klingemann(http://www.quasimondo.com) 가 제작한 소스를 이용하여 트윈클래스를 제작하였다.
기본적인 클래스 구조는 이전의 트윈클래스와 같다. 객체속성을 이용하여 for~in 구문으로 트윈을 적용하였다. 다른 객체에 비해 적용되는 속성의 갯수(matrix 속성)가 20개로써 많기 때문에 이미지를 크게 하거나 트윈길이를 너무 길게할 경우 느려질 수가 있다. 또한 트윈이후 이전 트윈클래스와 같이 콜백함수를 호출할 수 있다.

download sample

//==================================================================
//@class name  :  ColorMatrixTween.as
//@author          : vkimone. KimKiJeung  (http://kimkijeung.com)
//@last update   : 2007. 03. 19
//@version         : V1.0
//==================================================================
/**
 @description
* 무비클립의  ColorMatrixFilter 트윈 클래스 : 이징함수 설정으로 조절 
 
* @example
*     <code>
*     ColorMatrixTween.tween(targetMc,0,ColorMatrixTween.SATURATION,Regular.easeOut,30,
             {{func: callBackFunction,obj: functionScope, param: [파라미터 배열로 들어감]}}
*     </code>
*/
 
import flash.filters.ColorMatrixFilter;
import com.dstrict.UB.util.filters.ColorMatrix;
class com.dstrict.UB.util.transitions.tween.ColorMatrixTween{
  public static var SATURATION:String="saturation";
  public static var CONTRAST:String="contrast";
  public static var BRIGHTNESS:String="brightness";
 
 /**---------------------------------------------------------------------
  *@description  saturation, contrast, brightness 트랜지션
    @param mc : MovieClip, 적용무비클립
    @param value : Number , 적용 percentage
    @param mode :String , saturation or contrast or brightness mode
    @param func : Function, easing function
   @param durationFrame : Number, 지속프레임
*----------------------------------------------------------------------*/
public static function tween(mc:MovieClip,value:Number,mode:String,func:Function,durationFrame:Number):Void{
var time:Number=1;
var beginning:Array=new Array();
var change:Array=new Array();
 var mat:ColorMatrix = new ColorMatrix();
 switch(mode){
   case SATURATION :
     mat.adjustSaturation(value/100);
     break;
 
   case CONTRAST :
     mat.adjustContrast(value/100);
     break;
 
   case BRIGHTNESS :
     mat.adjustBrightness(255*value/100);
     break;
  }
  var cm:ColorMatrixFilter = new ColorMatrixFilter(mat.matrix);
  var startMatrix:Array=
                       (mc.filters[0].matrix==undefined) ? ColorMatrix.IDENTITY : mc.filters[0].matrix;
  var targetMatrix:Array=mat.matrix;
 
for(var i in targetMatrix){
  beginning.push(startMatrix[i]);
  change.push(targetMatrix[i]-startMatrix[i]);
}
       var type=(typeof(arguments[5])=="object")? true : false;
       if(type){
        var referObj=arguments[5];
       }else{
        var p:Number=arguments[5];
        var referObj=arguments[6];
       }
  mc.onEnterFrame=function(){
  var objIdx:Number=0;
  for(var i in targetMatrix){
   targetMatrix[i]=func(time,beginning[objIdx],change[objIdx],durationFrame,p);
   objIdx++;
   }
   targetColMatrixFilter.matrix=targetMatrix;
   mc.filters=[targetColMatrixFilter];
   time++;
  if(time&gt;durationFrame){
   delete this.onEnterFrame;
   if(referObj!=undefined){
    referObj.func.apply(referObj.obj,referObj.param);
   }
 
  }
};
}
}
 
import  com.dstrict.UB.util.transitions.tween.FilterTween;
import  mx.transitions.easing.*;
import com.dstrict.UB.util.filters.ColorMatrix;
//SATURATION (from 0  to 100)
image.onRollOver=function(){
 ColorMatrixTween.tween(image,0,ColorMatrixTween.SATURATION,Regular.easeOut,10);
}
image.onRollOut=function(){
 ColorMatrixTween.tween(image,100,ColorMatrixTween.SATURATION,Regular.easeOut,10);
}
//CONTRAST (from 0  to 100)
image2.onRollOver=function(){
 ColorMatrixTween.tween(image2,100,ColorMatrixTween.CONTRAST,Regular.easeOut,10);
}
image2.onRollOut=function(){
 ColorMatrixTween.tween(image2,0,ColorMatrixTween.CONTRAST,Regular.easeOut,10);
}
//BRIGHTNESS (from -100  to 100)
image3.onRollOver=function(){
ColorMatrixTween.tween(image3,100,ColorMatrixTween.BRIGHTNESS,Regular.easeOut,10);
}
image3.onRollOut=function(){
ColorMatrixTween.tween(image3,0,ColorMatrixTween.BRIGHTNESS,Regular.easeOut,10);
}

Filter TweenEngine

무비클립 속성에 비해 다루기가 까다로워 필터 트원은 좀처럼 사용하지는 않았다.
특히 프레임으로 모션트윈은 어느정도 사용했지만 스크립트를 이용한 조절은 거의……

전반적으로 필터에 대해 다시 살펴보면서 이번기회에 필터트윈 클래스를 만들어 보았다.
새로 만들긴 했지만 기존에 만들었던 무비클립 Tween 클래스와 기본 구조는 같다.
어짜피 필터 트원이라는것이 필터속성을 시간단위로 변화를 주는 것이기 때문에 무비클립의 그것과 다르지 않았다.

무비클립 트윈클래스와 다른점은 인자값으로 적용할 필터오브젝트를 생성해서 넘겨주는 것이다.
그리고 그 필터속성의 변화값을 무비클립 filters 속성에 적용해 주는 것이다.
그리고 다중필터 지원은 동시에 여러가지 필터를 적용하는 경우가 많지 않을뿐더러 쓸데없이 파라미터가 길어질 염려가 있어 적용하지 않았다.

필터 적용범위는 필터속성의 데이타 타입이 Number 일경우에만 적용해야 한다. 따라서 ColorMatrixFilter와 같이 속성값이 matrix 일 경우 이 클래스로는 트윈이 적용되지 않는다. 그 이외의 모든 필터의 속성에 대해서는 트윈이 가능하다.
또한 필터 속성마다 해당하는 속성 범위가 존재한다. 이점을 유의해서 적용해야한다.

download sample

//==================================================================
//@class name  :  FilterTween.as
//@author          : vkimone. KimKiJeung  (http://kimkijeung.com)
//@last update   : 2007. 03. 07
//@version         : V1.0
//==================================================================
/**
 @description
* 무비클립의 필터 속성  트윈 클래스 : 이징함수 설정으로 조절
* 트원할 필터속성의 갯수에 관계없이 오브젝트로 적용 가능
*
* @caution
*  -반드시 트원할 필터 속성의 데이타 타입이 Number 일경우에만 적용할수 있다.
    String,Boolean,Array 는 적용할 수 없다.
   -다중필터 적용은 지원하지 않는다.
*
* @example
*     <code>
*        Filtertween.tween(targetMc,{blurX:36,blurY:36},Regular.easeOut,30,
       {{func: callBackFunction,obj: functionScope, param: [파라미터 배열로 들어감]}}
*     </code>
*/
import flash.filters.BitmapFilter;
class com.dstrict.UB.util.transitions.tween.FilterTween{
/**------------------------------------------------------------------------
 * @param mc : MovieClip, 적용무비클립
 * @param filter : BitmapFilter , 적용할 필터 객체
 * @param obj : Object, 단일 필터속성 (필터 속성 data type 이 Number 일 경우)
                                                     ex. {blurX:32,blurY:32}
 * @param func : Function, easing function
 * @param durationFrame : Number, 지속프레임
 * @param referObj :[option] reference object(caution--&gt; 파라미터값 반드시 배열요소로 입력)
*----------------------------------------------------------------------*/
public static function tween(mc:MovieClip,filter:BitmapFilter,obj:Object,func:Function,durationFrame:Number):Void{
var time:Number=1;
var beginning:Array=new Array();
var change:Array=new Array();
 
for(var i in obj){
  beginning.push(filter[i]);
  change.push(obj[i]-filter[i]);
}
       var type=(typeof(arguments[5])=="object")? true : false;
       if(type){
        var referObj=arguments[5];
       }else{
        var p:Number=arguments[5];
        var referObj=arguments[6];
       }
  mc.onEnterFrame=function(){
  var objIdx:Number=0;
  for(var i in obj){
   filter[i]=func(time,beginning[objIdx],change[objIdx],durationFrame,p);
   mc.filters=[filter]; //필터 적용부분
   objIdx++;
   }
   time++;
  if(time&gt;durationFrame){
   delete this.onEnterFrame;
   if(referObj!=undefined){
    referObj.func.apply(referObj.obj,referObj.param);
   }
 
  }
};
}
}
 
import  com.dstrict.UB.util.transitions.tween.FilterTween;
import  mx.transitions.easing.*;
import flash.filters.*;
var blurFilter:BlurFilter=new BlurFilter(0,0,1);
var dropShadowFilter:DropShadowFilter=new DropShadowFilter(0,120,0x000000,1);
var glowFilter:GlowFilter=new GlowFilter(0x6E7D74,1,16,16,2,2);
//BlurFilter
image.onRollOver=function(){
 FilterTween.tween(image,blurFilter,{blurX:8,blurY:8},Regular.easeOut,15,
        {func:onFilterTweenFinished,obj:_root,param:["blur"]});
}
image.onRollOut=function(){
 FilterTween.tween(image,blurFilter,{blurX:0,blurY:0},Regular.easeOut,15);
}
//DropShadowFilter
image2.onRollOver=function(){
 FilterTween.tween(image2,dropShadowFilter,{distance:4,blurX:4,blurY:4},Regular.easeOut,15,
        {func:onFilterTweenFinished,obj:_root,param:["dropShadow"]});
}
image2.onRollOut=function(){
 FilterTween.tween(image2,dropShadowFilter,{distance:0,blurX:4,blurY:0},Regular.easeOut,15);
}
//GlowFilter
image3.onRollOver=function(){
 FilterTween.tween(image3,glowFilter,{alpha:1,blurX:36,blurY:36},Regular.easeOut,15,
        {func:onFilterTweenFinished,obj:_root,param:["glow"]});
}
image3.onRollOut=function(){
 FilterTween.tween(image3,glowFilter,{alpha:0,blurX:0,blurY:0},Regular.easeOut,15);
}
function onFilterTweenFinished(evt):Void{
 trace("filterTween finished....."+evt);
}

Tween Engine

기존에 동시에 2개의 속성을 트윈시킬 수 있었던 것에서 트원 속성 갯수에 관계없이

가능하도록 수정하였다.  이 클래스 하나로 무비클립 속성 트윈은 대부분 가능하다.
마지막 파라미터를 이용해 콜백함수를 호출할수 있다. 이징 함수 자체도 조절 가능하게
해보았지만 효율성 측면에서 생략했다.
어쩌면 이정도 길이에 가장 많은 기능을 제공하고 범용적으로 사용될수 있는 트윈클래스가 없지 않을듯 싶다…^^

/**
* @description
* 무비클립 속성  트윈 클래스 : 이징함수 설정으로 조절 
* 트원할 속성의 갯수에 관계없이 오브젝트로 적용 가능
* 
* 트윈이 끝났을 때 실행할 함수 및 파라미터 설정 방법
* 다른 클래스에서 직접 참조해서 호출 가능....
* @param referObj : reference object(caution--> 파라미터값 반드시 배열요소로 입력)
* @example
*     <code>
*        PropertyTweenEase.tween(targetMc,{_x:200,_y:100},Elastic.easeOut,30,{{func: 실행할 함수
이름 , obj: 참조오브젝트, param: [파라미터 배열로 들어감]}}
*     </code>
*/
class com.dstrict.UB.util.transitions.tween.PropertyTweenEase {
/**----------------------------------------------------------------------------------
* @param mc : MovieClip, 적용무비클립
* @param property : Object, 무비클립속성 오브젝트 ex. {_x:100,_y:200}
* @param func : Function, easing function
* @param durationFrame : Number, 지속프레임
*------------------------------------------------------------------------------------*/
public static function tween(mc:MovieClip,obj:Object,func:Function,durationFrame:Number):Void{
var time:Number=1;
var beginning:Array=new Array();
var change:Array=new Array();
  
for(var i in obj){
  beginning.push(mc[i]);
     change.push(obj[i]-mc[i]);
     
}
       var type=(typeof(arguments[4])=="object")? true : false;
       if(type){
        var referObj=arguments[4];
       }else{
        var p:Number=arguments[4]; 
        var referObj=arguments[5];
       }
  mc.onEnterFrame=function(){
  var objIdx:Number=0;
  for(var i in obj){
   mc[i]=func(time,beginning[objIdx],change[objIdx],durationFrame,p);
   objIdx++;
  }
     time++;
  if(time>durationFrame){
   delete this.onEnterFrame;
   if(referObj!=undefined){
    referObj.func.apply(referObj.obj,referObj.param);
   }
   
  }
};
}
}

<사용방법>

PropertyTweenEase.tween(mc,{_x:100,_y:200,_alpha:100},Regular.easeOut,20,
{func:this.onMotionFinished,obj:this});
 
function onMotionFinished():Void{
trace("motion finished.......");
}