Source: timer/AnimationTransition.js

/**
 * collie.Effect를 사용한 Transition 타이머
 * [튜토리얼 보기](../tutorial/timer_transition.html)
 *
 * @example
 * ```
 * 여러 개의 값으로 트랜지션
 * collie.Timer.transition(function (oEvent) {
 *  oDisplayObject.set("opacity", oEvent.value[0]);
 *  oDisplayObject.set("x", oEvent.value[1]);
 * }, 1000, {
 *  from : [1, 100],
 *  to : [0, 300]
 * });
 * ```
 * 
 * @example
 * DisplayObject를 callback으로 사용해서 여러 속성을 변경하는 방법
 * ```
 * collie.Timer.transition(oDisplayObject, 1000, {
 *  from : [10, 10], // from 은 생략 가능, 생략하면 현재 값이 자동으로 입력 됨
 *  to : [100, 200],
 *  set : ["x", "y"]
 * });  
 * ```
 * 하지만 이 때에는 DisplayObject의 move 메서드를 사용하는 것이 좋음
 * 
 * @example
 * 여러 개의 DisplayObject를 한꺼번에 실행할 수도 있음
 * ```
 * collie.Timer.transition([oDisplayObjectA, oDisplayObjectB], 1000, {
 *  from : 0, // 이 때에는 from 생략 불가능
 *  to : 1,
 *  set : "opacity"
 * });
 * ```
 * 
 * @see collie.Timer
 * @class
 * @extends collie.Animation
 * @param {Function|collie.DisplayObject|Array} fCallback 실행될 콜백 함수, DisplayObject를 넣게 되면 해당 객체에 관한 내용만 변경함. htOption의 set 참조.
 * @param {collie.AnimationCycle} fCallback.timer 현재 타이머 인스턴스
 * @param {Number} fCallback.frame 현재 프레임
 * @param {Number} fCallback.duration 타이머에 설정된 duraiton 값
 * @param {Number} fCallback.cycle 반복 횟수
 * @param {Number} fCallback.runningTime 타이머 시작 후 실행된 시간 (ms)
 * @param {Number|Array} fCallback.value 적용할 값. from, to 값이 배열일 경우 이 값도 배열로 반환
 * @param {Number|Array} fCallback.from 시작 값, 시작 값을 입력하지 않고 fCallback에 DisplayObject를 넣으면 해당 객체의 현재 값이 자동으로 입력됨
 * @param {Number|Array} fCallback.to 끝 값
 * @param {Number} nDuration 실행 시간
 * @param {Object} htOption 설정
 * @param {Number|Array} htOption.from 시작 값(배열로 넣을 수 있음)
 * @param {Number|Array} htOption.to 끝 값(배열로 넣을 수 있음)
 * @param {Number} [htOption.loop=1] 반복 횟수
 * @param {collie.Effect} [htOption.effect=collie.Effect.linear] 효과 함수
 * @param {String|Array} [htOption.set] fCallback에 DisplayObject를 넣을 경우 set을 이용해서 특정 값을 변경한다. 배열로 넣을 경우 여러 속성을 변경할 수 있다
 * @see collie.Effect
 */
collie.AnimationTransition = collie.Class(/** @lends collie.AnimationTransition.prototype */{
    $init : function (fCallback, nDuration, htOption) {
        this.option({
            from : null, // 시작 값(배열로 넣을 수 있음)
            to : null, // 끝 값(배열로 넣을 수 있음)
            set : "",
            loop : 1,
            effect : collie.Effect.linear // 이펙트 함수
        });
        this._htCallback = {};
        this.option(htOption || {});
        var fReset = this.reset.bind(this);
        this.optionSetter("from", fReset);
        this.optionSetter("to", fReset);
        this._nCount = 0;
        this._nCountCycle = 0;
        this._nFrameAtRunLastest = null;
        this._nRunningTime = null;
        this._bIsArrayValue = false;
    },
    
    /**
     * 시작할 때 실행되는 메서드
     * @override
     */
    start : function () {
        // 시작 값이 없을 떄 객체의 현재 값을 입력
        if (this._htOption.from === null && typeof this._fCallback !== "function") {
            this._setDefaultFromValues();
        }
        
        if (this._nFrameAtRunLastest === null) {
            this.reset();
        }
        
        this.constructor.$super.start.call(this);
    },
    
    /**
     * @private
     */
    _setDefaultFromValues : function () {
        var vFrom = null;
        
        if (this._htOption.set) {
            if (this._htOption.set instanceof Array) {
                vFrom = [];
                for (var i = 0, len = this._htOption.set.length; i < len; i++) {
                    vFrom.push(this._fCallback.get(this._htOption.set[i]));
                }
            } else {
                vFrom = this._fCallback.get(this._htOption.set)
            }
            
            this.option("from", vFrom);
        }
    },
    
    /**
     * 값을 초기화
     */
    reset : function () {
        this._nFrameAtRunLastest = null;
        this._nRunningTime = null;
        this._nValue = this._htOption.from;
        this._bIsArrayValue = this._htOption.from instanceof Array;
        this._nCount = 0;
        this._nCountCycle = 0;
        
        // 값이 배열일 경우 처리
        if (this._bIsArrayValue) {
            this._fEffect = [];
            var fEffect = null;
            
            for (var i = 0, len = this._htOption.from.length; i < len; i++) {
                fEffect = (this._htOption.effect instanceof Array) ? this._htOption.effect[i] : this._htOption.effect; 
                this._fEffect[i] = fEffect(this._htOption.from[i], this._htOption.to[i]);
            }
        } else {
            this._fEffect = this._htOption.effect(this._htOption.from, this._htOption.to);
        }
    },
    
    /**
     * 현재 값을 설정
     * 
     * @param {Variables} vValue
     */
    setValue : function (vValue) {
        this._nValue = vValue;
    },
    
    /**
     * 현재 값을 반환
     * 
     * @return {Variables}
     */
    getValue : function () {
        return this._nValue;
    },
    
    /**
     * 애니메이션을 실행
     * 
     * @param {Number} [nCurrentFrame] 현재 렌더러 프레임, 값이 없으면 자동으로 현재 렌더러 프레임을 가져 온다
     * @param {Number} [nFrameDuration] 진행된 프레임 시간(ms)
     */
    run : function (nCurrentFrame, nFrameDuration) {
        if (nCurrentFrame === undefined) {
            nCurrentFrame = collie.Renderer.getInfo().frame;
        }
        
        // 렌더러가 stop 된 경우
        if (this._nFrameAtRunLastest > nCurrentFrame) {
            this.reset();
            return;
        }
        
        // 시작 프레임 저장
        if (this._nFrameAtRunLastest === null) {
            this._nFrameAtRunLastest = nCurrentFrame;
            this._nRunningTime = 0;
            nFrameDuration = 0;
        }
        
        this._nRunningTime += nFrameDuration;
        this._nCount++;
        
        // 시간이 지났으면 멈춤
        if (this._nRunningTime >= this._nDuration) {
            this._nCountCycle++;
            
            // 끝나는 값이 아니면 끝나는 값으로 만듦(한번 더 실행), 루프의 마지막일 때만 보정함.
            if (!this._isEndValue() && this._htOption.loop && this._htOption.loop <= this._nCountCycle) {
                this._setEndValue();
            } else if (!this._htOption.loop || this._htOption.loop > this._nCountCycle) {
                /**
                 * loop가 있을 경우 트랜지션이 한번 끝났을 때 발생
                 * @name collie.AnimationTransition#end
                 * @event
                 * @param {Object} oEvent 기본 컴포넌트 이벤트 객체
                 */
                this.fireEvent("end");
                this._nFrameAtRunLastest = nCurrentFrame;
                this._nRunningTime = this._nRunningTime - this._nDuration; // loop면 처음부터 다시 시작이 아니라 이어서 시작
                this._nValue = this._htOption.from;
                this._transitionValue(this._nRunningTime);
            } else {
                /**
                 * 트랜지션이 끝난 후 발생
                 * @name collie.AnimationTransition#complete
                 * @event
                 * @param {Object} oEvent 기본 컴포넌트 이벤트 객체
                 */
                this.complete();
                return;
            }
        } else if (this._nRunningTime > 0) {
            this._transitionValue(this._nRunningTime);
        }
        
        // 객체 재활용
        this._htCallback.timer = this;
        this._htCallback.frame = nCurrentFrame;
        this._htCallback.duration = this._nDuration;
        this._htCallback.cycle = this._nCountCycle;
        this._htCallback.runningTime = this._nRunningTime;
        this._htCallback.from = this._htOption.from;
        this._htCallback.to = this._htOption.to;
        this._htCallback.value = this._nValue; // 값이 배열이면 이것도 배열로 반환됨
        this.triggerCallback(this._htCallback);
        
        if (this._nRunningTime > 0) {
            this._nFrameAtRunLastest = nCurrentFrame;
        }
    },
    
    /**
     * 현재 프레임 값을 받아 현재 값을 transition된 값으로 변경 한다
     * @private
     * @param {Number} nCurrentRunningTime 현재 진행된 시간(ms)
     */
    _transitionValue : function (nCurrentRunningTime) {
        if (this._bIsArrayValue) {
            this._nValue = [];
            
            for (var i = 0, len = this._htOption.from.length; i < len; i++) {
                this._nValue[i] = parseFloat(this._fEffect[i](Math.max(0, Math.min(1, nCurrentRunningTime / this._nDuration))));
            }
        } else {
            this._nValue = parseFloat(this._fEffect(Math.max(0, Math.min(1, nCurrentRunningTime / this._nDuration))));
        }
    },
    
    /**
     * 끝 값인지 여부를 반환
     * @private
     * @return {Boolean} true면 끝 값
     */
    _isEndValue : function () {
        if (this._bIsArrayValue) {
            for (var i = 0, len = this._htOption.to.length; i < len; i++) {
                if (this._nValue[i] !== parseFloat(this._fEffect[i](1))) {
                    return false;
                }
            }
            
            return true;
        } else {
            return this._nValue === parseFloat(this._fEffect(1));
        }
    },
    
    /**
     * 현재 값을 끝 값으로 설정 한다
     * @private
     * @param {Number} nValue
     */
    _setEndValue : function () {
        if (this._bIsArrayValue) {
            for (var i = 0, len = this._htOption.to.length; i < len; i++) {
                this._nValue[i] = parseFloat(this._fEffect[i](1));
            }
        } else {
            this._nValue = parseFloat(this._fEffect(1));
        }
    }
}, collie.Animation);
comments powered by Disqus