/* * Event * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * A collection of Classes that are shared across all the CreateJS libraries. The classes are included in the minified * files of each library and are available on the createsjs namespace directly. * * <h4>Example</h4> * myObject.addEventListener("change", createjs.proxy(myMethod, scope)); * * @module CreateJS * @main CreateJS */ // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; /** * Contains properties and methods shared by all events for use with * {{#crossLink "EventDispatcher"}}{{/crossLink}}. * * Note that Event objects are often reused, so you should never * rely on an event object's state outside of the call stack it was received in. * @class Event * @param {String} type The event type. * @param {Boolean} bubbles Indicates whether the event will bubble through the display list. * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled. * @constructor **/ var Event = function(type, bubbles, cancelable) { this.initialize(type, bubbles, cancelable); }; var p = Event.prototype; Event.prototype.constructor = Event; // events: // public properties: /** * The type of event. * @property type * @type String **/ p.type = null; /** * The object that generated an event. * @property target * @type Object * @default null * @readonly */ p.target = null; /** * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event * is generated from childObj, then a listener on parentObj would receive the event with * target=childObj (the original target) and currentTarget=parentObj (where the listener was added). * @property currentTarget * @type Object * @default null * @readonly */ p.currentTarget = null; /** * For bubbling events, this indicates the current event phase:<OL> * <LI> capture phase: starting from the top parent to the target</LI> * <LI> at target phase: currently being dispatched from the target</LI> * <LI> bubbling phase: from the target to the top parent</LI> * </OL> * @property eventPhase * @type Number * @default 0 * @readonly */ p.eventPhase = 0; /** * Indicates whether the event will bubble through the display list. * @property bubbles * @type Boolean * @default false * @readonly */ p.bubbles = false; /** * Indicates whether the default behaviour of this event can be cancelled via * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor. * @property cancelable * @type Boolean * @default false * @readonly */ p.cancelable = false; /** * The epoch time at which this event was created. * @property timeStamp * @type Number * @default 0 * @readonly */ p.timeStamp = 0; /** * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called * on this event. * @property defaultPrevented * @type Boolean * @default false * @readonly */ p.defaultPrevented = false; /** * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event. * @property propagationStopped * @type Boolean * @default false * @readonly */ p.propagationStopped = false; /** * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called * on this event. * @property immediatePropagationStopped * @type Boolean * @default false * @readonly */ p.immediatePropagationStopped = false; /** * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event. * @property removed * @type Boolean * @default false * @readonly */ p.removed = false; // constructor: /** * Initialization method. * @method initialize * @param {String} type The event type. * @param {Boolean} bubbles Indicates whether the event will bubble through the display list. * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled. * @protected **/ p.initialize = function(type, bubbles, cancelable) { this.type = type; this.bubbles = bubbles; this.cancelable = cancelable; this.timeStamp = (new Date()).getTime(); }; // public methods: /** * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true. * Mirrors the DOM event standard. * @method preventDefault **/ p.preventDefault = function() { this.defaultPrevented = true; }; /** * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true. * Mirrors the DOM event standard. * @method stopPropagation **/ p.stopPropagation = function() { this.propagationStopped = true; }; /** * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true. * Mirrors the DOM event standard. * @method stopImmediatePropagation **/ p.stopImmediatePropagation = function() { this.immediatePropagationStopped = this.propagationStopped = true; }; /** * Causes the active listener to be removed via removeEventListener(); * * myBtn.addEventListener("click", function(evt) { * // do stuff... * evt.remove(); // removes this listener. * }); * * @method remove **/ p.remove = function() { this.removed = true; }; /** * Returns a clone of the Event instance. * @method clone * @return {Event} a clone of the Event instance. **/ p.clone = function() { return new Event(this.type, this.bubbles, this.cancelable); }; /** * Returns a string representation of this object. * @method toString * @return {String} a string representation of the instance. **/ p.toString = function() { return "[Event (type="+this.type+")]"; }; createjs.Event = Event; }()); /* * EventDispatcher * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * @module CreateJS */ // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; /** * EventDispatcher provides methods for managing queues of event listeners and dispatching events. * * You can either extend EventDispatcher or mix its methods into an existing prototype or instance by using the * EventDispatcher {{#crossLink "EventDispatcher/initialize"}}{{/crossLink}} method. * * Together with the CreateJS Event class, EventDispatcher provides an extended event model that is based on the * DOM Level 2 event model, including addEventListener, removeEventListener, and dispatchEvent. It supports * bubbling / capture, preventDefault, stopPropagation, stopImmediatePropagation, and handleEvent. * * EventDispatcher also exposes a {{#crossLink "EventDispatcher/on"}}{{/crossLink}} method, which makes it easier * to create scoped listeners, listeners that only run once, and listeners with associated arbitrary data. The * {{#crossLink "EventDispatcher/off"}}{{/crossLink}} method is merely an alias to * {{#crossLink "EventDispatcher/removeEventListener"}}{{/crossLink}}. * * Another addition to the DOM Level 2 model is the {{#crossLink "EventDispatcher/removeAllEventListeners"}}{{/crossLink}} * method, which can be used to listeners for all events, or listeners for a specific event. The Event object also * includes a {{#crossLink "Event/remove"}}{{/crossLink}} method which removes the active listener. * * <h4>Example</h4> * Add EventDispatcher capabilities to the "MyClass" class. * * EventDispatcher.initialize(MyClass.prototype); * * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}). * * instance.addEventListener("eventName", handlerMethod); * function handlerMethod(event) { * console.log(event.target + " Was Clicked"); * } * * <b>Maintaining proper scope</b><br /> * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}} * method to subscribe to events simplifies this. * * instance.addEventListener("click", function(event) { * console.log(instance == this); // false, scope is ambiguous. * }); * * instance.on("click", function(event) { * console.log(instance == this); // true, "on" uses dispatcher scope by default. * }); * * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage scope. * * * @class EventDispatcher * @constructor **/ var EventDispatcher = function() { /* this.initialize(); */ // not needed. }; var p = EventDispatcher.prototype; EventDispatcher.prototype.constructor = EventDispatcher; /** * Static initializer to mix EventDispatcher methods into a target object or prototype. * * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class * EventDispatcher.initialize(myObject); // add to a specific instance * * @method initialize * @static * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a * prototype. **/ EventDispatcher.initialize = function(target) { target.addEventListener = p.addEventListener; target.on = p.on; target.removeEventListener = target.off = p.removeEventListener; target.removeAllEventListeners = p.removeAllEventListeners; target.hasEventListener = p.hasEventListener; target.dispatchEvent = p.dispatchEvent; target._dispatchEvent = p._dispatchEvent; target.willTrigger = p.willTrigger; }; // constructor: // private properties: /** * @protected * @property _listeners * @type Object **/ p._listeners = null; /** * @protected * @property _captureListeners * @type Object **/ p._captureListeners = null; // constructor: /** * Initialization method. * @method initialize * @protected **/ p.initialize = function() {}; // public methods: /** * Adds the specified event listener. Note that adding multiple listeners to the same function will result in * multiple callbacks getting fired. * * <h4>Example</h4> * * displayObject.addEventListener("click", handleClick); * function handleClick(event) { * // Click happened. * } * * @method addEventListener * @param {String} type The string type of the event. * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when * the event is dispatched. * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase. * @return {Function | Object} Returns the listener for chaining or assignment. **/ p.addEventListener = function(type, listener, useCapture) { var listeners; if (useCapture) { listeners = this._captureListeners = this._captureListeners||{}; } else { listeners = this._listeners = this._listeners||{}; } var arr = listeners[type]; if (arr) { this.removeEventListener(type, listener, useCapture); } arr = listeners[type]; // remove may have deleted the array if (!arr) { listeners[type] = [listener]; } else { arr.push(listener); } return listener; }; /** * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener * only run once, associate arbitrary data with the listener, and remove the listener. * * This method works by creating an anonymous wrapper function and subscribing it with addEventListener. * The created anonymous function is returned for use with .removeEventListener (or .off). * * <h4>Example</h4> * * var listener = myBtn.on("click", handleClick, null, false, {count:3}); * function handleClick(evt, data) { * data.count -= 1; * console.log(this == myBtn); // true - scope defaults to the dispatcher * if (data.count == 0) { * alert("clicked 3 times!"); * myBtn.off("click", listener); * // alternately: evt.remove(); * } * } * * @method on * @param {String} type The string type of the event. * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when * the event is dispatched. * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent). * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered. * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called. * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase. * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener. **/ p.on = function(type, listener, scope, once, data, useCapture) { if (listener.handleEvent) { scope = scope||listener; listener = listener.handleEvent; } scope = scope||this; return this.addEventListener(type, function(evt) { listener.call(scope, evt, data); once&&evt.remove(); }, useCapture); }; /** * Removes the specified event listener. * * <b>Important Note:</b> that you must pass the exact function reference used when the event was added. If a proxy * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or * closure will not work. * * <h4>Example</h4> * * displayObject.removeEventListener("click", handleClick); * * @method removeEventListener * @param {String} type The string type of the event. * @param {Function | Object} listener The listener function or object. * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase. **/ p.removeEventListener = function(type, listener, useCapture) { var listeners = useCapture ? this._captureListeners : this._listeners; if (!listeners) { return; } var arr = listeners[type]; if (!arr) { return; } for (var i=0,l=arr.length; i<l; i++) { if (arr[i] == listener) { if (l==1) { delete(listeners[type]); } // allows for faster checks. else { arr.splice(i,1); } break; } } }; /** * A shortcut to the removeEventListener method, with the same parameters and return value. This is a companion to the * .on method. * * @method off * @param {String} type The string type of the event. * @param {Function | Object} listener The listener function or object. * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase. **/ p.off = p.removeEventListener; /** * Removes all listeners for the specified type, or all listeners of all types. * * <h4>Example</h4> * * // Remove all listeners * displayObject.removeAllEventListeners(); * * // Remove all click listeners * displayObject.removeAllEventListeners("click"); * * @method removeAllEventListeners * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed. **/ p.removeAllEventListeners = function(type) { if (!type) { this._listeners = this._captureListeners = null; } else { if (this._listeners) { delete(this._listeners[type]); } if (this._captureListeners) { delete(this._captureListeners[type]); } } }; /** * Dispatches the specified event to all listeners. * * <h4>Example</h4> * * // Use a string event * this.dispatchEvent("complete"); * * // Use an Event instance * var event = new createjs.Event("progress"); * this.dispatchEvent(event); * * @method dispatchEvent * @param {Object | String | Event} eventObj An object with a "type" property, or a string type. * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used, * dispatchEvent will construct an Event instance with the specified type. * @return {Boolean} Returns the value of eventObj.defaultPrevented. **/ p.dispatchEvent = function(eventObj) { if (typeof eventObj == "string") { // won't bubble, so skip everything if there's no listeners: var listeners = this._listeners; if (!listeners || !listeners[eventObj]) { return false; } eventObj = new createjs.Event(eventObj); } else if (eventObj.target && eventObj.clone) { // redispatching an active event object, so clone it: eventObj = eventObj.clone(); } try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events if (!eventObj.bubbles || !this.parent) { this._dispatchEvent(eventObj, 2); } else { var top=this, list=[top]; while (top.parent) { list.push(top = top.parent); } var i, l=list.length; // capture & atTarget for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) { list[i]._dispatchEvent(eventObj, 1+(i==0)); } // bubbling for (i=1; i<l && !eventObj.propagationStopped; i++) { list[i]._dispatchEvent(eventObj, 3); } } return eventObj.defaultPrevented; }; /** * Indicates whether there is at least one listener for the specified event type. * @method hasEventListener * @param {String} type The string type of the event. * @return {Boolean} Returns true if there is at least one listener for the specified event. **/ p.hasEventListener = function(type) { var listeners = this._listeners, captureListeners = this._captureListeners; return !!((listeners && listeners[type]) || (captureListeners && captureListeners[type])); }; /** * Indicates whether there is at least one listener for the specified event type on this object or any of its * ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the * specified type is dispatched from this object, it will trigger at least one listener. * * This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire * event flow for a listener, not just this object. * @method willTrigger * @param {String} type The string type of the event. * @return {Boolean} Returns `true` if there is at least one listener for the specified event. **/ p.willTrigger = function(type) { var o = this; while (o) { if (o.hasEventListener(type)) { return true; } o = o.parent; } return false; }; /** * @method toString * @return {String} a string representation of the instance. **/ p.toString = function() { return "[EventDispatcher]"; }; // private methods: /** * @method _dispatchEvent * @param {Object | String | Event} eventObj * @param {Object} eventPhase * @protected **/ p._dispatchEvent = function(eventObj, eventPhase) { var l, listeners = (eventPhase==1) ? this._captureListeners : this._listeners; if (eventObj && listeners) { var arr = listeners[eventObj.type]; if (!arr||!(l=arr.length)) { return; } try { eventObj.currentTarget = this; } catch (e) {} try { eventObj.eventPhase = eventPhase; } catch (e) {} eventObj.removed = false; arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch for (var i=0; i<l && !eventObj.immediatePropagationStopped; i++) { var o = arr[i]; if (o.handleEvent) { o.handleEvent(eventObj); } else { o(eventObj); } if (eventObj.removed) { this.off(eventObj.type, o, eventPhase==1); eventObj.removed = false; } } } }; createjs.EventDispatcher = EventDispatcher; }()); /* * Ticker * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * @module CreateJS */ // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; // constructor: /** * The Ticker provides a centralized tick or heartbeat broadcast at a set interval. Listeners can subscribe to the tick * event to be notified when a set time interval has elapsed. * * Note that the interval that the tick event is called is a target interval, and may be broadcast at a slower interval * during times of high CPU load. The Ticker class uses a static interface (ex. <code>Ticker.getPaused()</code>) and * should not be instantiated. * * <h4>Example</h4> * * createjs.Ticker.addEventListener("tick", handleTick); * function handleTick(event) { * // Actions carried out each frame * if (!event.paused) { * // Actions carried out when the Ticker is not paused. * } * } * * To update a stage every tick, the {{#crossLink "Stage"}}{{/crossLink}} instance can also be used as a listener, as * it will automatically update when it receives a tick event: * * createjs.Ticker.addEventListener("tick", stage); * * @class Ticker * @uses EventDispatcher * @static **/ function Ticker() { throw "Ticker cannot be instantiated."; } // constants: /** * In this mode, Ticker uses the requestAnimationFrame API, but attempts to synch the ticks to target framerate. It * uses a simple heuristic that compares the time of the RAF return to the target time for the current frame and * dispatches the tick when the time is within a certain threshold. * * This mode has a higher variance for time between frames than TIMEOUT, but does not require that content be time * based as with RAF while gaining the benefits of that API (screen synch, background throttling). * * Variance is usually lowest for framerates that are a divisor of the RAF frequency. This is usually 60, so * framerates of 10, 12, 15, 20, and 30 work well. * * Falls back on TIMEOUT if the requestAnimationFrame API is not supported. * @property RAF_SYNCHED * @static * @type {String} * @default "synched" * @readonly **/ Ticker.RAF_SYNCHED = "synched"; /** * In this mode, Ticker passes through the requestAnimationFrame heartbeat, ignoring the target framerate completely. * Because requestAnimationFrame frequency is not deterministic, any content using this mode should be time based. * You can leverage {{#crossLink "Ticker/getTime"}}{{/crossLink}} and the tick event object's "delta" properties * to make this easier. * * Falls back on TIMEOUT if the requestAnimationFrame API is not supported. * @property RAF * @static * @type {String} * @default "raf" * @readonly **/ Ticker.RAF = "raf"; /** * In this mode, Ticker uses the setTimeout API. This provides predictable, adaptive frame timing, but does not * provide the benefits of requestAnimationFrame (screen synch, background throttling). * @property TIMEOUT * @static * @type {String} * @default "timer" * @readonly **/ Ticker.TIMEOUT = "timeout"; // static events: /** * Dispatched each tick. The event will be dispatched to each listener even when the Ticker has been paused using * {{#crossLink "Ticker/setPaused"}}{{/crossLink}}. * * <h4>Example</h4> * * createjs.Ticker.addEventListener("tick", handleTick); * function handleTick(event) { * console.log("Paused:", event.paused, event.delta); * } * * @event tick * @param {Object} target The object that dispatched the event. * @param {String} type The event type. * @param {Boolean} paused Indicates whether the ticker is currently paused. * @param {Number} delta The time elapsed in ms since the last tick. * @param {Number} time The total time in ms since Ticker was initialized. * @param {Number} runTime The total time in ms that Ticker was not paused since it was initialized. For example, * you could determine the amount of time that the Ticker has been paused since initialization with time-runTime. * @since 0.6.0 */ // public static properties: /** * Deprecated in favour of {{#crossLink "Ticker/timingMode"}}{{/crossLink}}, and will be removed in a future version. If true, timingMode will * use {{#crossLink "Ticker/RAF_SYNCHED"}}{{/crossLink}} by default. * @deprecated Deprecated in favour of {{#crossLink "Ticker/timingMode"}}{{/crossLink}}. * @property useRAF * @static * @type {Boolean} * @default false **/ Ticker.useRAF = false; /** * Specifies the timing api (setTimeout or requestAnimationFrame) and mode to use. See * {{#crossLink "Ticker/TIMEOUT"}}{{/crossLink}}, {{#crossLink "Ticker/RAF"}}{{/crossLink}}, and * {{#crossLink "Ticker/RAF_SYNCHED"}}{{/crossLink}} for mode details. * @property timingMode * @static * @type {String} * @default Ticker.TIMEOUT **/ Ticker.timingMode = null; /** * Specifies a maximum value for the delta property in the tick event object. This is useful when building time * based animations and systems to prevent issues caused by large time gaps caused by background tabs, system sleep, * alert dialogs, or other blocking routines. Double the expected frame duration is often an effective value * (ex. maxDelta=50 when running at 40fps). * * This does not impact any other values (ex. time, runTime, etc), so you may experience issues if you enable maxDelta * when using both delta and other values. * * If 0, there is no maximum. * @property maxDelta * @static * @type {number} * @default 0 */ Ticker.maxDelta = 0; // mix-ins: // EventDispatcher methods: Ticker.removeEventListener = null; Ticker.removeAllEventListeners = null; Ticker.dispatchEvent = null; Ticker.hasEventListener = null; Ticker._listeners = null; createjs.EventDispatcher.initialize(Ticker); // inject EventDispatcher methods. Ticker._addEventListener = Ticker.addEventListener; Ticker.addEventListener = function() { !Ticker._inited&&Ticker.init(); return Ticker._addEventListener.apply(Ticker, arguments); }; // private static properties: /** * @property _paused * @type {Boolean} * @protected **/ Ticker._paused = false; /** * @property _inited * @type {Boolean} * @protected **/ Ticker._inited = false; /** * @property _startTime * @type {Number} * @protected **/ Ticker._startTime = 0; /** * @property _pausedTime * @type {Number} * @protected **/ Ticker._pausedTime=0; /** * The number of ticks that have passed * @property _ticks * @type {Number} * @protected **/ Ticker._ticks = 0; /** * The number of ticks that have passed while Ticker has been paused * @property _pausedTicks * @type {Number} * @protected **/ Ticker._pausedTicks = 0; /** * @property _interval * @type {Number} * @protected **/ Ticker._interval = 50; /** * @property _lastTime * @type {Number} * @protected **/ Ticker._lastTime = 0; /** * @property _times * @type {Array} * @protected **/ Ticker._times = null; /** * @property _tickTimes * @type {Array} * @protected **/ Ticker._tickTimes = null; /** * Stores the timeout or requestAnimationFrame id. * @property _timerId * @type {Number} * @protected **/ Ticker._timerId = null; /** * True if currently using requestAnimationFrame, false if using setTimeout. * @property _raf * @type {Boolean} * @protected **/ Ticker._raf = true; // public static methods: /** * Starts the tick. This is called automatically when the first listener is added. * @method init * @static **/ Ticker.init = function() { if (Ticker._inited) { return; } Ticker._inited = true; Ticker._times = []; Ticker._tickTimes = []; Ticker._startTime = Ticker._getTime(); Ticker._times.push(Ticker._lastTime = 0); Ticker.setInterval(Ticker._interval); }; /** * Stops the Ticker and removes all listeners. Use init() to restart the Ticker. * @method reset * @static **/ Ticker.reset = function() { if (Ticker._raf) { var f = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame; f&&f(Ticker._timerId); } else { clearTimeout(Ticker._timerId); } Ticker.removeAllEventListeners("tick"); Ticker._timerId = null; Ticker._inited = false; }; /** * Sets the target time (in milliseconds) between ticks. Default is 50 (20 FPS). * * Note actual time between ticks may be more than requested depending on CPU load. * @method setInterval * @static * @param {Number} interval Time in milliseconds between ticks. Default value is 50. **/ Ticker.setInterval = function(interval) { Ticker._interval = interval; if (!Ticker._inited) { return; } Ticker._setupTick(); }; /** * Returns the current target time between ticks, as set with {{#crossLink "Ticker/setInterval"}}{{/crossLink}}. * @method getInterval * @static * @return {Number} The current target interval in milliseconds between tick events. **/ Ticker.getInterval = function() { return Ticker._interval; }; /** * Sets the target frame rate in frames per second (FPS). For example, with an interval of 40, <code>getFPS()</code> * will return 25 (1000ms per second divided by 40 ms per tick = 25fps). * @method setFPS * @static * @param {Number} value Target number of ticks broadcast per second. **/ Ticker.setFPS = function(value) { Ticker.setInterval(1000/value); }; /** * Returns the target frame rate in frames per second (FPS). For example, with an interval of 40, <code>getFPS()</code> * will return 25 (1000ms per second divided by 40 ms per tick = 25fps). * @method getFPS * @static * @return {Number} The current target number of frames / ticks broadcast per second. **/ Ticker.getFPS = function() { return 1000/Ticker._interval; }; /** * Returns the average time spent within a tick. This can vary significantly from the value provided by getMeasuredFPS * because it only measures the time spent within the tick execution stack. * * Example 1: With a target FPS of 20, getMeasuredFPS() returns 20fps, which indicates an average of 50ms between * the end of one tick and the end of the next. However, getMeasuredTickTime() returns 15ms. This indicates that * there may be up to 35ms of "idle" time between the end of one tick and the start of the next. * * Example 2: With a target FPS of 30, getFPS() returns 10fps, which indicates an average of 100ms between the end of * one tick and the end of the next. However, getMeasuredTickTime() returns 20ms. This would indicate that something * other than the tick is using ~80ms (another script, DOM rendering, etc). * @method getMeasuredTickTime * @static * @param {Number} [ticks] The number of previous ticks over which to measure the average time spent in a tick. * Defaults to the number of ticks per second. To get only the last tick's time, pass in 1. * @return {Number} The average time spent in a tick in milliseconds. **/ Ticker.getMeasuredTickTime = function(ticks) { var ttl=0, times=Ticker._tickTimes; if (!times || times.length < 1) { return -1; } // by default, calculate average for the past ~1 second: ticks = Math.min(times.length, ticks||(Ticker.getFPS()|0)); for (var i=0; i<ticks; i++) { ttl += times[i]; } return ttl/ticks; }; /** * Returns the actual frames / ticks per second. * @method getMeasuredFPS * @static * @param {Number} [ticks] The number of previous ticks over which to measure the actual frames / ticks per second. * Defaults to the number of ticks per second. * @return {Number} The actual frames / ticks per second. Depending on performance, this may differ * from the target frames per second. **/ Ticker.getMeasuredFPS = function(ticks) { var times = Ticker._times; if (!times || times.length < 2) { return -1; } // by default, calculate fps for the past ~1 second: ticks = Math.min(times.length-1, ticks||(Ticker.getFPS()|0)); return 1000/((times[0]-times[ticks])/ticks); }; /** * Changes the "paused" state of the Ticker, which can be retrieved by the {{#crossLink "Ticker/getPaused"}}{{/crossLink}} * method, and is passed as the "paused" property of the <code>tick</code> event. When the ticker is paused, all * listeners will still receive a tick event, but the <code>paused</code> property will be false. * * Note that in EaselJS v0.5.0 and earlier, "pauseable" listeners would <strong>not</strong> receive the tick * callback when Ticker was paused. This is no longer the case. * * <h4>Example</h4> * * createjs.Ticker.addEventListener("tick", handleTick); * createjs.Ticker.setPaused(true); * function handleTick(event) { * console.log("Paused:", event.paused, createjs.Ticker.getPaused()); * } * * @method setPaused * @static * @param {Boolean} value Indicates whether to pause (true) or unpause (false) Ticker. **/ Ticker.setPaused = function(value) { Ticker._paused = value; }; /** * Returns a boolean indicating whether Ticker is currently paused, as set with {{#crossLink "Ticker/setPaused"}}{{/crossLink}}. * When the ticker is paused, all listeners will still receive a tick event, but this value will be false. * * Note that in EaselJS v0.5.0 and earlier, "pauseable" listeners would <strong>not</strong> receive the tick * callback when Ticker was paused. This is no longer the case. * * <h4>Example</h4> * * createjs.Ticker.addEventListener("tick", handleTick); * createjs.Ticker.setPaused(true); * function handleTick(event) { * console.log("Paused:", createjs.Ticker.getPaused()); * } * * @method getPaused * @static * @return {Boolean} Whether the Ticker is currently paused. **/ Ticker.getPaused = function() { return Ticker._paused; }; /** * Returns the number of milliseconds that have elapsed since Ticker was initialized via {{#crossLink "Ticker/init"}}. * Returns -1 if Ticker has not been initialized. For example, you could use * this in a time synchronized animation to determine the exact amount of time that has elapsed. * @method getTime * @static * @param {Boolean} [runTime=false] If true only time elapsed while Ticker was not paused will be returned. * If false, the value returned will be total time elapsed since the first tick event listener was added. * @return {Number} Number of milliseconds that have elapsed since Ticker was initialized or -1. **/ Ticker.getTime = function(runTime) { return Ticker._startTime ? Ticker._getTime() - Ticker._startTime - (runTime ? Ticker._pausedTime : 0) : -1; }; /** * Similar to getTime(), but returns the time included with the current (or most recent) tick event object. * @method getEventTime * @param runTime {Boolean} [runTime=false] If true, the runTime property will be returned instead of time. * @returns {number} The time or runTime property from the most recent tick event or -1. */ Ticker.getEventTime = function(runTime) { return Ticker._startTime ? (Ticker._lastTime || Ticker._startTime) - (runTime ? Ticker._pausedTime : 0) : -1; }; /** * Returns the number of ticks that have been broadcast by Ticker. * @method getTicks * @static * @param {Boolean} pauseable Indicates whether to include ticks that would have been broadcast * while Ticker was paused. If true only tick events broadcast while Ticker is not paused will be returned. * If false, tick events that would have been broadcast while Ticker was paused will be included in the return * value. The default value is false. * @return {Number} of ticks that have been broadcast. **/ Ticker.getTicks = function(pauseable) { return Ticker._ticks - (pauseable ?Ticker._pausedTicks : 0); }; // private static methods: /** * @method _handleSynch * @static * @protected **/ Ticker._handleSynch = function() { Ticker._timerId = null; Ticker._setupTick(); // run if enough time has elapsed, with a little bit of flexibility to be early: if (Ticker._getTime() - Ticker._lastTime >= (Ticker._interval-1)*0.97) { Ticker._tick(); } }; /** * @method _handleRAF * @static * @protected **/ Ticker._handleRAF = function() { Ticker._timerId = null; Ticker._setupTick(); Ticker._tick(); }; /** * @method _handleTimeout * @static * @protected **/ Ticker._handleTimeout = function() { Ticker._timerId = null; Ticker._setupTick(); Ticker._tick(); }; /** * @method _setupTick * @static * @protected **/ Ticker._setupTick = function() { if (Ticker._timerId != null) { return; } // avoid duplicates var mode = Ticker.timingMode||(Ticker.useRAF&&Ticker.RAF_SYNCHED); if (mode == Ticker.RAF_SYNCHED || mode == Ticker.RAF) { var f = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame; if (f) { Ticker._timerId = f(mode == Ticker.RAF ? Ticker._handleRAF : Ticker._handleSynch); Ticker._raf = true; return; } } Ticker._raf = false; Ticker._timerId = setTimeout(Ticker._handleTimeout, Ticker._interval); }; /** * @method _tick * @static * @protected **/ Ticker._tick = function() { var time = Ticker._getTime(); var adjTime = time-Ticker._startTime; var elapsedTime = time-Ticker._lastTime; var paused = Ticker._paused; Ticker._ticks++; if (paused) { Ticker._pausedTicks++; Ticker._pausedTime += elapsedTime; } Ticker._lastTime = time; if (Ticker.hasEventListener("tick")) { var event = new createjs.Event("tick"); var maxDelta = Ticker.maxDelta; event.delta = (maxDelta && elapsedTime > maxDelta) ? maxDelta : elapsedTime; event.paused = paused; event.time = adjTime; event.runTime = adjTime-Ticker._pausedTime; Ticker.dispatchEvent(event); } Ticker._tickTimes.unshift(Ticker._getTime()-time); while (Ticker._tickTimes.length > 100) { Ticker._tickTimes.pop(); } Ticker._times.unshift(adjTime); while (Ticker._times.length > 100) { Ticker._times.pop(); } }; /** * @method _getTime * @static * @protected **/ var now = window.performance && (performance.now || performance.mozNow || performance.msNow || performance.oNow || performance.webkitNow); Ticker._getTime = function() { return (now&&now.call(performance))||(new Date().getTime()); }; createjs.Ticker = Ticker; }()); /* * Tween * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * The TweenJS Javascript library provides a simple but powerful tweening interface. It supports tweening of both * numeric object properties & CSS style properties, and allows you to chain tweens and actions together to create * complex sequences. * * <h4>Simple Tween</h4> * This tween will tween the target's alpha property from 0 to 1 for 1s then call the <code>handleComplete</code> function. * * target.alpha = 0; * Tween.get(target).to({alpha:1}, 1000).call(handleComplete); * function handleComplete() { * //Tween complete * } * * <strong>Arguments and Scope</strong> * Tween also supports a `call()` with arguments and/or a scope. If no scope is passed, then the function is called * anonymously (normal JavaScript behaviour). The scope is useful for maintaining scope when doing object-oriented * style development. * * Tween.get(target).to({alpha:0}) * .call(handleComplete, [argument1, argument2], this); * * <h4>Chainable Tween</h4> * This tween will wait 0.5s, tween the target's alpha property to 0 over 1s, set it's visible to false, then call the * <code>handleComplete</code> function. * * target.alpha = 1; * Tween.get(target).wait(500).to({alpha:0, visible:false}, 1000).call(handleComplete); * function handleComplete() { * //Tween complete * } * * <h4>Browser Support</h4> * TweenJS will work in all browsers. * * @module TweenJS * @main TweenJS */ // TODO: possibly add a END actionsMode (only runs actions that == position)? // TODO: evaluate a way to decouple paused from tick registration. // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; /** * A Tween instance tweens properties for a single target. Instance methods can be chained for easy construction and sequencing: * * <h4>Example</h4> * * target.alpha = 1; * Tween.get(target) * .wait(500) * .to({alpha:0, visible:false}, 1000) * .call(handleComplete); * function handleComplete() { * //Tween complete * } * * Multiple tweens can point to the same instance, however if they affect the same properties there could be unexpected * behaviour. To stop all tweens on an object, use {{#crossLink "Tween/removeTweens"}}{{/crossLink}} or pass <code>override:true</code> * in the props argument. * * Tween.get(target, {override:true}).to({x:100}); * * Subscribe to the "change" event to get notified when a property of the target is changed. * * Tween.get(target, {override:true}).to({x:100}).addEventListener("change", handleChange); * function handleChange(event) { * // The tween changed. * } * * See the Tween {{#crossLink "Tween/get"}}{{/crossLink}} method for additional param documentation. * @class Tween * @param {Object} target The target object that will have its properties tweened. * @param {Object} [props] The configuration properties to apply to this tween instance (ex. `{loop:true, paused:true}`. * All properties default to false. Supported props are:<UL> * <LI> loop: sets the loop property on this tween.</LI> * <LI> useTicks: uses ticks for all durations instead of milliseconds.</LI> * <LI> ignoreGlobalPause: sets the {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}} property on this tween.</LI> * <LI> override: if true, `Tween.removeTweens(target)` will be called to remove any other tweens with the same target. * <LI> paused: indicates whether to start the tween paused.</LI> * <LI> position: indicates the initial position for this tween.</LI> * <LI> onChange: specifies a listener for the "change" event.</LI> * </UL> * @param {Object} [pluginData] An object containing data for use by installed plugins. See individual * plugins' documentation for details. * @extends EventDispatcher * @constructor */ var Tween = function(target, props, pluginData) { this.initialize(target, props, pluginData); }; var p = Tween.prototype = new createjs.EventDispatcher(); Tween.prototype.constructor = Tween; // static interface: /** * Constant defining the none actionsMode for use with setPosition. * @property NONE * @type Number * @default 0 * @static */ Tween.NONE = 0; /** * Constant defining the loop actionsMode for use with setPosition. * @property LOOP * @type Number * @default 1 * @static */ Tween.LOOP = 1; /** * Constant defining the reverse actionsMode for use with setPosition. * @property REVERSE * @type Number * @default 2 * @static */ Tween.REVERSE = 2; /** * Constant returned by plugins to tell the tween not to use default assignment. * @property IGNORE * @type Object * @static */ Tween.IGNORE = {}; /** * @property _listeners * @type Array[Tween] * @static * @protected */ Tween._tweens = []; /** * @property _plugins * @type Object * @static * @protected */ Tween._plugins = {}; /** * Returns a new tween instance. This is functionally identical to using "new Tween(...)", but looks cleaner * with the chained syntax of TweenJS. * @example * var tween = createjs.Tween.get(target); * @method get * @param {Object} target The target object that will have its properties tweened. * @param {Object} [props] The configuration properties to apply to this tween instance (ex. <code>{loop:true, paused:true}</code>). * All properties default to false. Supported props are:<UL> * <LI> loop: sets the loop property on this tween.</LI> * <LI> useTicks: uses ticks for all durations instead of milliseconds.</LI> * <LI> ignoreGlobalPause: sets the {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}} property on this tween.</LI> * <LI> override: if true, Tween.removeTweens(target) will be called to remove any other tweens with the same target. * <LI> paused: indicates whether to start the tween paused.</LI> * <LI> position: indicates the initial position for this tween.</LI> * <LI> onChange: specifies a listener for the "change" event.</LI> * </UL> * @param {Object} [pluginData] An object containing data for use by installed plugins. See individual * plugins' documentation for details. * @param {Boolean} [override=false] If true, any previous tweens on the same target will be removed. This is the same as * calling <code>Tween.removeTweens(target)</code>. * @return {Tween} A reference to the created tween. Additional chained tweens, method calls, or callbacks can be * applied to the returned tween instance. * @static */ Tween.get = function(target, props, pluginData, override) { if (override) { Tween.removeTweens(target); } return new Tween(target, props, pluginData); }; /** * Advances all tweens. This typically uses the {{#crossLink "Ticker"}}{{/crossLink}} class, but you can call it * manually if you prefer to use your own "heartbeat" implementation. * @method tick * @param {Number} delta The change in time in milliseconds since the last tick. Required unless all tweens have * <code>useTicks</code> set to true. * @param {Boolean} paused Indicates whether a global pause is in effect. Tweens with {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}} * will ignore this, but all others will pause if this is `true`. * @static */ Tween.tick = function(delta, paused) { var tweens = Tween._tweens.slice(); // to avoid race conditions. for (var i=tweens.length-1; i>=0; i--) { var tween = tweens[i]; if ((paused && !tween.ignoreGlobalPause) || tween._paused) { continue; } tween.tick(tween._useTicks?1:delta); } }; /** * Handle events that result from Tween being used as an event handler. This is included to allow Tween to handle * tick events from <code>createjs.Ticker</code>. No other events are handled in Tween. * @method handleEvent * @param {Object} event An event object passed in by the {{#crossLink "EventDispatcher"}}{{/crossLink}}. Will * usually be of type "tick". * @private * @static * @since 0.4.2 */ Tween.handleEvent = function(event) { if (event.type == "tick") { this.tick(event.delta, event.paused); } }; /** * Removes all existing tweens for a target. This is called automatically by new tweens if the <code>override</code> * property is <code>true</code>. * @method removeTweens * @param {Object} target The target object to remove existing tweens from. * @static */ Tween.removeTweens = function(target) { if (!target.tweenjs_count) { return; } var tweens = Tween._tweens; for (var i=tweens.length-1; i>=0; i--) { var tween = tweens[i]; if (tween._target == target) { tween._paused = true; tweens.splice(i, 1); } } target.tweenjs_count = 0; }; /** * Stop and remove all existing tweens. * @method removeAllTweens * @static * @since 0.4.1 */ Tween.removeAllTweens = function() { var tweens = Tween._tweens; for (var i= 0, l=tweens.length; i<l; i++) { var tween = tweens[i]; tween._paused = true; tween.target.tweenjs_count = 0; } tweens.length = 0; }; /** * Indicates whether there are any active tweens (and how many) on the target object (if specified) or in general. * @method hasActiveTweens * @param {Object} [target] The target to check for active tweens. If not specified, the return value will indicate * if there are any active tweens on any target. * @return {Boolean} If there are active tweens. * @static */ Tween.hasActiveTweens = function(target) { if (target) { return target.tweenjs_count; } return Tween._tweens && !!Tween._tweens.length; }; /** * Installs a plugin, which can modify how certain properties are handled when tweened. See the {{#crossLink "CSSPlugin"}}{{/crossLink}} * for an example of how to write TweenJS plugins. * @method installPlugin * @static * @param {Object} plugin The plugin class to install * @param {Array} properties An array of properties that the plugin will handle. */ Tween.installPlugin = function(plugin, properties) { var priority = plugin.priority; if (priority == null) { plugin.priority = priority = 0; } for (var i=0,l=properties.length,p=Tween._plugins;i<l;i++) { var n = properties[i]; if (!p[n]) { p[n] = [plugin]; } else { var arr = p[n]; for (var j=0,jl=arr.length;j<jl;j++) { if (priority < arr[j].priority) { break; } } p[n].splice(j,0,plugin); } } }; /** * Registers or unregisters a tween with the ticking system. * @method _register * @param {Tween} tween The tween instance to register or unregister. * @param {Boolean} value If true, the tween is registered. If false the tween is unregistered. * @static * @protected */ Tween._register = function(tween, value) { var target = tween._target; var tweens = Tween._tweens; if (value) { // TODO: this approach might fail if a dev is using sealed objects in ES5 if (target) { target.tweenjs_count = target.tweenjs_count ? target.tweenjs_count+1 : 1; } tweens.push(tween); if (!Tween._inited && createjs.Ticker) { createjs.Ticker.addEventListener("tick", Tween); Tween._inited = true; } } else { if (target) { target.tweenjs_count--; } var i = tweens.length; while (i--) { if (tweens[i] == tween) { tweens.splice(i, 1); return; } } } }; // public properties: /** * Causes this tween to continue playing when a global pause is active. For example, if TweenJS is using {{#crossLink "Ticker"}}{{/crossLink}}, * then setting this to true (the default) will cause this tween to be paused when <code>Ticker.setPaused(true)</code> * is called. See the Tween {{#crossLink "Tween/tick"}}{{/crossLink}} method for more info. Can be set via the props * parameter. * @property ignoreGlobalPause * @type Boolean * @default false */ p.ignoreGlobalPause = false; /** * If true, the tween will loop when it reaches the end. Can be set via the props param. * @property loop * @type {Boolean} * @default false */ p.loop = false; /** * Read-only. Specifies the total duration of this tween in milliseconds (or ticks if useTicks is true). * This value is automatically updated as you modify the tween. Changing it directly could result in unexpected * behaviour. * @property duration * @type {Number} * @default 0 */ p.duration = 0; /** * Allows you to specify data that will be used by installed plugins. Each plugin uses this differently, but in general * you specify data by setting it to a property of pluginData with the same name as the plugin class. * @example * myTween.pluginData.PluginClassName = data; * <br/> * Also, most plugins support a property to enable or disable them. This is typically the plugin class name followed by "_enabled".<br/> * @example * myTween.pluginData.PluginClassName_enabled = false;<br/> * <br/> * Some plugins also store instance data in this object, usually in a property named _PluginClassName. * See the documentation for individual plugins for more details. * @property pluginData * @type {Object} */ p.pluginData = null; // TODO: deprecated. /** * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "Tween/change:event"}}{{/crossLink}} * event. * @property onChange * @type {Function} * @deprecated Use addEventListener and the "change" event. */ /** * Read-only. The target of this tween. This is the object on which the tweened properties will be changed. Changing * this property after the tween is created will not have any effect. * @property target * @type {Object} */ p.target = null; /** * Read-only. The current normalized position of the tween. This will always be a value between 0 and duration. * Changing this property directly will have no effect. * @property position * @type {Object} */ p.position = null; /** * Read-only. Indicates the tween's current position is within a passive wait. * @property passive * @type {Boolean} **/ p.passive = false; // events: /** * Called whenever the tween's position changes. * @event change * @since 0.4.0 **/ // private properties: /** * @property _paused * @type {Boolean} * @default false * @protected */ p._paused = false; /** * @property _curQueueProps * @type {Object} * @protected */ p._curQueueProps = null; /** * @property _initQueueProps * @type {Object} * @protected */ p._initQueueProps = null; /** * @property _steps * @type {Array} * @protected */ p._steps = null; /** * @property _actions * @type {Array} * @protected */ p._actions = null; /** * Raw position. * @property _prevPosition * @type {Number} * @default 0 * @protected */ p._prevPosition = 0; /** * The position within the current step. * @property _stepPosition * @type {Number} * @default 0 * @protected */ p._stepPosition = 0; // this is needed by MovieClip. /** * Normalized position. * @property _prevPos * @type {Number} * @default -1 * @protected */ p._prevPos = -1; /** * @property _target * @type {Object} * @protected */ p._target = null; /** * @property _useTicks * @type {Boolean} * @default false * @protected */ p._useTicks = false; /** * @property _inited * @type {boolean} * @default false * @protected */ p._inited = false; // constructor: /** * @method initialize * @param {Object} target * @param {Object} props * @param {Object} pluginData * @protected */ p.initialize = function(target, props, pluginData) { this.target = this._target = target; if (props) { this._useTicks = props.useTicks; this.ignoreGlobalPause = props.ignoreGlobalPause; this.loop = props.loop; props.onChange&&this.addEventListener("change", props.onChange); if (props.override) { Tween.removeTweens(target); } } this.pluginData = pluginData || {}; this._curQueueProps = {}; this._initQueueProps = {}; this._steps = []; this._actions = []; if (props&&props.paused) { this._paused=true; } else { Tween._register(this,true); } if (props&&props.position!=null) { this.setPosition(props.position, Tween.NONE); } }; // public methods: /** * Queues a wait (essentially an empty tween). * @example * //This tween will wait 1s before alpha is faded to 0. * createjs.Tween.get(target).wait(1000).to({alpha:0}, 1000); * @method wait * @param {Number} duration The duration of the wait in milliseconds (or in ticks if <code>useTicks</code> is true). * @param {Boolean} passive Tween properties will not be updated during a passive wait. This * is mostly useful for use with Timeline's that contain multiple tweens affecting the same target * at different times. * @return {Tween} This tween instance (for chaining calls). **/ p.wait = function(duration, passive) { if (duration == null || duration <= 0) { return this; } var o = this._cloneProps(this._curQueueProps); return this._addStep({d:duration, p0:o, e:this._linearEase, p1:o, v:passive}); }; /** * Queues a tween from the current values to the target properties. Set duration to 0 to jump to these value. * Numeric properties will be tweened from their current value in the tween to the target value. Non-numeric * properties will be set at the end of the specified duration. * @example * createjs.Tween.get(target).to({alpha:0}, 1000); * @method to * @param {Object} props An object specifying property target values for this tween (Ex. <code>{x:300}</code> would tween the x * property of the target to 300). * @param {Number} duration Optional. The duration of the wait in milliseconds (or in ticks if <code>useTicks</code> is true). * Defaults to 0. * @param {Function} ease Optional. The easing function to use for this tween. Defaults to a linear ease. * @return {Tween} This tween instance (for chaining calls). */ p.to = function(props, duration, ease) { if (isNaN(duration) || duration < 0) { duration = 0; } return this._addStep({d:duration||0, p0:this._cloneProps(this._curQueueProps), e:ease, p1:this._cloneProps(this._appendQueueProps(props))}); }; /** * Queues an action to call the specified function. * @example * //would call myFunction() after 1s. * myTween.wait(1000).call(myFunction); * @method call * @param {Function} callback The function to call. * @param {Array} params Optional. The parameters to call the function with. If this is omitted, then the function * will be called with a single param pointing to this tween. * @param {Object} scope Optional. The scope to call the function in. If omitted, it will be called in the target's * scope. * @return {Tween} This tween instance (for chaining calls). */ p.call = function(callback, params, scope) { return this._addAction({f:callback, p:params ? params : [this], o:scope ? scope : this._target}); }; // TODO: add clarification between this and a 0 duration .to: /** * Queues an action to set the specified props on the specified target. If target is null, it will use this tween's * target. * @example * myTween.wait(1000).set({visible:false},foo); * @method set * @param {Object} props The properties to set (ex. <code>{visible:false}</code>). * @param {Object} target Optional. The target to set the properties on. If omitted, they will be set on the tween's target. * @return {Tween} This tween instance (for chaining calls). */ p.set = function(props, target) { return this._addAction({f:this._set, o:this, p:[props, target ? target : this._target]}); }; /** * Queues an action to to play (unpause) the specified tween. This enables you to sequence multiple tweens. * @example * myTween.to({x:100},500).play(otherTween); * @method play * @param {Tween} tween The tween to play. * @return {Tween} This tween instance (for chaining calls). */ p.play = function(tween) { if (!tween) { tween = this; } return this.call(tween.setPaused, [false], tween); }; /** * Queues an action to to pause the specified tween. * @method pause * @param {Tween} tween The tween to play. If null, it pauses this tween. * @return {Tween} This tween instance (for chaining calls) */ p.pause = function(tween) { if (!tween) { tween = this; } return this.call(tween.setPaused, [true], tween); }; /** * Advances the tween to a specified position. * @method setPosition * @param {Number} value The position to seek to in milliseconds (or ticks if useTicks is true). * @param {Number} actionsMode Optional parameter specifying how actions are handled (ie. call, set, play, pause): * <code>Tween.NONE</code> (0) - run no actions. <code>Tween.LOOP</code> (1) - if new position is less than old, then run all actions * between old and duration, then all actions between 0 and new. Defaults to <code>LOOP</code>. <code>Tween.REVERSE</code> (2) - if new * position is less than old, run all actions between them in reverse. * @return {Boolean} Returns true if the tween is complete (ie. the full tween has run & loop is false). */ p.setPosition = function(value, actionsMode) { if (value < 0) { value = 0; } if (actionsMode == null) { actionsMode = 1; } // normalize position: var t = value; var end = false; if (t >= this.duration) { if (this.loop) { t = t%this.duration; } else { t = this.duration; end = true; } } if (t == this._prevPos) { return end; } var prevPos = this._prevPos; this.position = this._prevPos = t; // set this in advance in case an action modifies position. this._prevPosition = value; // handle tweens: if (this._target) { if (end) { // addresses problems with an ending zero length step. this._updateTargetProps(null,1); } else if (this._steps.length > 0) { // find our new tween index: for (var i=0, l=this._steps.length; i<l; i++) { if (this._steps[i].t > t) { break; } } var step = this._steps[i-1]; this._updateTargetProps(step,(this._stepPosition = t-step.t)/step.d); } } // run actions: if (actionsMode != 0 && this._actions.length > 0) { if (this._useTicks) { // only run the actions we landed on. this._runActions(t,t); } else if (actionsMode == 1 && t<prevPos) { if (prevPos != this.duration) { this._runActions(prevPos, this.duration); } this._runActions(0, t, true); } else { this._runActions(prevPos, t); } } if (end) { this.setPaused(true); } this.dispatchEvent("change"); return end; }; /** * Advances this tween by the specified amount of time in milliseconds (or ticks if <code>useTicks</code> is true). * This is normally called automatically by the Tween engine (via <code>Tween.tick</code>), but is exposed for * advanced uses. * @method tick * @param {Number} delta The time to advance in milliseconds (or ticks if <code>useTicks</code> is true). */ p.tick = function(delta) { if (this._paused) { return; } this.setPosition(this._prevPosition+delta); }; /** * Pauses or plays this tween. * @method setPaused * @param {Boolean} value Indicates whether the tween should be paused (true) or played (false). * @return {Tween} This tween instance (for chaining calls) */ p.setPaused = function(value) { if (this._paused === !!value) { return this; } this._paused = !!value; Tween._register(this, !value); return this; }; // tiny api (primarily for tool output): p.w = p.wait; p.t = p.to; p.c = p.call; p.s = p.set; /** * Returns a string representation of this object. * @method toString * @return {String} a string representation of the instance. */ p.toString = function() { return "[Tween]"; }; /** * @method clone * @protected */ p.clone = function() { throw("Tween can not be cloned.") }; // private methods: /** * @method _updateTargetProps * @param {Object} step * @param {Number} ratio * @protected */ p._updateTargetProps = function(step, ratio) { var p0,p1,v,v0,v1,arr; if (!step && ratio == 1) { // GDS: when does this run? Just at the very end? Shouldn't. this.passive = false; p0 = p1 = this._curQueueProps; } else { this.passive = !!step.v; if (this.passive) { return; } // don't update props. // apply ease to ratio. if (step.e) { ratio = step.e(ratio,0,1,1); } p0 = step.p0; p1 = step.p1; } for (var n in this._initQueueProps) { if ((v0 = p0[n]) == null) { p0[n] = v0 = this._initQueueProps[n]; } if ((v1 = p1[n]) == null) { p1[n] = v1 = v0; } if (v0 == v1 || ratio == 0 || ratio == 1 || (typeof(v0) != "number")) { // no interpolation - either at start, end, values don't change, or the value is non-numeric. v = ratio == 1 ? v1 : v0; } else { v = v0+(v1-v0)*ratio; } var ignore = false; if (arr = Tween._plugins[n]) { for (var i=0,l=arr.length;i<l;i++) { var v2 = arr[i].tween(this, n, v, p0, p1, ratio, !!step&&p0==p1, !step); if (v2 == Tween.IGNORE) { ignore = true; } else { v = v2; } } } if (!ignore) { this._target[n] = v; } } }; /** * @method _runActions * @param {Number} startPos * @param {Number} endPos * @param {Boolean} includeStart * @protected */ p._runActions = function(startPos, endPos, includeStart) { var sPos = startPos; var ePos = endPos; var i = -1; var j = this._actions.length; var k = 1; if (startPos > endPos) { // running backwards, flip everything: sPos = endPos; ePos = startPos; i = j; j = k = -1; } while ((i+=k) != j) { var action = this._actions[i]; var pos = action.t; if (pos == ePos || (pos > sPos && pos < ePos) || (includeStart && pos == startPos) ) { action.f.apply(action.o, action.p); } } }; /** * @method _appendQueueProps * @param {Object} o * @protected */ p._appendQueueProps = function(o) { var arr,oldValue,i, l, injectProps; for (var n in o) { if (this._initQueueProps[n] === undefined) { oldValue = this._target[n]; // init plugins: if (arr = Tween._plugins[n]) { for (i=0,l=arr.length;i<l;i++) { oldValue = arr[i].init(this, n, oldValue); } } this._initQueueProps[n] = this._curQueueProps[n] = (oldValue===undefined) ? null : oldValue; } else { oldValue = this._curQueueProps[n]; } } for (var n in o) { oldValue = this._curQueueProps[n]; if (arr = Tween._plugins[n]) { injectProps = injectProps||{}; for (i=0, l=arr.length;i<l;i++) { // TODO: remove the check for .step in the next version. It's here for backwards compatibility. if (arr[i].step) { arr[i].step(this, n, oldValue, o[n], injectProps); } } } this._curQueueProps[n] = o[n]; } if (injectProps) { this._appendQueueProps(injectProps); } return this._curQueueProps; }; /** * @method _cloneProps * @param {Object} props * @protected */ p._cloneProps = function(props) { var o = {}; for (var n in props) { o[n] = props[n]; } return o; }; /** * @method _addStep * @param {Object} o * @protected */ p._addStep = function(o) { if (o.d > 0) { this._steps.push(o); o.t = this.duration; this.duration += o.d; } return this; }; /** * @method _addAction * @param {Object} o * @protected */ p._addAction = function(o) { o.t = this.duration; this._actions.push(o); return this; }; /** * @method _set * @param {Object} props * @param {Object} o * @protected */ p._set = function(props, o) { for (var n in props) { o[n] = props[n]; } }; createjs.Tween = Tween; }()); /* * Timeline * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * @module TweenJS */ // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; /** * The Timeline class synchronizes multiple tweens and allows them to be controlled as a group. Please note that if a * timeline is looping, the tweens on it may appear to loop even if the "loop" property of the tween is false. * @class Timeline * @param {Array} tweens An array of Tweens to add to this timeline. See addTween for more info. * @param {Object} labels An object defining labels for using {{#crossLink "Timeline/gotoAndPlay"}}{{/crossLink}}/{{#crossLink "Timeline/gotoAndStop"}}{{/crossLink}}. * See {{#crossLink "Timeline/setLabels"}}{{/crossLink}} * for details. * @param {Object} props The configuration properties to apply to this tween instance (ex. `{loop:true}`). All properties * default to false. Supported props are:<UL> * <LI> loop: sets the loop property on this tween.</LI> * <LI> useTicks: uses ticks for all durations instead of milliseconds.</LI> * <LI> ignoreGlobalPause: sets the ignoreGlobalPause property on this tween.</LI> * <LI> paused: indicates whether to start the tween paused.</LI> * <LI> position: indicates the initial position for this timeline.</LI> * <LI> onChange: specifies a listener to add for the {{#crossLink "Timeline/change:event"}}{{/crossLink}} event.</LI> * </UL> * @extends EventDispatcher * @constructor **/ var Timeline = function(tweens, labels, props) { this.initialize(tweens, labels, props); }; var p = Timeline.prototype = new createjs.EventDispatcher(); Timeline.prototype.constructor = Timeline; // public properties: /** * Causes this timeline to continue playing when a global pause is active. * @property ignoreGlobalPause * @type Boolean **/ p.ignoreGlobalPause = false; /** * Read-only property specifying the total duration of this timeline in milliseconds (or ticks if useTicks is true). * This value is usually automatically updated as you modify the timeline. See updateDuration for more information. * @property duration * @type Number **/ p.duration = 0; /** * If true, the timeline will loop when it reaches the end. Can be set via the props param. * @property loop * @type Boolean **/ p.loop = false; // TODO: deprecated. /** * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "Timeline/change:event"}}{{/crossLink}} * event. * @property onChange * @type Function * @deprecated Use addEventListener and the "change" event. **/ /** * Read-only. The current normalized position of the timeline. This will always be a value between 0 and duration. * Changing this property directly will have no effect. * @property position * @type Object **/ p.position = null; // events: /** * Called whenever the timeline's position changes. * @event change * @since 0.5.0 **/ // private properties: /** * @property _paused * @type Boolean * @protected **/ p._paused = false; /** * @property _tweens * @type Array[Tween] * @protected **/ p._tweens = null; /** * @property _labels * @type Object * @protected **/ p._labels = null; /** * @property _labelList * @type Array[Object] * @protected **/ p._labelList = null; /** * @property _prevPosition * @type Number * @default 0 * @protected **/ p._prevPosition = 0; /** * @property _prevPos * @type Number * @default -1 * @protected **/ p._prevPos = -1; /** * @property _useTicks * @type Boolean * @default false * @protected **/ p._useTicks = false; // constructor: /** * Initialization method. * @method initialize * @protected **/ p.initialize = function(tweens, labels, props) { this._tweens = []; if (props) { this._useTicks = props.useTicks; this.loop = props.loop; this.ignoreGlobalPause = props.ignoreGlobalPause; props.onChange&&this.addEventListener("change", props.onChange); } if (tweens) { this.addTween.apply(this, tweens); } this.setLabels(labels); if (props&&props.paused) { this._paused=true; } else { createjs.Tween._register(this,true); } if (props&&props.position!=null) { this.setPosition(props.position, createjs.Tween.NONE); } }; // public methods: /** * Adds one or more tweens (or timelines) to this timeline. The tweens will be paused (to remove them from the normal ticking system) * and managed by this timeline. Adding a tween to multiple timelines will result in unexpected behaviour. * @method addTween * @param tween The tween(s) to add. Accepts multiple arguments. * @return Tween The first tween that was passed in. **/ p.addTween = function(tween) { var l = arguments.length; if (l > 1) { for (var i=0; i<l; i++) { this.addTween(arguments[i]); } return arguments[0]; } else if (l == 0) { return null; } this.removeTween(tween); this._tweens.push(tween); tween.setPaused(true); tween._paused = false; tween._useTicks = this._useTicks; if (tween.duration > this.duration) { this.duration = tween.duration; } if (this._prevPos >= 0) { tween.setPosition(this._prevPos, createjs.Tween.NONE); } return tween; }; /** * Removes one or more tweens from this timeline. * @method removeTween * @param tween The tween(s) to remove. Accepts multiple arguments. * @return Boolean Returns true if all of the tweens were successfully removed. **/ p.removeTween = function(tween) { var l = arguments.length; if (l > 1) { var good = true; for (var i=0; i<l; i++) { good = good && this.removeTween(arguments[i]); } return good; } else if (l == 0) { return false; } var tweens = this._tweens; var i = tweens.length; while (i--) { if (tweens[i] == tween) { tweens.splice(i, 1); if (tween.duration >= this.duration) { this.updateDuration(); } return true; } } return false; }; /** * Adds a label that can be used with {{#crossLink "Timeline/gotoAndPlay"}}{{/crossLink}}/{{#crossLink "Timeline/gotoAndStop"}}{{/crossLink}}. * @method addLabel * @param {String} label The label name. * @param {Number} position The position this label represents. **/ p.addLabel = function(label, position) { this._labels[label] = position; var list = this._labelList; if (list) { for (var i= 0,l=list.length; i<l; i++) { if (position < list[i].position) { break; } } list.splice(i, 0, {label:label, position:position}); } }; /** * Defines labels for use with gotoAndPlay/Stop. Overwrites any previously set labels. * @method setLabels * @param {Object} o An object defining labels for using gotoAndPlay/Stop in the form `{labelName:time}` where time is in * milliseconds (or ticks if `useTicks` is true). **/ p.setLabels = function(o) { this._labels = o ? o : {}; }; /** * Returns a sorted list of the labels defined on this timeline. * @method getLabels * @return {Array[Object]} A sorted array of objects with label and position properties. **/ p.getLabels = function() { var list = this._labelList; if (!list) { list = this._labelList = []; var labels = this._labels; for (var n in labels) { list.push({label:n, position:labels[n]}); } list.sort(function (a,b) { return a.position- b.position; }); } return list; }; /** * Returns the name of the label on or immediately before the current position. For example, given a timeline with * two labels, "first" on frame index 4, and "second" on frame 8, getCurrentLabel would return:<UL> * <LI>null if the current position is 2.</LI> * <LI>"first" if the current position is 4.</LI> * <LI>"first" if the current position is 7.</LI> * <LI>"second" if the current position is 15.</LI></UL> * @method getCurrentLabel * @return {String} The name of the current label or null if there is no label **/ p.getCurrentLabel = function() { var labels = this.getLabels(); var pos = this.position; var l = labels.length; if (l) { for (var i = 0; i<l; i++) { if (pos < labels[i].position) { break; } } return (i==0) ? null : labels[i-1].label; } return null; }; /** * Unpauses this timeline and jumps to the specified position or label. * @method gotoAndPlay * @param {String|Number} positionOrLabel The position in milliseconds (or ticks if `useTicks` is true) or label to jump to. **/ p.gotoAndPlay = function(positionOrLabel) { this.setPaused(false); this._goto(positionOrLabel); }; /** * Pauses this timeline and jumps to the specified position or label. * @method gotoAndStop * @param {String|Number} positionOrLabel The position in milliseconds (or ticks if `useTicks` is true) or label to jump to. **/ p.gotoAndStop = function(positionOrLabel) { this.setPaused(true); this._goto(positionOrLabel); }; /** * Advances the timeline to the specified position. * @method setPosition * @param {Number} value The position to seek to in milliseconds (or ticks if `useTicks` is true). * @param {Number} [actionsMode] parameter specifying how actions are handled. See the Tween {{#crossLink "Tween/setPosition"}}{{/crossLink}} * method for more details. * @return {Boolean} Returns true if the timeline is complete (ie. the full timeline has run & loop is false). **/ p.setPosition = function(value, actionsMode) { if (value < 0) { value = 0; } var t = this.loop ? value%this.duration : value; var end = !this.loop && value >= this.duration; if (t == this._prevPos) { return end; } this._prevPosition = value; this.position = this._prevPos = t; // in case an action changes the current frame. for (var i=0, l=this._tweens.length; i<l; i++) { this._tweens[i].setPosition(t, actionsMode); if (t != this._prevPos) { return false; } // an action changed this timeline's position. } if (end) { this.setPaused(true); } this.dispatchEvent("change"); return end; }; /** * Pauses or plays this timeline. * @method setPaused * @param {Boolean} value Indicates whether the tween should be paused (true) or played (false). **/ p.setPaused = function(value) { this._paused = !!value; createjs.Tween._register(this, !value); }; /** * Recalculates the duration of the timeline. * The duration is automatically updated when tweens are added or removed, but this method is useful * if you modify a tween after it was added to the timeline. * @method updateDuration **/ p.updateDuration = function() { this.duration = 0; for (var i=0,l=this._tweens.length; i<l; i++) { var tween = this._tweens[i]; if (tween.duration > this.duration) { this.duration = tween.duration; } } }; /** * Advances this timeline by the specified amount of time in milliseconds (or ticks if useTicks is true). * This is normally called automatically by the Tween engine (via Tween.tick), but is exposed for advanced uses. * @method tick * @param {Number} delta The time to advance in milliseconds (or ticks if useTicks is true). **/ p.tick = function(delta) { this.setPosition(this._prevPosition+delta); }; /** * If a numeric position is passed, it is returned unchanged. If a string is passed, the position of the * corresponding frame label will be returned, or null if a matching label is not defined. * @method resolve * @param {String|Number} positionOrLabel A numeric position value or label string. **/ p.resolve = function(positionOrLabel) { var pos = Number(positionOrLabel); if (isNaN(pos)) { pos = this._labels[positionOrLabel]; } return pos; }; /** * Returns a string representation of this object. * @method toString * @return {String} a string representation of the instance. **/ p.toString = function() { return "[Timeline]"; }; /** * @method clone * @protected **/ p.clone = function() { throw("Timeline can not be cloned.") }; // private methods: /** * @method _goto * @protected **/ p._goto = function(positionOrLabel) { var pos = this.resolve(positionOrLabel); if (pos != null) { this.setPosition(pos); } }; createjs.Timeline = Timeline; }()); /* * Ease * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * @module TweenJS */ // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; // constructor: /** * The Ease class provides a collection of easing functions for use with TweenJS. It does not use the standard 4 param * easing signature. Instead it uses a single param which indicates the current linear ratio (0 to 1) of the tween. * * Most methods on Ease can be passed directly as easing functions: * * Tween.get(target).to({x:100}, 500, Ease.linear); * * However, methods beginning with "get" will return an easing function based on parameter values: * * Tween.get(target).to({y:200}, 500, Ease.getPowIn(2.2)); * * Please see the <a href="http://www.createjs.com/#!/TweenJS/demos/sparkTable">spark table demo</a> for an overview * of the different ease types on <a href="http://tweenjs.com">TweenJS.com</a>. * * <i>Equations derived from work by Robert Penner.</i> * @class Ease * @static **/ var Ease = function() { throw "Ease cannot be instantiated."; } // public static methods: /** * @method linear * @static **/ Ease.linear = function(t) { return t; } /** * Identical to linear. * @method none * @static **/ Ease.none = Ease.linear; /** * Mimics the simple -100 to 100 easing in Flash Pro. * @method get * @param amount A value from -1 (ease in) to 1 (ease out) indicating the strength and direction of the ease. * @static **/ Ease.get = function(amount) { if (amount < -1) { amount = -1; } if (amount > 1) { amount = 1; } return function(t) { if (amount==0) { return t; } if (amount<0) { return t*(t*-amount+1+amount); } return t*((2-t)*amount+(1-amount)); } } /** * Configurable exponential ease. * @method getPowIn * @param pow The exponent to use (ex. 3 would return a cubic ease). * @static **/ Ease.getPowIn = function(pow) { return function(t) { return Math.pow(t,pow); } } /** * Configurable exponential ease. * @method getPowOut * @param pow The exponent to use (ex. 3 would return a cubic ease). * @static **/ Ease.getPowOut = function(pow) { return function(t) { return 1-Math.pow(1-t,pow); } } /** * Configurable exponential ease. * @method getPowInOut * @param pow The exponent to use (ex. 3 would return a cubic ease). * @static **/ Ease.getPowInOut = function(pow) { return function(t) { if ((t*=2)<1) return 0.5*Math.pow(t,pow); return 1-0.5*Math.abs(Math.pow(2-t,pow)); } } /** * @method quadIn * @static **/ Ease.quadIn = Ease.getPowIn(2); /** * @method quadOut * @static **/ Ease.quadOut = Ease.getPowOut(2); /** * @method quadInOut * @static **/ Ease.quadInOut = Ease.getPowInOut(2); /** * @method cubicIn * @static **/ Ease.cubicIn = Ease.getPowIn(3); /** * @method cubicOut * @static **/ Ease.cubicOut = Ease.getPowOut(3); /** * @method cubicInOut * @static **/ Ease.cubicInOut = Ease.getPowInOut(3); /** * @method quartIn * @static **/ Ease.quartIn = Ease.getPowIn(4); /** * @method quartOut * @static **/ Ease.quartOut = Ease.getPowOut(4); /** * @method quartInOut * @static **/ Ease.quartInOut = Ease.getPowInOut(4); /** * @method quintIn * @static **/ Ease.quintIn = Ease.getPowIn(5); /** * @method quintOut * @static **/ Ease.quintOut = Ease.getPowOut(5); /** * @method quintInOut * @static **/ Ease.quintInOut = Ease.getPowInOut(5); /** * @method sineIn * @static **/ Ease.sineIn = function(t) { return 1-Math.cos(t*Math.PI/2); } /** * @method sineOut * @static **/ Ease.sineOut = function(t) { return Math.sin(t*Math.PI/2); } /** * @method sineInOut * @static **/ Ease.sineInOut = function(t) { return -0.5*(Math.cos(Math.PI*t) - 1) } /** * Configurable "back in" ease. * @method getBackIn * @param amount The strength of the ease. * @static **/ Ease.getBackIn = function(amount) { return function(t) { return t*t*((amount+1)*t-amount); } } /** * @method backIn * @static **/ Ease.backIn = Ease.getBackIn(1.7); /** * Configurable "back out" ease. * @method getBackOut * @param amount The strength of the ease. * @static **/ Ease.getBackOut = function(amount) { return function(t) { return (--t*t*((amount+1)*t + amount) + 1); } } /** * @method backOut * @static **/ Ease.backOut = Ease.getBackOut(1.7); /** * Configurable "back in out" ease. * @method getBackInOut * @param amount The strength of the ease. * @static **/ Ease.getBackInOut = function(amount) { amount*=1.525; return function(t) { if ((t*=2)<1) return 0.5*(t*t*((amount+1)*t-amount)); return 0.5*((t-=2)*t*((amount+1)*t+amount)+2); } } /** * @method backInOut * @static **/ Ease.backInOut = Ease.getBackInOut(1.7); /** * @method circIn * @static **/ Ease.circIn = function(t) { return -(Math.sqrt(1-t*t)- 1); } /** * @method circOut * @static **/ Ease.circOut = function(t) { return Math.sqrt(1-(--t)*t); } /** * @method circInOut * @static **/ Ease.circInOut = function(t) { if ((t*=2) < 1) return -0.5*(Math.sqrt(1-t*t)-1); return 0.5*(Math.sqrt(1-(t-=2)*t)+1); } /** * @method bounceIn * @static **/ Ease.bounceIn = function(t) { return 1-Ease.bounceOut(1-t); } /** * @method bounceOut * @static **/ Ease.bounceOut = function(t) { if (t < 1/2.75) { return (7.5625*t*t); } else if (t < 2/2.75) { return (7.5625*(t-=1.5/2.75)*t+0.75); } else if (t < 2.5/2.75) { return (7.5625*(t-=2.25/2.75)*t+0.9375); } else { return (7.5625*(t-=2.625/2.75)*t +0.984375); } } /** * @method bounceInOut * @static **/ Ease.bounceInOut = function(t) { if (t<0.5) return Ease.bounceIn (t*2) * .5; return Ease.bounceOut(t*2-1)*0.5+0.5; } /** * Configurable elastic ease. * @method getElasticIn * @param amplitude * @param period * @static **/ Ease.getElasticIn = function(amplitude,period) { var pi2 = Math.PI*2; return function(t) { if (t==0 || t==1) return t; var s = period/pi2*Math.asin(1/amplitude); return -(amplitude*Math.pow(2,10*(t-=1))*Math.sin((t-s)*pi2/period)); } } /** * @method elasticIn * @static **/ Ease.elasticIn = Ease.getElasticIn(1,0.3); /** * Configurable elastic ease. * @method getElasticOut * @param amplitude * @param period * @static **/ Ease.getElasticOut = function(amplitude,period) { var pi2 = Math.PI*2; return function(t) { if (t==0 || t==1) return t; var s = period/pi2 * Math.asin(1/amplitude); return (amplitude*Math.pow(2,-10*t)*Math.sin((t-s)*pi2/period )+1); } } /** * @method elasticOut * @static **/ Ease.elasticOut = Ease.getElasticOut(1,0.3); /** * Configurable elastic ease. * @method getElasticInOut * @param amplitude * @param period * @static **/ Ease.getElasticInOut = function(amplitude,period) { var pi2 = Math.PI*2; return function(t) { var s = period/pi2 * Math.asin(1/amplitude); if ((t*=2)<1) return -0.5*(amplitude*Math.pow(2,10*(t-=1))*Math.sin( (t-s)*pi2/period )); return amplitude*Math.pow(2,-10*(t-=1))*Math.sin((t-s)*pi2/period)*0.5+1; } } /** * @method elasticInOut * @static **/ Ease.elasticInOut = Ease.getElasticInOut(1,0.3*1.5); createjs.Ease = Ease; }()); /* * MotionGuidePlugin * Visit http://createjs.com/ for documentation, updates and examples. * * Copyright (c) 2010 gskinner.com, inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * @module TweenJS */ // namespace: this.createjs = this.createjs||{}; (function() { "use strict"; /** * A TweenJS plugin for working with motion guides. * * To use, install the plugin after TweenJS has loaded. Next tween the 'guide' property with an object as detailed below. * * createjs.MotionGuidePlugin.install(); * * <h4>Example</h4> * * // Using a Motion Guide * createjs.Tween.get(target).to({guide:{ path:[0,0, 0,200,200,200, 200,0,0,0] }},7000); * // Visualizing the line * graphics.moveTo(0,0).curveTo(0,200,200,200).curveTo(200,0,0,0); * * Each path needs pre-computation to ensure there's fast performance. Because of the pre-computation there's no * built in support for path changes mid tween. These are the Guide Object's properties:<UL> * <LI> path: Required, Array : The x/y points used to draw the path with a moveTo and 1 to n curveTo calls.</LI> * <LI> start: Optional, 0-1 : Initial position, default 0 except for when continuing along the same path.</LI> * <LI> end: Optional, 0-1 : Final position, default 1 if not specified.</LI> * <LI> orient: Optional, string : "fixed"/"auto"/"cw"/"ccw"<UL> * <LI>"fixed" forces the object to face down the path all movement (relative to start rotation),</LI> * <LI>"auto" rotates the object along the path relative to the line.</LI> * <LI>"cw"/"ccw" force clockwise or counter clockwise rotations including flash like behaviour</LI> * </UL></LI> * </UL> * Guide objects should not be shared between tweens even if all properties are identical, the library stores * information on these objects in the background and sharing them can cause unexpected behaviour. Values * outside 0-1 range of tweens will be a "best guess" from the appropriate part of the defined curve. * * @class MotionGuidePlugin * @constructor **/ var MotionGuidePlugin = function() { throw("MotionGuidePlugin cannot be instantiated.") }; // static interface: /** * @property priority * @protected * @static **/ MotionGuidePlugin.priority = 0; // high priority, should run sooner /** * @property temporary variable storage * @private * @static */ MotionGuidePlugin._rotOffS; /** * @property temporary variable storage * @private * @static */ MotionGuidePlugin._rotOffE; /** * @property temporary variable storage * @private * @static */ MotionGuidePlugin._rotNormS; /** * @property temporary variable storage * @private * @static */ MotionGuidePlugin._rotNormE; /** * Installs this plugin for use with TweenJS. Call this once after TweenJS is loaded to enable this plugin. * @method install * @static **/ MotionGuidePlugin.install = function() { createjs.Tween.installPlugin(MotionGuidePlugin, ["guide", "x", "y", "rotation"]); return createjs.Tween.IGNORE; }; /** * @method init * @protected * @static **/ MotionGuidePlugin.init = function(tween, prop, value) { var target = tween.target; if(!target.hasOwnProperty("x")){ target.x = 0; } if(!target.hasOwnProperty("y")){ target.y = 0; } if(!target.hasOwnProperty("rotation")){ target.rotation = 0; } if(prop=="rotation"){ tween.__needsRot = true; } return prop=="guide"?null:value; }; /** * @method step * @protected * @static **/ MotionGuidePlugin.step = function(tween, prop, startValue, endValue, injectProps) { // other props if(prop == "rotation"){ tween.__rotGlobalS = startValue; tween.__rotGlobalE = endValue; MotionGuidePlugin.testRotData(tween, injectProps); } if(prop != "guide"){ return endValue; } // guide only information - Start - var temp, data = endValue; if(!data.hasOwnProperty("path")){ data.path = []; } var path = data.path; if(!data.hasOwnProperty("end")){ data.end = 1; } if(!data.hasOwnProperty("start")){ data.start = (startValue&&startValue.hasOwnProperty("end")&&startValue.path===path)?startValue.end:0; } // Figure out subline information if(data.hasOwnProperty("_segments") && data._length){ return endValue; } var l = path.length; var accuracy = 10; // Adjust to improve line following precision but sacrifice performance (# of seg) if(l >= 6 && (l-2) % 4 == 0){ // Enough points && contains correct number per entry ignoring start data._segments = []; data._length = 0; for(var i=2; i<l; i+=4){ var sx = path[i-2], sy = path[i-1]; var cx = path[i+0], cy = path[i+1]; var ex = path[i+2], ey = path[i+3]; var oldX = sx, oldY = sy; var tempX, tempY, total = 0; var sublines = []; for(var j=1; j<=accuracy; j++){ var t = j/accuracy; var inv = 1 - t; tempX = inv*inv * sx + 2 * inv * t * cx + t*t * ex; tempY = inv*inv * sy + 2 * inv * t * cy + t*t * ey; total += sublines[sublines.push(Math.sqrt((temp=tempX-oldX)*temp + (temp=tempY-oldY)*temp))-1]; oldX = tempX; oldY = tempY; } data._segments.push(total); data._segments.push(sublines); data._length += total; } } else { throw("invalid 'path' data, please see documentation for valid paths"); } // Setup x/y tweens temp = data.orient; data.orient = true; var o = {}; MotionGuidePlugin.calc(data, data.start, o); tween.__rotPathS = Number(o.rotation.toFixed(5)); MotionGuidePlugin.calc(data, data.end, o); tween.__rotPathE = Number(o.rotation.toFixed(5)); data.orient = false; //here and now we don't know if we need to MotionGuidePlugin.calc(data, data.end, injectProps); data.orient = temp; // Setup rotation properties if(!data.orient){ return endValue; } tween.__guideData = data; MotionGuidePlugin.testRotData(tween, injectProps); return endValue; }; /** * @method testRotData * @protected * @static **/ MotionGuidePlugin.testRotData = function(tween, injectProps){ // no rotation informat? if we need it come back, if we don't use 0 & ensure we have guide data if(tween.__rotGlobalS === undefined || tween.__rotGlobalE === undefined){ if(tween.__needsRot){ return; } if(tween._curQueueProps.rotation !== undefined){ tween.__rotGlobalS = tween.__rotGlobalE = tween._curQueueProps.rotation; } else { tween.__rotGlobalS = tween.__rotGlobalE = injectProps.rotation = tween.target.rotation || 0; } } if(tween.__guideData === undefined){ return; } // Process rotation properties var data = tween.__guideData; var rotGlobalD = tween.__rotGlobalE - tween.__rotGlobalS; var rotPathD = tween.__rotPathE - tween.__rotPathS; var rot = rotGlobalD - rotPathD; if(data.orient == "auto"){ if(rot > 180){ rot -= 360; } else if(rot < -180){ rot += 360; } } else if(data.orient == "cw"){ while(rot < 0){ rot += 360; } if(rot == 0 && rotGlobalD > 0 && rotGlobalD != 180){ rot += 360; } } else if(data.orient == "ccw"){ rot = rotGlobalD - ((rotPathD > 180)?(360-rotPathD):(rotPathD)); // sign flipping on path while(rot > 0){ rot -= 360; } if(rot == 0 && rotGlobalD < 0 && rotGlobalD != -180){ rot -= 360; } } data.rotDelta = rot; data.rotOffS = tween.__rotGlobalS - tween.__rotPathS; // reset tween.__rotGlobalS = tween.__rotGlobalE = tween.__guideData = tween.__needsRot = undefined; }; /** * @method tween * @protected * @static **/ MotionGuidePlugin.tween = function(tween, prop, value, startValues, endValues, ratio, wait, end) { var data = endValues.guide; if(data == undefined || data === startValues.guide){ return value; } if(data.lastRatio != ratio){ // first time through so calculate what I need to var t = ((data.end-data.start)*(wait?data.end:ratio)+data.start); MotionGuidePlugin.calc(data, t, tween.target); switch(data.orient){ case "cw": // mix in the original rotation case "ccw": case "auto": tween.target.rotation += data.rotOffS + data.rotDelta*ratio; break; case "fixed": // follow fixed behaviour to solve potential issues default: tween.target.rotation += data.rotOffS; break; } data.lastRatio = ratio; } if(prop == "rotation" && ((!data.orient) || data.orient == "false")){ return value; } return tween.target[prop]; }; /** * Determine the appropriate x/y/rotation information about a path for a given ratio along the path. * Assumes a path object with all optional parameters specified. * @param data Data object you would pass to the "guide:" property in a Tween * @param ratio 0-1 Distance along path, values outside 0-1 are "best guess" * @param target Object to copy the results onto, will use a new object if not supplied. * @return {Object} The target object or a new object w/ the tweened properties * @static */ MotionGuidePlugin.calc = function(data, ratio, target) { if(data._segments == undefined){ MotionGuidePlugin.validate(data); } if(target == undefined){ target = {x:0, y:0, rotation:0}; } var seg = data._segments; var path = data.path; // find segment var pos = data._length * ratio; var cap = seg.length - 2; var n = 0; while(pos > seg[n] && n < cap){ pos -= seg[n]; n+=2; } // find subline var sublines = seg[n+1]; var i = 0; cap = sublines.length-1; while(pos > sublines[i] && i < cap){ pos -= sublines[i]; i++; } var t = (i/++cap)+(pos/(cap*sublines[i])); // find x/y n = (n*2)+2; var inv = 1 - t; target.x = inv*inv * path[n-2] + 2 * inv * t * path[n+0] + t*t * path[n+2]; target.y = inv*inv * path[n-1] + 2 * inv * t * path[n+1] + t*t * path[n+3]; // orientation if(data.orient){ target.rotation = 57.2957795 * Math.atan2( (path[n+1]-path[n-1])*inv + (path[n+3]-path[n+1])*t, (path[n+0]-path[n-2])*inv + (path[n+2]-path[n+0])*t); } return target; }; // public properties: // private properties: // constructor: // public methods: // private methods: createjs.MotionGuidePlugin = MotionGuidePlugin; }()); /** * @module TweenJS */ this.createjs = this.createjs || {}; (function() { "use strict"; /** * Static class holding library specific information such as the version and buildDate of * the library. * @class TweenJS **/ var s = createjs.TweenJS = createjs.TweenJS || {}; /** * The version string for this release. * @property version * @type String * @static **/ s.version = /*version*/"NEXT"; // injected by build process /** * The build date for this release in UTC format. * @property buildDate * @type String * @static **/ s.buildDate = /*date*/"Fri, 24 Oct 2014 16:09:53 GMT"; // injected by build process })();