Source: camera/CameraShake.js

//------------------------------------------------------------------------------
// Constructor scope
//------------------------------------------------------------------------------

/**
 * Creates a new instance of CameraShake.
 *
 * @constructor
 * 
 * @class
 * @classdesc
 * 
 * The CameraShake class represents a subsystem for applying a shake effect to 
 * camera objects.
 */
rune.camera.CameraShake = function() {

    //--------------------------------------------------------------------------
    // Private properties
    //--------------------------------------------------------------------------

    /**
     * Shake amount in x and y axis.
     *
     * @type {rune.geom.Point}
     * @private
     */
    this.m_ammount = new rune.geom.Point(0, 0);

    /**
     * Method to call when the effect is over.
     *
     * @type {Function}
     * @private
     */
    this.m_callback = null;

    /**
     * How long the camera should shake. Time is given in milliseconds.
     *
     * @type {number}
     * @private
     */
    this.m_duration = 0.0;

    /**
     * Whether the effect should gradually diminish.
     *
     * @type {boolean}
     * @private
     */
    this.m_easing = false;

    /**
     * Elapsed time in milliseconds.
     *
     * @type {number}
     * @private
     */
    this.m_elapsed = 0.0;

    /**
     * Shake offset.
     *
     * @type {rune.geom.Point}
     * @private
     */
    this.m_offset = new rune.geom.Point();

    /**
     * Remaining time in milliseconds.
     *
     * @type {number}
     * @private
     */
    this.m_remaining = 0;

    /**
     * Execution scope for possible callback method.
     *
     * @type {Object}
     * @private
     */
    this.m_scope = null;
};

//------------------------------------------------------------------------------
// Public getter and setter methods
//------------------------------------------------------------------------------

/**
 * Current offset in the x axis.
 *
 * @member {number} x
 * @memberof rune.camera.CameraShake
 * @instance
 * @readonly
 */
Object.defineProperty(rune.camera.CameraShake.prototype, "x", {
    /**
     * @this rune.camera.CameraShake
     * @ignore
     */
    get : function() {
        return this.m_offset.x;
    }
});

/**
 * Current offset in the y axis.
 *
 * @member {number} y
 * @memberof rune.camera.CameraShake
 * @instance
 * @readonly
 */
Object.defineProperty(rune.camera.CameraShake.prototype, "y", {
    /**
     * @this rune.camera.CameraShake
     * @ignore
     */
    get : function() {
        return this.m_offset.y;
    }
});

//------------------------------------------------------------------------------
// Public prototype methods (API)
//------------------------------------------------------------------------------

/**
 * Activates the shake effect according to specified parameters.
 *
 * @param {number} duration Shake duration (in milliseconds).
 * @param {number} [ammountX=5] Shake offset in x axis.
 * @param {number} [ammountY=5] Shake offset in y axis.
 * @param {boolean} [easing=false] Shake easing.
 * @param {Function} [callback] Callback function.
 * @param {Object} [scope] Scope of execution.
 * 
 * @returns {undefined}
 */
rune.camera.CameraShake.prototype.start = function(duration, ammountX, ammountY, easing, callback, scope) {
    this.m_elapsed = 0.0;
    this.m_duration = duration;
    this.m_remaining = this.m_duration;
    this.m_ammount.x = parseInt(ammountX, 10) || 5;
    this.m_ammount.y = parseInt(ammountY, 10) || 5;
    this.m_easing = Boolean(easing);
    this.m_callback = callback || null;
    this.m_scope = scope || window;
};

/**
 * Stops ongoing effect.
 *
 * @param {boolean} [triggerCallback=false] If the callback is to be activated.
 * 
 * @returns {undefined}
 */
rune.camera.CameraShake.prototype.stop = function(triggerCallback) {
    this.m_active = false;
    this.m_ammount.x = 0.0;
    this.m_ammount.y = 0.0;
    this.m_offset.x  = 0.0;
    this.m_offset.y  = 0.0;

    if (triggerCallback === true && typeof this.m_callback === "function") {
        this.m_callback.call(this.m_scope);
    }
};

//------------------------------------------------------------------------------
// Internal prototype methods
//------------------------------------------------------------------------------

/**
 * Updates the logic of the effect. This method is called automatically by the 
 * current camera and thus should not be called manually.
 *
 * @param {number} step Current time step.
 * 
 * @returns {undefined}
 * @package
 * @ignore
 */
rune.camera.CameraShake.prototype.update = function(step) {
    if (this.m_remaining > 0) {
        this.m_remaining -= step;
        
        var e = (this.m_easing) ? this.m_remaining / this.m_duration : 1.0;
        var x = this.m_ammount.x * e;
        var y = this.m_ammount.y * e;
        
        this.m_offset.x = rune.util.Math.random(-x, x);
        this.m_offset.y = rune.util.Math.random(-y, y);
        
        if (this.m_remaining <= 0) {
            this.stop(true);
        }
    }
};

/**
 * Removes the subsystem (CameraShake).
 * 
 * @returns {undefined}
 * @package
 * @ignore
 */
rune.camera.CameraShake.prototype.dispose = function() {
    this.m_offset = null;
    this.m_ammount = null;
    this.m_callback = null;
};