mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 17:45:40 -05:00
11675 lines
No EOL
387 KiB
JavaScript
11675 lines
No EOL
387 KiB
JavaScript
|
|
|
|
//##############################################################################
|
|
// extend.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
/**
|
|
* @class Utility Methods
|
|
*/
|
|
|
|
/**
|
|
* Sets up the prototype chain and constructor property for a new class.
|
|
*
|
|
* This should be called right after creating the class constructor.
|
|
*
|
|
* function MySubClass() {}
|
|
* createjs.extend(MySubClass, MySuperClass);
|
|
* ClassB.prototype.doSomething = function() { }
|
|
*
|
|
* var foo = new MySubClass();
|
|
* console.log(foo instanceof MySuperClass); // true
|
|
* console.log(foo.prototype.constructor === MySubClass); // true
|
|
*
|
|
* @method extends
|
|
* @param {Function} subclass The subclass.
|
|
* @param {Function} superclass The superclass to extend.
|
|
* @return {Function} Returns the subclass's new prototype.
|
|
*/
|
|
createjs.extend = function(subclass, superclass) {
|
|
"use strict";
|
|
|
|
function o() { this.constructor = subclass; }
|
|
o.prototype = superclass.prototype;
|
|
return (subclass.prototype = new o());
|
|
};
|
|
|
|
//##############################################################################
|
|
// promote.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
/**
|
|
* @class Utility Methods
|
|
*/
|
|
|
|
/**
|
|
* Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
|
|
* It is recommended to use the super class's name as the prefix.
|
|
* An alias to the super class's constructor is always added in the format `prefix_constructor`.
|
|
* This allows the subclass to call super class methods without using `function.call`, providing better performance.
|
|
*
|
|
* For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
|
|
* would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
|
|
* prototype of `MySubClass` as `MySuperClass_draw`.
|
|
*
|
|
* This should be called after the class's prototype is fully defined.
|
|
*
|
|
* function ClassA(name) {
|
|
* this.name = name;
|
|
* }
|
|
* ClassA.prototype.greet = function() {
|
|
* return "Hello "+this.name;
|
|
* }
|
|
*
|
|
* function ClassB(name, punctuation) {
|
|
* this.ClassA_constructor(name);
|
|
* this.punctuation = punctuation;
|
|
* }
|
|
* createjs.extend(ClassB, ClassA);
|
|
* ClassB.prototype.greet = function() {
|
|
* return this.ClassA_greet()+this.punctuation;
|
|
* }
|
|
* createjs.promote(ClassB, "ClassA");
|
|
*
|
|
* var foo = new ClassB("World", "!?!");
|
|
* console.log(foo.greet()); // Hello World!?!
|
|
*
|
|
* @method promote
|
|
* @param {Function} subclass The class to promote super class methods on.
|
|
* @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
|
|
* @return {Function} Returns the subclass.
|
|
*/
|
|
createjs.promote = function(subclass, prefix) {
|
|
"use strict";
|
|
|
|
var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__;
|
|
if (supP) {
|
|
subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable
|
|
for (var n in supP) {
|
|
if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; }
|
|
}
|
|
}
|
|
return subclass;
|
|
};
|
|
|
|
//##############################################################################
|
|
// indexOf.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
/**
|
|
* @class Utility Methods
|
|
*/
|
|
|
|
/**
|
|
* Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
|
|
* that value. Returns -1 if value is not found.
|
|
*
|
|
* var i = createjs.indexOf(myArray, myElementToFind);
|
|
*
|
|
* @method indexOf
|
|
* @param {Array} array Array to search for searchElement
|
|
* @param searchElement Element to find in array.
|
|
* @return {Number} The first index of searchElement in array.
|
|
*/
|
|
createjs.indexOf = function (array, searchElement){
|
|
"use strict";
|
|
|
|
for (var i = 0,l=array.length; i < l; i++) {
|
|
if (searchElement === array[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
//##############################################################################
|
|
// Event.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
// constructor:
|
|
/**
|
|
* 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
|
|
**/
|
|
function Event(type, bubbles, cancelable) {
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The type of event.
|
|
* @property type
|
|
* @type String
|
|
**/
|
|
this.type = type;
|
|
|
|
/**
|
|
* The object that generated an event.
|
|
* @property target
|
|
* @type Object
|
|
* @default null
|
|
* @readonly
|
|
*/
|
|
this.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
|
|
*/
|
|
this.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
|
|
*/
|
|
this.eventPhase = 0;
|
|
|
|
/**
|
|
* Indicates whether the event will bubble through the display list.
|
|
* @property bubbles
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.bubbles = !!bubbles;
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
this.cancelable = !!cancelable;
|
|
|
|
/**
|
|
* The epoch time at which this event was created.
|
|
* @property timeStamp
|
|
* @type Number
|
|
* @default 0
|
|
* @readonly
|
|
*/
|
|
this.timeStamp = (new Date()).getTime();
|
|
|
|
/**
|
|
* Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
|
|
* on this event.
|
|
* @property defaultPrevented
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.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
|
|
*/
|
|
this.propagationStopped = false;
|
|
|
|
/**
|
|
* Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
|
|
* on this event.
|
|
* @property immediatePropagationStopped
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.immediatePropagationStopped = false;
|
|
|
|
/**
|
|
* Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
|
|
* @property removed
|
|
* @type Boolean
|
|
* @default false
|
|
* @readonly
|
|
*/
|
|
this.removed = false;
|
|
}
|
|
var p = Event.prototype;
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true.
|
|
* Mirrors the DOM event standard.
|
|
* @method preventDefault
|
|
**/
|
|
p.preventDefault = function() {
|
|
this.defaultPrevented = this.cancelable&&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);
|
|
};
|
|
|
|
/**
|
|
* Provides a chainable shortcut method for setting a number of properties on the instance.
|
|
*
|
|
* @method set
|
|
* @param {Object} props A generic object containing properties to copy to the instance.
|
|
* @return {Event} Returns the instance the method is called on (useful for chaining calls.)
|
|
*/
|
|
p.set = function(props) {
|
|
for (var n in props) { this[n] = props[n]; }
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* 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.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* 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
|
|
**/
|
|
function EventDispatcher() {
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* @protected
|
|
* @property _listeners
|
|
* @type Object
|
|
**/
|
|
this._listeners = null;
|
|
|
|
/**
|
|
* @protected
|
|
* @property _captureListeners
|
|
* @type Object
|
|
**/
|
|
this._captureListeners = null;
|
|
}
|
|
var p = EventDispatcher.prototype;
|
|
|
|
|
|
// static public methods:
|
|
/**
|
|
* 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;
|
|
};
|
|
|
|
|
|
// 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.js
|
|
//##############################################################################
|
|
|
|
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;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// UID.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Global utility for generating sequential unique ID numbers. The UID class uses a static interface (ex. <code>UID.get()</code>)
|
|
* and should not be instantiated.
|
|
* @class UID
|
|
* @static
|
|
**/
|
|
function UID() {
|
|
throw "UID cannot be instantiated";
|
|
}
|
|
|
|
|
|
// private static properties:
|
|
/**
|
|
* @property _nextID
|
|
* @type Number
|
|
* @protected
|
|
**/
|
|
UID._nextID = 0;
|
|
|
|
|
|
// public static methods:
|
|
/**
|
|
* Returns the next unique id.
|
|
* @method get
|
|
* @return {Number} The next unique id
|
|
* @static
|
|
**/
|
|
UID.get = function() {
|
|
return UID._nextID++;
|
|
};
|
|
|
|
|
|
createjs.UID = UID;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// MouseEvent.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Passed as the parameter to all mouse/pointer/touch related events. For a listing of mouse events and their properties,
|
|
* see the {{#crossLink "DisplayObject"}}{{/crossLink}} and {{#crossLink "Stage"}}{{/crossLink}} event listings.
|
|
* @class MouseEvent
|
|
* @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.
|
|
* @param {Number} stageX The normalized x position relative to the stage.
|
|
* @param {Number} stageY The normalized y position relative to the stage.
|
|
* @param {MouseEvent} nativeEvent The native DOM event related to this mouse event.
|
|
* @param {Number} pointerID The unique id for the pointer.
|
|
* @param {Boolean} primary Indicates whether this is the primary pointer in a multitouch environment.
|
|
* @param {Number} rawX The raw x position relative to the stage.
|
|
* @param {Number} rawY The raw y position relative to the stage.
|
|
* @extends Event
|
|
* @constructor
|
|
**/
|
|
function MouseEvent(type, bubbles, cancelable, stageX, stageY, nativeEvent, pointerID, primary, rawX, rawY) {
|
|
this.Event_constructor(type, bubbles, cancelable);
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The normalized x position on the stage. This will always be within the range 0 to stage width.
|
|
* @property stageX
|
|
* @type Number
|
|
*/
|
|
this.stageX = stageX;
|
|
|
|
/**
|
|
* The normalized y position on the stage. This will always be within the range 0 to stage height.
|
|
* @property stageY
|
|
* @type Number
|
|
**/
|
|
this.stageY = stageY;
|
|
|
|
/**
|
|
* The raw x position relative to the stage. Normally this will be the same as the stageX value, unless
|
|
* stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
|
|
* @property rawX
|
|
* @type Number
|
|
*/
|
|
this.rawX = (rawX==null)?stageX:rawX;
|
|
|
|
/**
|
|
* The raw y position relative to the stage. Normally this will be the same as the stageY value, unless
|
|
* stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
|
|
* @property rawY
|
|
* @type Number
|
|
*/
|
|
this.rawY = (rawY==null)?stageY:rawY;
|
|
|
|
/**
|
|
* The native MouseEvent generated by the browser. The properties and API for this
|
|
* event may differ between browsers. This property will be null if the
|
|
* EaselJS property was not directly generated from a native MouseEvent.
|
|
* @property nativeEvent
|
|
* @type HtmlMouseEvent
|
|
* @default null
|
|
**/
|
|
this.nativeEvent = nativeEvent;
|
|
|
|
/**
|
|
* The unique id for the pointer (touch point or cursor). This will be either -1 for the mouse, or the system
|
|
* supplied id value.
|
|
* @property pointerID
|
|
* @type {Number}
|
|
*/
|
|
this.pointerID = pointerID;
|
|
|
|
/**
|
|
* Indicates whether this is the primary pointer in a multitouch environment. This will always be true for the mouse.
|
|
* For touch pointers, the first pointer in the current stack will be considered the primary pointer.
|
|
* @property primary
|
|
* @type {Boolean}
|
|
*/
|
|
this.primary = !!primary;
|
|
}
|
|
var p = createjs.extend(MouseEvent, createjs.Event);
|
|
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Returns the x position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
|
|
* @property localX
|
|
* @type {Number}
|
|
* @readonly
|
|
*/
|
|
p._get_localX = function() {
|
|
return this.currentTarget.globalToLocal(this.rawX, this.rawY).x;
|
|
};
|
|
|
|
/**
|
|
* Returns the y position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
|
|
* @property localY
|
|
* @type {Number}
|
|
* @readonly
|
|
*/
|
|
p._get_localY = function() {
|
|
return this.currentTarget.globalToLocal(this.rawX, this.rawY).y;
|
|
};
|
|
|
|
try {
|
|
Object.defineProperties(p, {
|
|
localX: { get: p._get_localX },
|
|
localY: { get: p._get_localY }
|
|
});
|
|
} catch (e) {} // TODO: use Log
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns a clone of the MouseEvent instance.
|
|
* @method clone
|
|
* @return {MouseEvent} a clone of the MouseEvent instance.
|
|
**/
|
|
p.clone = function() {
|
|
return new MouseEvent(this.type, this.bubbles, this.cancelable, this.stageX, this.stageY, this.nativeEvent, this.pointerID, this.primary, this.rawX, this.rawY);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[MouseEvent (type="+this.type+" stageX="+this.stageX+" stageY="+this.stageY+")]";
|
|
};
|
|
|
|
|
|
createjs.MouseEvent = createjs.promote(MouseEvent, "Event");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Matrix2D.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Represents an affine transformation matrix, and provides tools for constructing and concatenating matrixes.
|
|
* @class Matrix2D
|
|
* @param {Number} [a=1] Specifies the a property for the new matrix.
|
|
* @param {Number} [b=0] Specifies the b property for the new matrix.
|
|
* @param {Number} [c=0] Specifies the c property for the new matrix.
|
|
* @param {Number} [d=1] Specifies the d property for the new matrix.
|
|
* @param {Number} [tx=0] Specifies the tx property for the new matrix.
|
|
* @param {Number} [ty=0] Specifies the ty property for the new matrix.
|
|
* @constructor
|
|
**/
|
|
function Matrix2D(a, b, c, d, tx, ty) {
|
|
this.setValues(a,b,c,d,tx,ty);
|
|
|
|
// public properties:
|
|
// assigned in the setValues method.
|
|
/**
|
|
* Position (0, 0) in a 3x3 affine transformation matrix.
|
|
* @property a
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (0, 1) in a 3x3 affine transformation matrix.
|
|
* @property b
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (1, 0) in a 3x3 affine transformation matrix.
|
|
* @property c
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (1, 1) in a 3x3 affine transformation matrix.
|
|
* @property d
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (2, 0) in a 3x3 affine transformation matrix.
|
|
* @property tx
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Position (2, 1) in a 3x3 affine transformation matrix.
|
|
* @property ty
|
|
* @type Number
|
|
**/
|
|
}
|
|
var p = Matrix2D.prototype;
|
|
|
|
|
|
// constants:
|
|
/**
|
|
* Multiplier for converting degrees to radians. Used internally by Matrix2D.
|
|
* @property DEG_TO_RAD
|
|
* @static
|
|
* @final
|
|
* @type Number
|
|
* @readonly
|
|
**/
|
|
Matrix2D.DEG_TO_RAD = Math.PI/180;
|
|
|
|
|
|
// static public properties:
|
|
/**
|
|
* An identity matrix, representing a null transformation.
|
|
* @property identity
|
|
* @static
|
|
* @type Matrix2D
|
|
* @readonly
|
|
**/
|
|
Matrix2D.identity = null; // set at bottom of class definition.
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Sets the specified values on this instance.
|
|
* @method setValues
|
|
* @param {Number} [a=1] Specifies the a property for the new matrix.
|
|
* @param {Number} [b=0] Specifies the b property for the new matrix.
|
|
* @param {Number} [c=0] Specifies the c property for the new matrix.
|
|
* @param {Number} [d=1] Specifies the d property for the new matrix.
|
|
* @param {Number} [tx=0] Specifies the tx property for the new matrix.
|
|
* @param {Number} [ty=0] Specifies the ty property for the new matrix.
|
|
* @return {Matrix2D} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.setValues = function(a, b, c, d, tx, ty) {
|
|
// don't forget to update docs in the constructor if these change:
|
|
this.a = (a == null) ? 1 : a;
|
|
this.b = b || 0;
|
|
this.c = c || 0;
|
|
this.d = (d == null) ? 1 : d;
|
|
this.tx = tx || 0;
|
|
this.ty = ty || 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Appends the specified matrix properties to this matrix. All parameters are required.
|
|
* This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
|
|
* @method append
|
|
* @param {Number} a
|
|
* @param {Number} b
|
|
* @param {Number} c
|
|
* @param {Number} d
|
|
* @param {Number} tx
|
|
* @param {Number} ty
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.append = function(a, b, c, d, tx, ty) {
|
|
var tx1 = this.tx;
|
|
if (a != 1 || b != 0 || c != 0 || d != 1) {
|
|
var a1 = this.a;
|
|
var c1 = this.c;
|
|
this.a = a1*a+this.b*c;
|
|
this.b = a1*b+this.b*d;
|
|
this.c = c1*a+this.d*c;
|
|
this.d = c1*b+this.d*d;
|
|
}
|
|
this.tx = tx1*a+this.ty*c+tx;
|
|
this.ty = tx1*b+this.ty*d+ty;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Prepends the specified matrix properties to this matrix.
|
|
* This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
|
|
* All parameters are required.
|
|
* @method prepend
|
|
* @param {Number} a
|
|
* @param {Number} b
|
|
* @param {Number} c
|
|
* @param {Number} d
|
|
* @param {Number} tx
|
|
* @param {Number} ty
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.prepend = function(a, b, c, d, tx, ty) {
|
|
var a1 = this.a;
|
|
var b1 = this.b;
|
|
var c1 = this.c;
|
|
var d1 = this.d;
|
|
|
|
this.a = a*a1+b*c1;
|
|
this.b = a*b1+b*d1;
|
|
this.c = c*a1+d*c1;
|
|
this.d = c*b1+d*d1;
|
|
this.tx = tx*a1+ty*c1+this.tx;
|
|
this.ty = tx*b1+ty*d1+this.ty;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Appends the specified matrix to this matrix.
|
|
* This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
|
|
* For example, you could calculate the combined transformation for a child object using:
|
|
* var o = myDisplayObject;
|
|
* var mtx = o.getMatrix();
|
|
* while (o = o.parent) {
|
|
* // append each parent's transformation in turn:
|
|
* o.appendMatrix(o.getMatrix());
|
|
* }
|
|
* @method appendMatrix
|
|
* @param {Matrix2D} matrix
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.appendMatrix = function(matrix) {
|
|
return this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
|
|
};
|
|
|
|
/**
|
|
* Prepends the specified matrix to this matrix.
|
|
* This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
|
|
* @method prependMatrix
|
|
* @param {Matrix2D} matrix
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.prependMatrix = function(matrix) {
|
|
return this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
|
|
};
|
|
|
|
/**
|
|
* Generates matrix properties from the specified display object transform properties, and appends them to this matrix.
|
|
* For example, you can use this to generate a matrix from a display object:
|
|
*
|
|
* var mtx = new Matrix2D();
|
|
* mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation);
|
|
* @method appendTransform
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} scaleX
|
|
* @param {Number} scaleY
|
|
* @param {Number} rotation
|
|
* @param {Number} skewX
|
|
* @param {Number} skewY
|
|
* @param {Number} regX Optional.
|
|
* @param {Number} regY Optional.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.appendTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
|
|
if (rotation%360) {
|
|
var r = rotation*Matrix2D.DEG_TO_RAD;
|
|
var cos = Math.cos(r);
|
|
var sin = Math.sin(r);
|
|
} else {
|
|
cos = 1;
|
|
sin = 0;
|
|
}
|
|
|
|
if (regX || regY) {
|
|
// append the registration offset:
|
|
this.tx -= regX; this.ty -= regY;
|
|
}
|
|
if (skewX || skewY) {
|
|
// TODO: can this be combined into a single prepend operation?
|
|
skewX *= Matrix2D.DEG_TO_RAD;
|
|
skewY *= Matrix2D.DEG_TO_RAD;
|
|
this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0);
|
|
this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
|
|
} else {
|
|
this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Generates matrix properties from the specified display object transform properties, and prepends them to this matrix.
|
|
* For example, you can use this to generate a matrix from a display object:
|
|
*
|
|
* var mtx = new Matrix2D();
|
|
* mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation);
|
|
* @method prependTransform
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} scaleX
|
|
* @param {Number} scaleY
|
|
* @param {Number} rotation
|
|
* @param {Number} skewX
|
|
* @param {Number} skewY
|
|
* @param {Number} regX Optional.
|
|
* @param {Number} regY Optional.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.prependTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
|
|
if (rotation%360) {
|
|
var r = rotation*Matrix2D.DEG_TO_RAD;
|
|
var cos = Math.cos(r);
|
|
var sin = Math.sin(r);
|
|
} else {
|
|
cos = 1;
|
|
sin = 0;
|
|
}
|
|
|
|
if (skewX || skewY) {
|
|
// TODO: can this be combined into a single append?
|
|
skewX *= Matrix2D.DEG_TO_RAD;
|
|
skewY *= Matrix2D.DEG_TO_RAD;
|
|
this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
|
|
this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0);
|
|
} else {
|
|
this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y);
|
|
}
|
|
|
|
if (regX || regY) {
|
|
// prepend the registration offset:
|
|
this.tx -= regX*this.a+regY*this.c;
|
|
this.ty -= regX*this.b+regY*this.d;
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Applies a rotation transformation to the matrix.
|
|
* @method rotate
|
|
* @param {Number} angle The angle in radians. To use degrees, multiply by `Math.PI/180`.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.rotate = function(angle) {
|
|
var cos = Math.cos(angle);
|
|
var sin = Math.sin(angle);
|
|
|
|
var a1 = this.a;
|
|
var c1 = this.c;
|
|
var tx1 = this.tx;
|
|
|
|
this.a = a1*cos-this.b*sin;
|
|
this.b = a1*sin+this.b*cos;
|
|
this.c = c1*cos-this.d*sin;
|
|
this.d = c1*sin+this.d*cos;
|
|
this.tx = tx1*cos-this.ty*sin;
|
|
this.ty = tx1*sin+this.ty*cos;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Applies a skew transformation to the matrix.
|
|
* @method skew
|
|
* @param {Number} skewX The amount to skew horizontally in degrees.
|
|
* @param {Number} skewY The amount to skew vertically in degrees.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
*/
|
|
p.skew = function(skewX, skewY) {
|
|
skewX = skewX*Matrix2D.DEG_TO_RAD;
|
|
skewY = skewY*Matrix2D.DEG_TO_RAD;
|
|
this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), 0, 0);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Applies a scale transformation to the matrix.
|
|
* @method scale
|
|
* @param {Number} x The amount to scale horizontally
|
|
* @param {Number} y The amount to scale vertically
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.scale = function(x, y) {
|
|
this.a *= x;
|
|
this.d *= y;
|
|
this.c *= x;
|
|
this.b *= y;
|
|
this.tx *= x;
|
|
this.ty *= y;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Translates the matrix on the x and y axes.
|
|
* @method translate
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.translate = function(x, y) {
|
|
this.tx += x;
|
|
this.ty += y;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Sets the properties of the matrix to those of an identity matrix (one that applies a null transformation).
|
|
* @method identity
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.identity = function() {
|
|
this.a = this.d = 1;
|
|
this.b = this.c = this.tx = this.ty = 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Inverts the matrix, causing it to perform the opposite transformation.
|
|
* @method invert
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.invert = function() {
|
|
var a1 = this.a;
|
|
var b1 = this.b;
|
|
var c1 = this.c;
|
|
var d1 = this.d;
|
|
var tx1 = this.tx;
|
|
var n = a1*d1-b1*c1;
|
|
|
|
this.a = d1/n;
|
|
this.b = -b1/n;
|
|
this.c = -c1/n;
|
|
this.d = a1/n;
|
|
this.tx = (c1*this.ty-d1*tx1)/n;
|
|
this.ty = -(a1*this.ty-b1*tx1)/n;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns true if the matrix is an identity matrix.
|
|
* @method isIdentity
|
|
* @return {Boolean}
|
|
**/
|
|
p.isIdentity = function() {
|
|
return this.tx === 0 && this.ty === 0 && this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1;
|
|
};
|
|
|
|
/**
|
|
* Returns true if this matrix is equal to the specified matrix (all property values are equal).
|
|
* @method equals
|
|
* @param {Matrix2D} matrix The matrix to compare.
|
|
* @return {Boolean}
|
|
**/
|
|
p.equals = function(matrix) {
|
|
return this.tx === matrix.tx && this.ty === matrix.ty && this.a === matrix.a && this.b === matrix.b && this.c === matrix.c && this.d === matrix.d;
|
|
};
|
|
|
|
/**
|
|
* Transforms a point according to this matrix.
|
|
* @method transformPoint
|
|
* @param {Number} x The x component of the point to transform.
|
|
* @param {Number} y The y component of the point to transform.
|
|
* @param {Point | Object} [pt] An object to copy the result into. If omitted a generic object with x/y properties will be returned.
|
|
* @return {Point} This matrix. Useful for chaining method calls.
|
|
**/
|
|
p.transformPoint = function(x, y, pt) {
|
|
pt = pt||{};
|
|
pt.x = x*this.a+y*this.c+this.tx;
|
|
pt.y = x*this.b+y*this.d+this.ty;
|
|
return pt;
|
|
};
|
|
|
|
/**
|
|
* Decomposes the matrix into transform properties (x, y, scaleX, scaleY, and rotation). Note that these values
|
|
* may not match the transform properties you used to generate the matrix, though they will produce the same visual
|
|
* results.
|
|
* @method decompose
|
|
* @param {Object} target The object to apply the transform properties to. If null, then a new object will be returned.
|
|
* @return {Object} The target, or a new generic object with the transform properties applied.
|
|
*/
|
|
p.decompose = function(target) {
|
|
// TODO: it would be nice to be able to solve for whether the matrix can be decomposed into only scale/rotation even when scale is negative
|
|
if (target == null) { target = {}; }
|
|
target.x = this.tx;
|
|
target.y = this.ty;
|
|
target.scaleX = Math.sqrt(this.a * this.a + this.b * this.b);
|
|
target.scaleY = Math.sqrt(this.c * this.c + this.d * this.d);
|
|
|
|
var skewX = Math.atan2(-this.c, this.d);
|
|
var skewY = Math.atan2(this.b, this.a);
|
|
|
|
var delta = Math.abs(1-skewX/skewY);
|
|
if (delta < 0.00001) { // effectively identical, can use rotation:
|
|
target.rotation = skewY/Matrix2D.DEG_TO_RAD;
|
|
if (this.a < 0 && this.d >= 0) {
|
|
target.rotation += (target.rotation <= 0) ? 180 : -180;
|
|
}
|
|
target.skewX = target.skewY = 0;
|
|
} else {
|
|
target.skewX = skewX/Matrix2D.DEG_TO_RAD;
|
|
target.skewY = skewY/Matrix2D.DEG_TO_RAD;
|
|
}
|
|
return target;
|
|
};
|
|
|
|
/**
|
|
* Copies all properties from the specified matrix to this matrix.
|
|
* @method copy
|
|
* @param {Matrix2D} matrix The matrix to copy properties from.
|
|
* @return {Matrix2D} This matrix. Useful for chaining method calls.
|
|
*/
|
|
p.copy = function(matrix) {
|
|
return this.setValues(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Matrix2D instance.
|
|
* @method clone
|
|
* @return {Matrix2D} a clone of the Matrix2D instance.
|
|
**/
|
|
p.clone = function() {
|
|
return new Matrix2D(this.a, this.b, this.c, this.d, this.tx, this.ty);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Matrix2D (a="+this.a+" b="+this.b+" c="+this.c+" d="+this.d+" tx="+this.tx+" ty="+this.ty+")]";
|
|
};
|
|
|
|
// this has to be populated after the class is defined:
|
|
Matrix2D.identity = new Matrix2D();
|
|
|
|
|
|
createjs.Matrix2D = Matrix2D;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// DisplayProps.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
/**
|
|
* Used for calculating and encapsulating display related properties.
|
|
* @class DisplayProps
|
|
* @param {Number} [visible=true] Visible value.
|
|
* @param {Number} [alpha=0] Alpha value.
|
|
* @param {Number} [shadow=null] A Shadow instance or null.
|
|
* @param {Number} [compositeOperation=null] A compositeOperation value or null.
|
|
* @param {Number} [matrix] A transformation matrix. Defaults to a new identity matrix.
|
|
* @constructor
|
|
**/
|
|
function DisplayProps(visible, alpha, shadow, compositeOperation, matrix) {
|
|
this.setValues(visible, alpha, shadow, compositeOperation, matrix);
|
|
|
|
// public properties:
|
|
// assigned in the setValues method.
|
|
/**
|
|
* Property representing the alpha that will be applied to a display object.
|
|
* @property alpha
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Property representing the shadow that will be applied to a display object.
|
|
* @property shadow
|
|
* @type Shadow
|
|
**/
|
|
|
|
/**
|
|
* Property representing the compositeOperation that will be applied to a display object.
|
|
* You can find a list of valid composite operations at:
|
|
* <a href="https://developer.mozilla.org/en/Canvas_tutorial/Compositing">https://developer.mozilla.org/en/Canvas_tutorial/Compositing</a>
|
|
* @property compositeOperation
|
|
* @type String
|
|
**/
|
|
|
|
/**
|
|
* Property representing the value for visible that will be applied to a display object.
|
|
* @property visible
|
|
* @type Boolean
|
|
**/
|
|
|
|
/**
|
|
* The transformation matrix that will be applied to a display object.
|
|
* @property matrix
|
|
* @type Matrix2D
|
|
**/
|
|
}
|
|
var p = DisplayProps.prototype;
|
|
|
|
// initialization:
|
|
/**
|
|
* Reinitializes the instance with the specified values.
|
|
* @method setValues
|
|
* @param {Number} [visible=true] Visible value.
|
|
* @param {Number} [alpha=1] Alpha value.
|
|
* @param {Number} [shadow=null] A Shadow instance or null.
|
|
* @param {Number} [compositeOperation=null] A compositeOperation value or null.
|
|
* @param {Number} [matrix] A transformation matrix. Defaults to an identity matrix.
|
|
* @return {DisplayProps} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.setValues = function (visible, alpha, shadow, compositeOperation, matrix) {
|
|
this.visible = visible == null ? true : !!visible;
|
|
this.alpha = alpha == null ? 1 : alpha;
|
|
this.shadow = shadow;
|
|
this.compositeOperation = shadow;
|
|
this.matrix = matrix || (this.matrix&&this.matrix.identity()) || new createjs.Matrix2D();
|
|
return this;
|
|
};
|
|
|
|
// public methods:
|
|
/**
|
|
* Prepends the specified display properties. This is generally used to apply a parent's properties to a child's.
|
|
* For example, to get the combined display properties that would be applied to a child, you could use:
|
|
* var o = myDisplayObject;
|
|
* var props = new createjs.DisplayProps();
|
|
* do {
|
|
* // append each parent's props in turn:
|
|
* props.append(o.visible, o.alpha, o.shadow, o.compositeOperation, o.getMatrix());
|
|
* o = o.parent;
|
|
* } while (o);
|
|
* @method prepend
|
|
* @param {Boolean} visible desired visible value
|
|
* @param {Number} alpha desired alpha value
|
|
* @param {Shadow} shadow desired shadow value
|
|
* @param {String} compositeOperation desired composite operation value
|
|
* @param {Matrix2D} [matrix] a Matrix2D instance
|
|
* @return {DisplayProps} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.prepend = function(visible, alpha, shadow, compositeOperation, matrix) {
|
|
this.alpha *= alpha;
|
|
this.shadow = shadow || this.shadow;
|
|
this.compositeOperation = compositeOperation || this.compositeOperation;
|
|
this.visible = this.visible && visible;
|
|
matrix&&this.matrix.prependMatrix(matrix);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Appends the specified display properties. This is generally used to apply a child's properties its parent's.
|
|
* @method append
|
|
* @param {Boolean} visible desired visible value
|
|
* @param {Number} alpha desired alpha value
|
|
* @param {Shadow} shadow desired shadow value
|
|
* @param {String} compositeOperation desired composite operation value
|
|
* @param {Matrix2D} [matrix] a Matrix2D instance
|
|
* @return {DisplayProps} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.append = function(visible, alpha, shadow, compositeOperation, matrix) {
|
|
this.alpha *= alpha;
|
|
this.shadow = this.shadow || shadow;
|
|
this.compositeOperation = this.compositeOperation || compositeOperation;
|
|
this.visible = this.visible && visible;
|
|
matrix&&this.matrix.appendMatrix(matrix);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Resets this instance and its matrix to default values.
|
|
* @method identity
|
|
* @return {DisplayProps} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.identity = function() {
|
|
this.visible = true;
|
|
this.alpha = 1;
|
|
this.shadow = this.compositeOperation = null;
|
|
this.matrix.identity();
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the DisplayProps instance. Clones the associated matrix.
|
|
* @method clone
|
|
* @return {DisplayProps} a clone of the DisplayProps instance.
|
|
**/
|
|
p.clone = function() {
|
|
return new DisplayProps(this.alpha, this.shadow, this.compositeOperation, this.visible, this.matrix.clone());
|
|
};
|
|
|
|
// private methods:
|
|
|
|
createjs.DisplayProps = DisplayProps;
|
|
})();
|
|
|
|
//##############################################################################
|
|
// Point.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Represents a point on a 2 dimensional x / y coordinate system.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var point = new createjs.Point(0, 100);
|
|
*
|
|
* @class Point
|
|
* @param {Number} [x=0] X position.
|
|
* @param {Number} [y=0] Y position.
|
|
* @constructor
|
|
**/
|
|
function Point(x, y) {
|
|
this.setValues(x, y);
|
|
|
|
|
|
// public properties:
|
|
// assigned in the setValues method.
|
|
/**
|
|
* X position.
|
|
* @property x
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Y position.
|
|
* @property y
|
|
* @type Number
|
|
**/
|
|
}
|
|
var p = Point.prototype;
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Sets the specified values on this instance.
|
|
* @method setValues
|
|
* @param {Number} [x=0] X position.
|
|
* @param {Number} [y=0] Y position.
|
|
* @return {Point} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.setValues = function(x, y) {
|
|
this.x = x||0;
|
|
this.y = y||0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Copies all properties from the specified point to this point.
|
|
* @method copy
|
|
* @param {Point} point The point to copy properties from.
|
|
* @return {Point} This point. Useful for chaining method calls.
|
|
*/
|
|
p.copy = function(point) {
|
|
this.x = point.x;
|
|
this.y = point.y;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Point instance.
|
|
* @method clone
|
|
* @return {Point} a clone of the Point instance.
|
|
**/
|
|
p.clone = function() {
|
|
return new Point(this.x, this.y);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Point (x="+this.x+" y="+this.y+")]";
|
|
};
|
|
|
|
|
|
createjs.Point = Point;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Rectangle.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Represents a rectangle as defined by the points (x, y) and (x+width, y+height).
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var rect = new createjs.Rectangle(0, 0, 100, 100);
|
|
*
|
|
* @class Rectangle
|
|
* @param {Number} [x=0] X position.
|
|
* @param {Number} [y=0] Y position.
|
|
* @param {Number} [width=0] The width of the Rectangle.
|
|
* @param {Number} [height=0] The height of the Rectangle.
|
|
* @constructor
|
|
**/
|
|
function Rectangle(x, y, width, height) {
|
|
this.setValues(x, y, width, height);
|
|
|
|
|
|
// public properties:
|
|
// assigned in the setValues method.
|
|
/**
|
|
* X position.
|
|
* @property x
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Y position.
|
|
* @property y
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Width.
|
|
* @property width
|
|
* @type Number
|
|
**/
|
|
|
|
/**
|
|
* Height.
|
|
* @property height
|
|
* @type Number
|
|
**/
|
|
}
|
|
var p = Rectangle.prototype;
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Sets the specified values on this instance.
|
|
* @method setValues
|
|
* @param {Number} [x=0] X position.
|
|
* @param {Number} [y=0] Y position.
|
|
* @param {Number} [width=0] The width of the Rectangle.
|
|
* @param {Number} [height=0] The height of the Rectangle.
|
|
* @return {Rectangle} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.setValues = function(x, y, width, height) {
|
|
// don't forget to update docs in the constructor if these change:
|
|
this.x = x||0;
|
|
this.y = y||0;
|
|
this.width = width||0;
|
|
this.height = height||0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Extends the rectangle's bounds to include the described point or rectangle.
|
|
* @method extend
|
|
* @param {Number} x X position of the point or rectangle.
|
|
* @param {Number} y Y position of the point or rectangle.
|
|
* @param {Number} [width=0] The width of the rectangle.
|
|
* @param {Number} [height=0] The height of the rectangle.
|
|
* @return {Rectangle} This instance. Useful for chaining method calls.
|
|
*/
|
|
p.extend = function(x, y, width, height) {
|
|
width = width||0;
|
|
height = height||0;
|
|
if (x+width > this.x+this.width) { this.width = x+width-this.x; }
|
|
if (y+height > this.y+this.height) { this.height = y+height-this.y; }
|
|
if (x < this.x) { this.width += this.x-x; this.x = x; }
|
|
if (y < this.y) { this.height += this.y-y; this.y = y; }
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns true if this rectangle fully encloses the described point or rectangle.
|
|
* @method contains
|
|
* @param {Number} x X position of the point or rectangle.
|
|
* @param {Number} y Y position of the point or rectangle.
|
|
* @param {Number} [width=0] The width of the rectangle.
|
|
* @param {Number} [height=0] The height of the rectangle.
|
|
* @return {Boolean} True if the described point or rectangle is contained within this rectangle.
|
|
*/
|
|
p.contains = function(x, y, width, height) {
|
|
width = width||0;
|
|
height = height||0;
|
|
return (x >= this.x && x+width <= this.x+this.width && y >= this.y && y+height <= this.y+this.height);
|
|
};
|
|
|
|
/**
|
|
* Returns a new rectangle which contains this rectangle and the specified rectangle.
|
|
* @method union
|
|
* @param {Rectangle} rect The rectangle to calculate a union with.
|
|
* @return {Rectangle} A new rectangle describing the union.
|
|
*/
|
|
p.union = function(rect) {
|
|
return this.clone().extend(rect.x, rect.y, rect.width, rect.height);
|
|
};
|
|
|
|
/**
|
|
* Returns a new rectangle which describes the intersection (overlap) of this rectangle and the specified rectangle,
|
|
* or null if they do not intersect.
|
|
* @method intersection
|
|
* @param {Rectangle} rect The rectangle to calculate an intersection with.
|
|
* @return {Rectangle} A new rectangle describing the intersection or null.
|
|
*/
|
|
p.intersection = function(rect) {
|
|
var x1 = rect.x, y1 = rect.y, x2 = x1+rect.width, y2 = y1+rect.height;
|
|
if (this.x > x1) { x1 = this.x; }
|
|
if (this.y > y1) { y1 = this.y; }
|
|
if (this.x + this.width < x2) { x2 = this.x + this.width; }
|
|
if (this.y + this.height < y2) { y2 = this.y + this.height; }
|
|
return (x2 <= x1 || y2 <= y1) ? null : new Rectangle(x1, y1, x2-x1, y2-y1);
|
|
};
|
|
|
|
/**
|
|
* Returns true if the specified rectangle intersects (has any overlap) with this rectangle.
|
|
* @method intersects
|
|
* @param {Rectangle} rect The rectangle to compare.
|
|
* @return {Boolean} True if the rectangles intersect.
|
|
*/
|
|
p.intersects = function(rect) {
|
|
return (rect.x <= this.x+this.width && this.x <= rect.x+rect.width && rect.y <= this.y+this.height && this.y <= rect.y + rect.height);
|
|
};
|
|
|
|
/**
|
|
* Returns true if the width or height are equal or less than 0.
|
|
* @method isEmpty
|
|
* @return {Boolean} True if the rectangle is empty.
|
|
*/
|
|
p.isEmpty = function() {
|
|
return this.width <= 0 || this.height <= 0;
|
|
};
|
|
|
|
/**
|
|
* Copies all properties from the specified rectangle to this rectangle.
|
|
* @method copy
|
|
* @param {Rectangle} rectangle The rectangle to copy properties from.
|
|
* @return {Rectangle} This rectangle. Useful for chaining method calls.
|
|
*/
|
|
p.copy = function(rectangle) {
|
|
return this.setValues(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Rectangle instance.
|
|
* @method clone
|
|
* @return {Rectangle} a clone of the Rectangle instance.
|
|
**/
|
|
p.clone = function() {
|
|
return new Rectangle(this.x, this.y, this.width, this.height);
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Rectangle (x="+this.x+" y="+this.y+" width="+this.width+" height="+this.height+")]";
|
|
};
|
|
|
|
|
|
createjs.Rectangle = Rectangle;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// ButtonHelper.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* The ButtonHelper is a helper class to create interactive buttons from {{#crossLink "MovieClip"}}{{/crossLink}} or
|
|
* {{#crossLink "Sprite"}}{{/crossLink}} instances. This class will intercept mouse events from an object, and
|
|
* automatically call {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}},
|
|
* to the respective animation labels, add a pointer cursor, and allows the user to define a hit state frame.
|
|
*
|
|
* The ButtonHelper instance does not need to be added to the stage, but a reference should be maintained to prevent
|
|
* garbage collection.
|
|
*
|
|
* Note that over states will not work unless you call {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var helper = new createjs.ButtonHelper(myInstance, "out", "over", "down", false, myInstance, "hit");
|
|
* myInstance.addEventListener("click", handleClick);
|
|
* function handleClick(event) {
|
|
* // Click Happened.
|
|
* }
|
|
*
|
|
* @class ButtonHelper
|
|
* @param {Sprite|MovieClip} target The instance to manage.
|
|
* @param {String} [outLabel="out"] The label or animation to go to when the user rolls out of the button.
|
|
* @param {String} [overLabel="over"] The label or animation to go to when the user rolls over the button.
|
|
* @param {String} [downLabel="down"] The label or animation to go to when the user presses the button.
|
|
* @param {Boolean} [play=false] If the helper should call "gotoAndPlay" or "gotoAndStop" on the button when changing
|
|
* states.
|
|
* @param {DisplayObject} [hitArea] An optional item to use as the hit state for the button. If this is not defined,
|
|
* then the button's visible states will be used instead. Note that the same instance as the "target" argument can be
|
|
* used for the hitState.
|
|
* @param {String} [hitLabel] The label or animation on the hitArea instance that defines the hitArea bounds. If this is
|
|
* null, then the default state of the hitArea will be used. *
|
|
* @constructor
|
|
*/
|
|
function ButtonHelper(target, outLabel, overLabel, downLabel, play, hitArea, hitLabel) {
|
|
if (!target.addEventListener) { return; }
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The target for this button helper.
|
|
* @property target
|
|
* @type MovieClip | Sprite
|
|
* @readonly
|
|
**/
|
|
this.target = target;
|
|
|
|
/**
|
|
* The label name or frame number to display when the user mouses out of the target. Defaults to "over".
|
|
* @property overLabel
|
|
* @type String | Number
|
|
**/
|
|
this.overLabel = overLabel == null ? "over" : overLabel;
|
|
|
|
/**
|
|
* The label name or frame number to display when the user mouses over the target. Defaults to "out".
|
|
* @property outLabel
|
|
* @type String | Number
|
|
**/
|
|
this.outLabel = outLabel == null ? "out" : outLabel;
|
|
|
|
/**
|
|
* The label name or frame number to display when the user presses on the target. Defaults to "down".
|
|
* @property downLabel
|
|
* @type String | Number
|
|
**/
|
|
this.downLabel = downLabel == null ? "down" : downLabel;
|
|
|
|
/**
|
|
* If true, then ButtonHelper will call gotoAndPlay, if false, it will use gotoAndStop. Default is false.
|
|
* @property play
|
|
* @default false
|
|
* @type Boolean
|
|
**/
|
|
this.play = play;
|
|
|
|
|
|
// private properties
|
|
/**
|
|
* @property _isPressed
|
|
* @type Boolean
|
|
* @protected
|
|
**/
|
|
this._isPressed = false;
|
|
|
|
/**
|
|
* @property _isOver
|
|
* @type Boolean
|
|
* @protected
|
|
**/
|
|
this._isOver = false;
|
|
|
|
/**
|
|
* @property _enabled
|
|
* @type Boolean
|
|
* @protected
|
|
**/
|
|
this._enabled = false;
|
|
|
|
|
|
// setup:
|
|
target.mouseChildren = false; // prevents issues when children are removed from the display list when state changes.
|
|
this.enabled = true;
|
|
this.handleEvent({});
|
|
if (hitArea) {
|
|
if (hitLabel) {
|
|
hitArea.actionsEnabled = false;
|
|
hitArea.gotoAndStop&&hitArea.gotoAndStop(hitLabel);
|
|
}
|
|
target.hitArea = hitArea;
|
|
}
|
|
}
|
|
var p = ButtonHelper.prototype;
|
|
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Enables or disables the button functionality on the target.
|
|
* @property enabled
|
|
* @type {Boolean}
|
|
**/
|
|
/**
|
|
* Enables or disables the button functionality on the target.
|
|
* @deprecated in favour of the enabled property.
|
|
* @method setEnabled
|
|
* @param {Boolean} value
|
|
**/
|
|
p.setEnabled = function(value) { // TODO: deprecated.
|
|
if (value == this._enabled) { return; }
|
|
var o = this.target;
|
|
this._enabled = value;
|
|
if (value) {
|
|
o.cursor = "pointer";
|
|
o.addEventListener("rollover", this);
|
|
o.addEventListener("rollout", this);
|
|
o.addEventListener("mousedown", this);
|
|
o.addEventListener("pressup", this);
|
|
} else {
|
|
o.cursor = null;
|
|
o.removeEventListener("rollover", this);
|
|
o.removeEventListener("rollout", this);
|
|
o.removeEventListener("mousedown", this);
|
|
o.removeEventListener("pressup", this);
|
|
}
|
|
};
|
|
/**
|
|
* Returns enabled state of this instance.
|
|
* @deprecated in favour of the enabled property.
|
|
* @method getEnabled
|
|
* @return {Boolean} The last value passed to setEnabled().
|
|
**/
|
|
p.getEnabled = function() {
|
|
return this._enabled;
|
|
};
|
|
|
|
try {
|
|
Object.defineProperties(p, {
|
|
enabled: { get: p.getEnabled, set: p.setEnabled }
|
|
});
|
|
} catch (e) {} // TODO: use Log
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[ButtonHelper]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method handleEvent
|
|
* @param {Object} evt The mouse event to handle.
|
|
* @protected
|
|
**/
|
|
p.handleEvent = function(evt) {
|
|
var label, t = this.target, type = evt.type;
|
|
if (type == "mousedown") {
|
|
this._isPressed = true;
|
|
label = this.downLabel;
|
|
} else if (type == "pressup") {
|
|
this._isPressed = false;
|
|
label = this._isOver ? this.overLabel : this.outLabel;
|
|
} else if (type == "rollover") {
|
|
this._isOver = true;
|
|
label = this._isPressed ? this.downLabel : this.overLabel;
|
|
} else { // rollout and default
|
|
this._isOver = false;
|
|
label = this._isPressed ? this.overLabel : this.outLabel;
|
|
}
|
|
if (this.play) {
|
|
t.gotoAndPlay&&t.gotoAndPlay(label);
|
|
} else {
|
|
t.gotoAndStop&&t.gotoAndStop(label);
|
|
}
|
|
};
|
|
|
|
|
|
createjs.ButtonHelper = ButtonHelper;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Shadow.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* This class encapsulates the properties required to define a shadow to apply to a {{#crossLink "DisplayObject"}}{{/crossLink}}
|
|
* via its <code>shadow</code> property.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* myImage.shadow = new createjs.Shadow("#000000", 5, 5, 10);
|
|
*
|
|
* @class Shadow
|
|
* @constructor
|
|
* @param {String} color The color of the shadow.
|
|
* @param {Number} offsetX The x offset of the shadow in pixels.
|
|
* @param {Number} offsetY The y offset of the shadow in pixels.
|
|
* @param {Number} blur The size of the blurring effect.
|
|
**/
|
|
function Shadow(color, offsetX, offsetY, blur) {
|
|
|
|
|
|
// public properties:
|
|
/** The color of the shadow.
|
|
* property color
|
|
* @type String
|
|
* @default null
|
|
*/
|
|
this.color = color||"black";
|
|
|
|
/** The x offset of the shadow.
|
|
* property offsetX
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
this.offsetX = offsetX||0;
|
|
|
|
/** The y offset of the shadow.
|
|
* property offsetY
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
this.offsetY = offsetY||0;
|
|
|
|
/** The blur of the shadow.
|
|
* property blur
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
this.blur = blur||0;
|
|
}
|
|
var p = Shadow.prototype;
|
|
|
|
|
|
// static public properties:
|
|
/**
|
|
* An identity shadow object (all properties are set to 0).
|
|
* @property identity
|
|
* @type Shadow
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
**/
|
|
Shadow.identity = new Shadow("transparent", 0, 0, 0);
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Shadow]";
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Shadow instance.
|
|
* @method clone
|
|
* @return {Shadow} A clone of the current Shadow instance.
|
|
**/
|
|
p.clone = function() {
|
|
return new Shadow(this.color, this.offsetX, this.offsetY, this.blur);
|
|
};
|
|
|
|
|
|
createjs.Shadow = Shadow;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// SpriteSheet.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Encapsulates the properties and methods associated with a sprite sheet. A sprite sheet is a series of images (usually
|
|
* animation frames) combined into a larger image (or images). For example, an animation consisting of eight 100x100
|
|
* images could be combined into a single 400x200 sprite sheet (4 frames across by 2 high).
|
|
*
|
|
* The data passed to the SpriteSheet constructor defines:<ol>
|
|
* <li> The source image or images to use.</li>
|
|
* <li> The positions of individual image frames.</li>
|
|
* <li> Sequences of frames that form named animations. Optional.</li>
|
|
* <li> The target playback framerate. Optional.</li>
|
|
* </OL>
|
|
*
|
|
* <h3>SpriteSheet Format</h3>
|
|
*
|
|
* SpriteSheets are an object with two required properties (`images` and `frames`), and two optional properties
|
|
* (`framerate` and `animations`). This makes them easy to define in javascript code, or in JSON.
|
|
*
|
|
* <h4>images</h4>
|
|
* An array of source images. Images can be either an HTMLImage
|
|
* instance, or a uri to an image. The former is recommended to control preloading.
|
|
*
|
|
* images: [image1, "path/to/image2.png"],
|
|
*
|
|
* <h4>frames</h4>
|
|
* Defines the individual frames. There are two supported formats for frame data:<OL>
|
|
* <LI> when all of the frames are the same size (in a grid), use an object with `width`, `height`, `regX`, `regY`, and `count` properties.
|
|
* `width` & `height` are required and specify the dimensions of the frames.
|
|
* `regX` & `regY` indicate the registration point or "origin" of the frames.
|
|
* `count` allows you to specify the total number of frames in the spritesheet; if omitted, this will be calculated
|
|
* based on the dimensions of the source images and the frames. Frames will be assigned indexes based on their position
|
|
* in the source images (left to right, top to bottom).
|
|
*
|
|
* frames: {width:64, height:64, count:20, regX: 32, regY:64}
|
|
*
|
|
* <LI> if the frames are of different sizes, use an array of frame definitions. Each definition is itself an array
|
|
* with 4 required and 3 optional entries, in the order: `x`, `y`, `width`, `height`, `imageIndex`, `regX`, `regY`. The first
|
|
* four entries are required and define the frame rectangle. The fifth specifies the index of the source image (defaults to 0). The
|
|
* last two specify the registration point of the frame.
|
|
*
|
|
* frames: [
|
|
* // x, y, width, height, imageIndex*, regX*, regY*
|
|
* [64, 0, 96, 64],
|
|
* [0, 0, 64, 64, 1, 32, 32]
|
|
* // etc.
|
|
* ]
|
|
*
|
|
* </OL>
|
|
*
|
|
* <h4>animations</h4>
|
|
* Optional. An object defining sequences of frames to play as named animations. Each property corresponds to an
|
|
* animation of the same name. Each animation must specify the frames to play, and may
|
|
* also include a relative playback `speed` (ex. 2 would playback at double speed, 0.5 at half), and
|
|
* the name of the `next` animation to sequence to after it completes.
|
|
*
|
|
* There are three formats supported for defining the frames in an animation, which can be mixed and matched as appropriate:<OL>
|
|
* <LI> for a single frame animation, you can simply specify the frame index
|
|
*
|
|
* animations: {
|
|
* sit: 7
|
|
* }
|
|
*
|
|
* <LI> for an animation of consecutive frames, you can use an array with two required, and two optional entries
|
|
* in the order: `start`, `end`, `next`, and `speed`. This will play the frames from start to end inclusive.
|
|
*
|
|
* animations: {
|
|
* // start, end, next*, speed*
|
|
* run: [0, 8],
|
|
* jump: [9, 12, "run", 2]
|
|
* }
|
|
*
|
|
* <LI> for non-consecutive frames, you can use an object with a `frames` property defining an array of frame indexes to
|
|
* play in order. The object can also specify `next` and `speed` properties.
|
|
*
|
|
* animations: {
|
|
* walk: {
|
|
* frames: [1,2,3,3,2,1]
|
|
* },
|
|
* shoot: {
|
|
* frames: [1,4,5,6],
|
|
* next: "walk",
|
|
* speed: 0.5
|
|
* }
|
|
* }
|
|
*
|
|
* </OL>
|
|
* <strong>Note:</strong> the `speed` property was added in EaselJS 0.7.0. Earlier versions had a `frequency`
|
|
* property instead, which was the inverse of `speed`. For example, a value of "4" would be 1/4 normal speed in earlier
|
|
* versions, but is 4x normal speed in 0.7.0+.
|
|
*
|
|
* <h4>framerate</h4>
|
|
* Optional. Indicates the default framerate to play this spritesheet at in frames per second.
|
|
* See {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} for more information.
|
|
*
|
|
* framerate: 20
|
|
*
|
|
* <h4>Example</h4>
|
|
* To define a simple sprite sheet, with a single image "sprites.jpg" arranged in a regular 50x50 grid with three
|
|
* animations: "stand" showing the first frame, "run" looping frame 1-5 inclusive, and "jump" playing frame 6-8 and sequencing back to run.
|
|
*
|
|
* var data = {
|
|
* images: ["sprites.jpg"],
|
|
* frames: {width:50, height:50},
|
|
* animations: {
|
|
* stand:0,
|
|
* run:[1,5],
|
|
* jump:[6,8,"run"]
|
|
* }
|
|
* };
|
|
* var spriteSheet = new createjs.SpriteSheet(data);
|
|
* var animation = new createjs.Sprite(spriteSheet, "run");
|
|
*
|
|
*
|
|
* <strong>Warning:</strong> Images loaded cross-origin will throw cross-origin security errors when interacted with
|
|
* using a mouse, using methods such as `getObjectUnderPoint`, using filters, or caching. You can get around this by
|
|
* setting `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`
|
|
*
|
|
* @class SpriteSheet
|
|
* @constructor
|
|
* @param {Object} data An object describing the SpriteSheet data.
|
|
* @extends EventDispatcher
|
|
**/
|
|
function SpriteSheet(data) {
|
|
this.EventDispatcher_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* Indicates whether all images are finished loading.
|
|
* @property complete
|
|
* @type Boolean
|
|
* @readonly
|
|
**/
|
|
this.complete = true;
|
|
|
|
/**
|
|
* Specifies the framerate to use by default for Sprite instances using the SpriteSheet. See
|
|
* Sprite.framerate for more information.
|
|
* @property framerate
|
|
* @type Number
|
|
**/
|
|
this.framerate = 0;
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _animations
|
|
* @protected
|
|
**/
|
|
this._animations = null;
|
|
|
|
/**
|
|
* @property _frames
|
|
* @protected
|
|
**/
|
|
this._frames = null;
|
|
|
|
/**
|
|
* @property _images
|
|
* @protected
|
|
**/
|
|
this._images = null;
|
|
|
|
/**
|
|
* @property _data
|
|
* @protected
|
|
**/
|
|
this._data = null;
|
|
|
|
/**
|
|
* @property _loadCount
|
|
* @protected
|
|
**/
|
|
this._loadCount = 0;
|
|
|
|
// only used for simple frame defs:
|
|
/**
|
|
* @property _frameHeight
|
|
* @protected
|
|
**/
|
|
this._frameHeight = 0;
|
|
|
|
/**
|
|
* @property _frameWidth
|
|
* @protected
|
|
**/
|
|
this._frameWidth = 0;
|
|
|
|
/**
|
|
* @property _numFrames
|
|
* @protected
|
|
**/
|
|
this._numFrames = 0;
|
|
|
|
/**
|
|
* @property _regX
|
|
* @protected
|
|
**/
|
|
this._regX = 0;
|
|
|
|
/**
|
|
* @property _regY
|
|
* @protected
|
|
**/
|
|
this._regY = 0;
|
|
|
|
|
|
// setup:
|
|
this._parseData(data);
|
|
}
|
|
var p = createjs.extend(SpriteSheet, createjs.EventDispatcher);
|
|
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when all images are loaded. Note that this only fires if the images
|
|
* were not fully loaded when the sprite sheet was initialized. You should check the complete property
|
|
* to prior to adding a listener. Ex.
|
|
*
|
|
* var sheet = new SpriteSheet(data);
|
|
* if (!sheet.complete) {
|
|
* // not preloaded, listen for the complete event:
|
|
* sheet.addEventListener("complete", handler);
|
|
* }
|
|
*
|
|
* @event complete
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns the total number of frames in the specified animation, or in the whole sprite
|
|
* sheet if the animation param is omitted. Returns 0 if the spritesheet relies on calculated frame counts, and
|
|
* the images have not been fully loaded.
|
|
* @method getNumFrames
|
|
* @param {String} animation The name of the animation to get a frame count for.
|
|
* @return {Number} The number of frames in the animation, or in the entire sprite sheet if the animation param is omitted.
|
|
*/
|
|
p.getNumFrames = function(animation) {
|
|
if (animation == null) {
|
|
return this._frames ? this._frames.length : this._numFrames || 0;
|
|
} else {
|
|
var data = this._data[animation];
|
|
if (data == null) { return 0; }
|
|
else { return data.frames.length; }
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns an array of all available animation names as strings.
|
|
* @method getAnimations
|
|
* @return {Array} an array of animation names available on this sprite sheet.
|
|
**/
|
|
p.getAnimations = function() {
|
|
return this._animations.slice(0);
|
|
};
|
|
|
|
/**
|
|
* Returns an object defining the specified animation. The returned object contains:<UL>
|
|
* <LI>frames: an array of the frame ids in the animation</LI>
|
|
* <LI>speed: the playback speed for this animation</LI>
|
|
* <LI>name: the name of the animation</LI>
|
|
* <LI>next: the default animation to play next. If the animation loops, the name and next property will be the
|
|
* same.</LI>
|
|
* </UL>
|
|
* @method getAnimation
|
|
* @param {String} name The name of the animation to get.
|
|
* @return {Object} a generic object with frames, speed, name, and next properties.
|
|
**/
|
|
p.getAnimation = function(name) {
|
|
return this._data[name];
|
|
};
|
|
|
|
/**
|
|
* Returns an object specifying the image and source rect of the specified frame. The returned object has:<UL>
|
|
* <LI>an image property holding a reference to the image object in which the frame is found</LI>
|
|
* <LI>a rect property containing a Rectangle instance which defines the boundaries for the frame within that
|
|
* image.</LI>
|
|
* <LI> A regX and regY property corresponding to the regX/Y values for the frame.
|
|
* </UL>
|
|
* @method getFrame
|
|
* @param {Number} frameIndex The index of the frame.
|
|
* @return {Object} a generic object with image and rect properties. Returns null if the frame does not exist.
|
|
**/
|
|
p.getFrame = function(frameIndex) {
|
|
var frame;
|
|
if (this._frames && (frame=this._frames[frameIndex])) { return frame; }
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the specified frame relative
|
|
* to the origin. For example, a 90 x 70 frame with a regX of 50 and a regY of 40 would return:
|
|
*
|
|
* [x=-50, y=-40, width=90, height=70]
|
|
*
|
|
* @method getFrameBounds
|
|
* @param {Number} frameIndex The index of the frame.
|
|
* @param {Rectangle} [rectangle] A Rectangle instance to copy the values into. By default a new instance is created.
|
|
* @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully loaded.
|
|
**/
|
|
p.getFrameBounds = function(frameIndex, rectangle) {
|
|
var frame = this.getFrame(frameIndex);
|
|
return frame ? (rectangle||new createjs.Rectangle()).setValues(-frame.regX, -frame.regY, frame.rect.width, frame.rect.height) : null;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[SpriteSheet]";
|
|
};
|
|
|
|
/**
|
|
* SpriteSheet cannot be cloned. A SpriteSheet can be shared by multiple Sprite instances without cloning it.
|
|
* @method clone
|
|
**/
|
|
p.clone = function() {
|
|
throw("SpriteSheet cannot be cloned.")
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _parseData
|
|
* @param {Object} data An object describing the SpriteSheet data.
|
|
* @protected
|
|
**/
|
|
p._parseData = function(data) {
|
|
var i,l,o,a;
|
|
if (data == null) { return; }
|
|
|
|
this.framerate = data.framerate||0;
|
|
|
|
// parse images:
|
|
if (data.images && (l=data.images.length) > 0) {
|
|
a = this._images = [];
|
|
for (i=0; i<l; i++) {
|
|
var img = data.images[i];
|
|
if (typeof img == "string") {
|
|
var src = img;
|
|
img = document.createElement("img");
|
|
img.src = src;
|
|
}
|
|
a.push(img);
|
|
if (!img.getContext && !img.complete) {
|
|
this._loadCount++;
|
|
this.complete = false;
|
|
(function(o) { img.onload = function() { o._handleImageLoad(); } })(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// parse frames:
|
|
if (data.frames == null) { // nothing
|
|
} else if (data.frames instanceof Array) {
|
|
this._frames = [];
|
|
a = data.frames;
|
|
for (i=0,l=a.length;i<l;i++) {
|
|
var arr = a[i];
|
|
this._frames.push({image:this._images[arr[4]?arr[4]:0], rect:new createjs.Rectangle(arr[0],arr[1],arr[2],arr[3]), regX:arr[5]||0, regY:arr[6]||0 });
|
|
}
|
|
} else {
|
|
o = data.frames;
|
|
this._frameWidth = o.width;
|
|
this._frameHeight = o.height;
|
|
this._regX = o.regX||0;
|
|
this._regY = o.regY||0;
|
|
this._numFrames = o.count;
|
|
if (this._loadCount == 0) { this._calculateFrames(); }
|
|
}
|
|
|
|
// parse animations:
|
|
this._animations = [];
|
|
if ((o=data.animations) != null) {
|
|
this._data = {};
|
|
var name;
|
|
for (name in o) {
|
|
var anim = {name:name};
|
|
var obj = o[name];
|
|
if (typeof obj == "number") { // single frame
|
|
a = anim.frames = [obj];
|
|
} else if (obj instanceof Array) { // simple
|
|
if (obj.length == 1) { anim.frames = [obj[0]]; }
|
|
else {
|
|
anim.speed = obj[3];
|
|
anim.next = obj[2];
|
|
a = anim.frames = [];
|
|
for (i=obj[0];i<=obj[1];i++) {
|
|
a.push(i);
|
|
}
|
|
}
|
|
} else { // complex
|
|
anim.speed = obj.speed;
|
|
anim.next = obj.next;
|
|
var frames = obj.frames;
|
|
a = anim.frames = (typeof frames == "number") ? [frames] : frames.slice(0);
|
|
}
|
|
if (anim.next === true || anim.next === undefined) { anim.next = name; } // loop
|
|
if (anim.next === false || (a.length < 2 && anim.next == name)) { anim.next = null; } // stop
|
|
if (!anim.speed) { anim.speed = 1; }
|
|
this._animations.push(name);
|
|
this._data[name] = anim;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _handleImageLoad
|
|
* @protected
|
|
**/
|
|
p._handleImageLoad = function() {
|
|
if (--this._loadCount == 0) {
|
|
this._calculateFrames();
|
|
this.complete = true;
|
|
this.dispatchEvent("complete");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _calculateFrames
|
|
* @protected
|
|
**/
|
|
p._calculateFrames = function() {
|
|
if (this._frames || this._frameWidth == 0) { return; }
|
|
this._frames = [];
|
|
var ttlFrames = 0;
|
|
var fw = this._frameWidth;
|
|
var fh = this._frameHeight;
|
|
for (var i=0,imgs = this._images; i<imgs.length; i++) {
|
|
var img = imgs[i];
|
|
var cols = img.width/fw|0;
|
|
var rows = img.height/fh|0;
|
|
var ttl = this._numFrames>0 ? Math.min(this._numFrames-ttlFrames,cols*rows) : cols*rows;
|
|
for (var j=0;j<ttl;j++) {
|
|
this._frames.push({image:img, rect:new createjs.Rectangle(j%cols*fw,(j/cols|0)*fh,fw,fh), regX:this._regX, regY:this._regY });
|
|
}
|
|
ttlFrames += ttl;
|
|
}
|
|
this._numFrames = ttlFrames;
|
|
};
|
|
|
|
|
|
createjs.SpriteSheet = createjs.promote(SpriteSheet, "EventDispatcher");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Graphics.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a
|
|
* specified context. Note that you can use Graphics without any dependency on the Easel framework by calling {{#crossLink "Graphics/draw"}}{{/crossLink}}
|
|
* directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the
|
|
* context of an EaselJS display list.
|
|
*
|
|
* There are two approaches to working with Graphics object: calling methods on a Graphics instance (the "Graphics API"), or
|
|
* instantiating Graphics command objects and adding them to the graphics queue via {{#crossLink "Graphics/append"}}{{/crossLink}}.
|
|
* The former abstracts the latter, simplifying beginning and ending paths, fills, and strokes.
|
|
*
|
|
* var g = new createjs.Graphics();
|
|
* g.setStrokeStyle(1);
|
|
* g.beginStroke("#000000");
|
|
* g.beginFill("red");
|
|
* g.drawCircle(0,0,30);
|
|
*
|
|
* All drawing methods in Graphics return the Graphics instance, so they can be chained together. For example,
|
|
* the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill:
|
|
*
|
|
* myGraphics.beginStroke("red").beginFill("blue").drawRect(20, 20, 100, 50);
|
|
*
|
|
* Each graphics API call generates a command object (see below). The last command to be created can be accessed via
|
|
* {{#crossLink "Graphics/command:property"}}{{/crossLink}}:
|
|
*
|
|
* var fillCommand = myGraphics.beginFill("red").command;
|
|
* // ... later, update the fill style/color:
|
|
* fillCommand.style = "blue";
|
|
* // or change it to a bitmap fill:
|
|
* fillCommand.bitmap(myImage);
|
|
*
|
|
* For more direct control of rendering, you can instantiate and append command objects to the graphics queue directly. In this case, you
|
|
* need to manage path creation manually, and ensure that fill/stroke is applied to a defined path:
|
|
*
|
|
* // start a new path. Graphics.beginPath is a reusable BeginPath instance:
|
|
* myGraphics.append(Graphics.beginPath);
|
|
* // we need to define the path before applying the fill:
|
|
* var circle = new Graphics.Circle(0,0,30);
|
|
* myGraphics.append(circle);
|
|
* // fill the path we just defined:
|
|
* var fill = new Graphics.Fill("red");
|
|
* myGraphics.append(fill);
|
|
*
|
|
* These approaches can be used together, for example to insert a custom command:
|
|
*
|
|
* myGraphics.beginFill("red");
|
|
* var customCommand = new CustomSpiralCommand(etc);
|
|
* myGraphics.append(customCommand);
|
|
* myGraphics.beginFill("blue");
|
|
* myGraphics.drawCircle(0, 0, 30);
|
|
*
|
|
* See {{#crossLink "Graphics/append"}}{{/crossLink}} for more info on creating custom commands.
|
|
*
|
|
* <h4>Tiny API</h4>
|
|
* The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the
|
|
* Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS
|
|
* to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected
|
|
* descriptions in the docs.
|
|
*
|
|
* <table>
|
|
* <tr><td><b>Tiny</b></td><td><b>Method</b></td><td><b>Tiny</b></td><td><b>Method</b></td></tr>
|
|
* <tr><td>mt</td><td>{{#crossLink "Graphics/moveTo"}}{{/crossLink}} </td>
|
|
* <td>lt</td> <td>{{#crossLink "Graphics/lineTo"}}{{/crossLink}}</td></tr>
|
|
* <tr><td>a/at</td><td>{{#crossLink "Graphics/arc"}}{{/crossLink}} / {{#crossLink "Graphics/arcTo"}}{{/crossLink}} </td>
|
|
* <td>bt</td><td>{{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>qt</td><td>{{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo)</td>
|
|
* <td>r</td><td>{{#crossLink "Graphics/rect"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>cp</td><td>{{#crossLink "Graphics/closePath"}}{{/crossLink}} </td>
|
|
* <td>c</td><td>{{#crossLink "Graphics/clear"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>f</td><td>{{#crossLink "Graphics/beginFill"}}{{/crossLink}} </td>
|
|
* <td>lf</td><td>{{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>rf</td><td>{{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} </td>
|
|
* <td>bf</td><td>{{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>ef</td><td>{{#crossLink "Graphics/endFill"}}{{/crossLink}} </td>
|
|
* <td>ss</td><td>{{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>s</td><td>{{#crossLink "Graphics/beginStroke"}}{{/crossLink}} </td>
|
|
* <td>ls</td><td>{{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>rs</td><td>{{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} </td>
|
|
* <td>bs</td><td>{{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>es</td><td>{{#crossLink "Graphics/endStroke"}}{{/crossLink}} </td>
|
|
* <td>dr</td><td>{{#crossLink "Graphics/drawRect"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>rr</td><td>{{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} </td>
|
|
* <td>rc</td><td>{{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>dc</td><td>{{#crossLink "Graphics/drawCircle"}}{{/crossLink}} </td>
|
|
* <td>de</td><td>{{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} </td></tr>
|
|
* <tr><td>dp</td><td>{{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} </td>
|
|
* <td>p</td><td>{{#crossLink "Graphics/decodePath"}}{{/crossLink}} </td></tr>
|
|
* </table>
|
|
*
|
|
* Here is the above example, using the tiny API instead.
|
|
*
|
|
* myGraphics.s("red").f("blue").r(20, 20, 100, 50);
|
|
*
|
|
* @class Graphics
|
|
* @constructor
|
|
**/
|
|
function Graphics() {
|
|
|
|
|
|
// public properties
|
|
/**
|
|
* Holds a reference to the last command that was created or appended. For example, you could retain a reference
|
|
* to a Fill command in order to dynamically update the color later by using:
|
|
* myFill = myGraphics.beginFill("red").command;
|
|
* // update color later:
|
|
* myFill.style = "yellow";
|
|
* @property command
|
|
* @type Object
|
|
**/
|
|
this.command = null;
|
|
|
|
|
|
// private properties
|
|
/**
|
|
* @property _stroke
|
|
* @protected
|
|
* @type {Array}
|
|
**/
|
|
this._stroke = null;
|
|
|
|
/**
|
|
* @property _strokeStyle
|
|
* @protected
|
|
* @type {Array}
|
|
**/
|
|
this._strokeStyle = null;
|
|
|
|
/**
|
|
* @property _strokeIgnoreScale
|
|
* @protected
|
|
* @type Boolean
|
|
**/
|
|
this._strokeIgnoreScale = false;
|
|
|
|
/**
|
|
* @property _fill
|
|
* @protected
|
|
* @type {Array}
|
|
**/
|
|
this._fill = null;
|
|
|
|
/**
|
|
* @property _instructions
|
|
* @protected
|
|
* @type {Array}
|
|
**/
|
|
this._instructions = [];
|
|
|
|
/**
|
|
* Indicates the last instruction index that was committed.
|
|
* @property _commitIndex
|
|
* @protected
|
|
* @type {Number}
|
|
**/
|
|
this._commitIndex = 0;
|
|
|
|
/**
|
|
* Uncommitted instructions.
|
|
* @property _activeInstructions
|
|
* @protected
|
|
* @type {Array}
|
|
**/
|
|
this._activeInstructions = [];
|
|
|
|
/**
|
|
* This indicates that there have been changes to the activeInstruction list since the last updateInstructions call.
|
|
* @property _dirty
|
|
* @protected
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
this._dirty = false;
|
|
|
|
/**
|
|
* Index to draw from if a store operation has happened.
|
|
* @property _storeIndex
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this._storeIndex = 0;
|
|
|
|
// setup:
|
|
this.clear();
|
|
}
|
|
var p = Graphics.prototype;
|
|
var G = Graphics; // shortcut
|
|
|
|
|
|
// static public methods:
|
|
/**
|
|
* Returns a CSS compatible color string based on the specified RGB numeric color values in the format
|
|
* "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example,
|
|
*
|
|
* createjs.Graphics.getRGB(50, 100, 150, 0.5);
|
|
* // Returns "rgba(50,100,150,0.5)"
|
|
*
|
|
* It also supports passing a single hex color value as the first param, and an optional alpha value as the second
|
|
* param. For example,
|
|
*
|
|
* createjs.Graphics.getRGB(0xFF00FF, 0.2);
|
|
* // Returns "rgba(255,0,255,0.2)"
|
|
*
|
|
* @method getRGB
|
|
* @static
|
|
* @param {Number} r The red component for the color, between 0 and 0xFF (255).
|
|
* @param {Number} g The green component for the color, between 0 and 0xFF (255).
|
|
* @param {Number} b The blue component for the color, between 0 and 0xFF (255).
|
|
* @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
|
|
* @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format
|
|
* "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)".
|
|
**/
|
|
Graphics.getRGB = function(r, g, b, alpha) {
|
|
if (r != null && b == null) {
|
|
alpha = g;
|
|
b = r&0xFF;
|
|
g = r>>8&0xFF;
|
|
r = r>>16&0xFF;
|
|
}
|
|
if (alpha == null) {
|
|
return "rgb("+r+","+g+","+b+")";
|
|
} else {
|
|
return "rgba("+r+","+g+","+b+","+alpha+")";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)",
|
|
* or if alpha is null then in the format "hsl(360,100,100)".
|
|
*
|
|
* createjs.Graphics.getHSL(150, 100, 70);
|
|
* // Returns "hsl(150,100,70)"
|
|
*
|
|
* @method getHSL
|
|
* @static
|
|
* @param {Number} hue The hue component for the color, between 0 and 360.
|
|
* @param {Number} saturation The saturation component for the color, between 0 and 100.
|
|
* @param {Number} lightness The lightness component for the color, between 0 and 100.
|
|
* @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
|
|
* @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format
|
|
* "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)".
|
|
**/
|
|
Graphics.getHSL = function(hue, saturation, lightness, alpha) {
|
|
if (alpha == null) {
|
|
return "hsl("+(hue%360)+","+saturation+"%,"+lightness+"%)";
|
|
} else {
|
|
return "hsla("+(hue%360)+","+saturation+"%,"+lightness+"%,"+alpha+")";
|
|
}
|
|
};
|
|
|
|
|
|
// static properties:
|
|
/**
|
|
* A reusable instance of {{#crossLink "Graphics/BeginPath"}}{{/crossLink}} to avoid
|
|
* unnecessary instantiation.
|
|
* @property beginCmd
|
|
* @type {Graphics.BeginPath}
|
|
* @static
|
|
**/
|
|
// defined at the bottom of this file.
|
|
|
|
/**
|
|
* Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}.
|
|
* @property BASE_64
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
* @type {Object}
|
|
**/
|
|
Graphics.BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63};
|
|
|
|
|
|
/**
|
|
* Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
|
|
* corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
|
|
* "butt", 1 to "round", and 2 to "square".
|
|
* For example, to set the line caps to "square":
|
|
*
|
|
* myGraphics.ss(16, 2);
|
|
*
|
|
* @property STROKE_CAPS_MAP
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
* @type {Array}
|
|
**/
|
|
Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"];
|
|
|
|
/**
|
|
* Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
|
|
* corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
|
|
* "miter", 1 to "round", and 2 to "bevel".
|
|
* For example, to set the line joints to "bevel":
|
|
*
|
|
* myGraphics.ss(16, 0, 2);
|
|
*
|
|
* @property STROKE_JOINTS_MAP
|
|
* @static
|
|
* @final
|
|
* @readonly
|
|
* @type {Array}
|
|
**/
|
|
Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"];
|
|
|
|
/**
|
|
* @property _ctx
|
|
* @static
|
|
* @protected
|
|
* @type {CanvasRenderingContext2D}
|
|
**/
|
|
var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
|
|
if (canvas.getContext) {
|
|
Graphics._ctx = canvas.getContext("2d");
|
|
canvas.width = canvas.height = 1;
|
|
}
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true if this Graphics instance has no drawing commands.
|
|
* @method isEmpty
|
|
* @return {Boolean} Returns true if this Graphics instance has no drawing commands.
|
|
**/
|
|
p.isEmpty = function() {
|
|
return !(this._instructions.length || this._activeInstructions.length);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Object} data Optional data that is passed to graphics command exec methods. When called from a Shape instance, the shape passes itself as the data parameter. This can be used by custom graphic commands to insert contextual data.
|
|
**/
|
|
p.draw = function(ctx, data) {
|
|
this._updateInstructions();
|
|
var instr = this._instructions;
|
|
for (var i=this._storeIndex, l=instr.length; i<l; i++) {
|
|
instr[i].exec(ctx, data);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Draws only the path described for this Graphics instance, skipping any non-path instructions, including fill and
|
|
* stroke descriptions. Used for <code>DisplayObject.mask</code> to draw the clipping path, for example.
|
|
* @method drawAsPath
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
**/
|
|
p.drawAsPath = function(ctx) {
|
|
this._updateInstructions();
|
|
var instr, instrs = this._instructions;
|
|
for (var i=0, l=instrs.length; i<l; i++) {
|
|
// the first command is always a beginPath command.
|
|
if ((instr = instrs[i]).path !== false) { instr.exec(ctx); }
|
|
}
|
|
};
|
|
|
|
|
|
// public methods that map directly to context 2D calls:
|
|
/**
|
|
* Moves the drawing point to the specified position. A tiny API method "mt" also exists.
|
|
* @method moveTo
|
|
* @param {Number} x The x coordinate the drawing point should move to.
|
|
* @param {Number} y The y coordinate the drawing point should move to.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls).
|
|
**/
|
|
p.moveTo = function(x, y) {
|
|
return this.append(new G.MoveTo(x,y), true);
|
|
};
|
|
|
|
/**
|
|
* Draws a line from the current drawing point to the specified position, which become the new current drawing
|
|
* point. A tiny API method "lt" also exists.
|
|
*
|
|
* For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#complex-shapes-(paths)">
|
|
* whatwg spec</a>.
|
|
* @method lineTo
|
|
* @param {Number} x The x coordinate the drawing point should draw to.
|
|
* @param {Number} y The y coordinate the drawing point should draw to.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.lineTo = function(x, y) {
|
|
return this.append(new G.LineTo(x,y));
|
|
};
|
|
|
|
/**
|
|
* Draws an arc with the specified control points and radius. For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arcto">
|
|
* whatwg spec</a>. A tiny API method "at" also exists.
|
|
* @method arcTo
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} x2
|
|
* @param {Number} y2
|
|
* @param {Number} radius
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.arcTo = function(x1, y1, x2, y2, radius) {
|
|
return this.append(new G.ArcTo(x1, y1, x2, y2, radius));
|
|
};
|
|
|
|
/**
|
|
* Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For
|
|
* example, to draw a full circle with a radius of 20 centered at (100, 100):
|
|
*
|
|
* arc(100, 100, 20, 0, Math.PI*2);
|
|
*
|
|
* For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc">whatwg spec</a>.
|
|
* A tiny API method "a" also exists.
|
|
* @method arc
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
* @param {Number} startAngle Measured in radians.
|
|
* @param {Number} endAngle Measured in radians.
|
|
* @param {Boolean} anticlockwise
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.arc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
|
|
return this.append(new G.Arc(x, y, radius, startAngle, endAngle, anticlockwise));
|
|
};
|
|
|
|
/**
|
|
* Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed
|
|
* information, read the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-quadraticcurveto">
|
|
* whatwg spec</a>. A tiny API method "qt" also exists.
|
|
* @method quadraticCurveTo
|
|
* @param {Number} cpx
|
|
* @param {Number} cpy
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.quadraticCurveTo = function(cpx, cpy, x, y) {
|
|
return this.append(new G.QuadraticCurveTo(cpx, cpy, x, y));
|
|
};
|
|
|
|
/**
|
|
* Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x,
|
|
* cp2y). For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-beziercurveto">
|
|
* whatwg spec</a>. A tiny API method "bt" also exists.
|
|
* @method bezierCurveTo
|
|
* @param {Number} cp1x
|
|
* @param {Number} cp1y
|
|
* @param {Number} cp2x
|
|
* @param {Number} cp2y
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
return this.append(new G.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y));
|
|
};
|
|
|
|
/**
|
|
* Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke.
|
|
* For detailed information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-rect">
|
|
* whatwg spec</a>. A tiny API method "r" also exists.
|
|
* @method rect
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w Width of the rectangle
|
|
* @param {Number} h Height of the rectangle
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.rect = function(x, y, w, h) {
|
|
return this.append(new G.Rect(x, y, w, h));
|
|
};
|
|
|
|
/**
|
|
* Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified
|
|
* since the fill or stroke was last set. A tiny API method "cp" also exists.
|
|
* @method closePath
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.closePath = function() {
|
|
return this._activeInstructions.length ? this.append(new G.ClosePath()) : this;
|
|
};
|
|
|
|
|
|
// public methods that roughly map to Flash graphics APIs:
|
|
/**
|
|
* Clears all drawing instructions, effectively resetting this Graphics instance. Any line and fill styles will need
|
|
* to be redefined to draw shapes following a clear call. A tiny API method "c" also exists.
|
|
* @method clear
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.clear = function() {
|
|
this._instructions.length = this._activeInstructions.length = this._commitIndex = 0;
|
|
this._strokeStyle = this._stroke = this._fill = null;
|
|
this._dirty = this._strokeIgnoreScale = false;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Begins a fill with the specified color. This ends the current sub-path. A tiny API method "f" also exists.
|
|
* @method beginFill
|
|
* @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
|
|
* null will result in no fill.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.beginFill = function(color) {
|
|
return this._setFill(color ? new G.Fill(color) : null);
|
|
};
|
|
|
|
/**
|
|
* Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
|
|
* example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
|
|
* square to display it:
|
|
*
|
|
* myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
|
|
*
|
|
* A tiny API method "lf" also exists.
|
|
* @method beginLinearGradientFill
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
|
|
* drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
|
|
* the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.beginLinearGradientFill = function(colors, ratios, x0, y0, x1, y1) {
|
|
return this._setFill(new G.Fill().linearGradient(colors, ratios, x0, y0, x1, y1));
|
|
};
|
|
|
|
/**
|
|
* Begins a radial gradient fill. This ends the current sub-path. For example, the following code defines a red to
|
|
* blue radial gradient centered at (100, 100), with a radius of 50, and draws a circle to display it:
|
|
*
|
|
* myGraphics.beginRadialGradientFill(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50).drawCircle(100, 100, 50);
|
|
*
|
|
* A tiny API method "rf" also exists.
|
|
* @method beginRadialGradientFill
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} y0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} r0 Radius of the inner circle that defines the gradient.
|
|
* @param {Number} x1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} y1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} r1 Radius of the outer circle that defines the gradient.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.beginRadialGradientFill = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
|
|
return this._setFill(new G.Fill().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
|
|
};
|
|
|
|
/**
|
|
* Begins a pattern fill using the specified image. This ends the current sub-path. A tiny API method "bf" also
|
|
* exists.
|
|
* @method beginBitmapFill
|
|
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
|
|
* as the pattern.
|
|
* @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
|
|
* "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
|
|
* "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
|
|
* @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
|
|
* will be applied relative to the parent transform.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.beginBitmapFill = function(image, repetition, matrix) {
|
|
return this._setFill(new G.Fill(null,matrix).bitmap(image, repetition));
|
|
};
|
|
|
|
/**
|
|
* Ends the current sub-path, and begins a new one with no fill. Functionally identical to <code>beginFill(null)</code>.
|
|
* A tiny API method "ef" also exists.
|
|
* @method endFill
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.endFill = function() {
|
|
return this.beginFill();
|
|
};
|
|
|
|
/**
|
|
* Sets the stroke style for the current sub-path. Like all drawing methods, this can be chained, so you can define
|
|
* the stroke style and color in a single line of code like so:
|
|
*
|
|
* myGraphics.setStrokeStyle(8,"round").beginStroke("#F00");
|
|
*
|
|
* A tiny API method "ss" also exists.
|
|
* @method setStrokeStyle
|
|
* @param {Number} thickness The width of the stroke.
|
|
* @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
|
|
* round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
|
|
* the tiny API.
|
|
* @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
|
|
* One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
|
|
* for use with the tiny API.
|
|
* @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
|
|
* controls at what point a mitered joint will be clipped.
|
|
* @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
|
|
* of active transformations.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.setStrokeStyle = function(thickness, caps, joints, miterLimit, ignoreScale) {
|
|
this._updateInstructions(true);
|
|
this._strokeStyle = this.command = new G.StrokeStyle(thickness, caps, joints, miterLimit, ignoreScale);
|
|
|
|
// ignoreScale lives on Stroke, not StrokeStyle, so we do a little trickery:
|
|
if (this._stroke) { this._stroke.ignoreScale = ignoreScale; }
|
|
this._strokeIgnoreScale = ignoreScale;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Begins a stroke with the specified color. This ends the current sub-path. A tiny API method "s" also exists.
|
|
* @method beginStroke
|
|
* @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
|
|
* null will result in no stroke.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.beginStroke = function(color) {
|
|
return this._setStroke(color ? new G.Stroke(color) : null);
|
|
};
|
|
|
|
/**
|
|
* Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
|
|
* example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
|
|
* square to display it:
|
|
*
|
|
* myGraphics.setStrokeStyle(10).
|
|
* beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
|
|
*
|
|
* A tiny API method "ls" also exists.
|
|
* @method beginLinearGradientStroke
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
|
|
* @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
|
|
* @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.beginLinearGradientStroke = function(colors, ratios, x0, y0, x1, y1) {
|
|
return this._setStroke(new G.Stroke().linearGradient(colors, ratios, x0, y0, x1, y1));
|
|
};
|
|
|
|
/**
|
|
* Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to
|
|
* blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it:
|
|
*
|
|
* myGraphics.setStrokeStyle(10)
|
|
* .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50)
|
|
* .drawRect(50, 90, 150, 110);
|
|
*
|
|
* A tiny API method "rs" also exists.
|
|
* @method beginRadialGradientStroke
|
|
* @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
|
|
* a gradient drawing from red to blue.
|
|
* @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
|
|
* 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
|
|
* to 100%.
|
|
* @param {Number} x0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} y0 Center position of the inner circle that defines the gradient.
|
|
* @param {Number} r0 Radius of the inner circle that defines the gradient.
|
|
* @param {Number} x1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} y1 Center position of the outer circle that defines the gradient.
|
|
* @param {Number} r1 Radius of the outer circle that defines the gradient.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.beginRadialGradientStroke = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
|
|
return this._setStroke(new G.Stroke().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
|
|
};
|
|
|
|
/**
|
|
* Begins a pattern fill using the specified image. This ends the current sub-path. Note that unlike bitmap fills,
|
|
* strokes do not currently support a matrix parameter due to limitations in the canvas API. A tiny API method "bs"
|
|
* also exists.
|
|
* @method beginBitmapStroke
|
|
* @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
|
|
* as the pattern.
|
|
* @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
|
|
* "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.beginBitmapStroke = function(image, repetition) {
|
|
// NOTE: matrix is not supported for stroke because transforms on strokes also affect the drawn stroke width.
|
|
return this._setStroke(new G.Stroke().bitmap(image, repetition));
|
|
};
|
|
|
|
/**
|
|
* Ends the current sub-path, and begins a new one with no stroke. Functionally identical to <code>beginStroke(null)</code>.
|
|
* A tiny API method "es" also exists.
|
|
* @method endStroke
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.endStroke = function() {
|
|
return this.beginStroke();
|
|
};
|
|
|
|
/**
|
|
* Maps the familiar ActionScript <code>curveTo()</code> method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}}
|
|
* method.
|
|
* @method curveTo
|
|
* @type {Function}
|
|
**/
|
|
p.curveTo = p.quadraticCurveTo;
|
|
|
|
/**
|
|
* Maps the familiar ActionScript <code>drawRect()</code> method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}}
|
|
* method.
|
|
* @method drawRect
|
|
* @type {Function}
|
|
**/
|
|
p.drawRect = p.rect;
|
|
|
|
/**
|
|
* Draws a rounded rectangle with all corners with the specified radius.
|
|
* @method drawRoundRect
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w
|
|
* @param {Number} h
|
|
* @param {Number} radius Corner radius.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.drawRoundRect = function(x, y, w, h, radius) {
|
|
return this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius);
|
|
};
|
|
|
|
/**
|
|
* Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii. A tiny API
|
|
* method "rc" also exists.
|
|
* @method drawRoundRectComplex
|
|
* @param {Number} x The horizontal coordinate to draw the round rect.
|
|
* @param {Number} y The vertical coordinate to draw the round rect.
|
|
* @param {Number} w The width of the round rect.
|
|
* @param {Number} h The height of the round rect.
|
|
* @param {Number} radiusTL Top left corner radius.
|
|
* @param {Number} radiusTR Top right corner radius.
|
|
* @param {Number} radiusBR Bottom right corner radius.
|
|
* @param {Number} radiusBL Bottom left corner radius.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.drawRoundRectComplex = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
|
|
return this.append(new G.RoundRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL));
|
|
};
|
|
|
|
/**
|
|
* Draws a circle with the specified radius at (x, y).
|
|
*
|
|
* var g = new createjs.Graphics();
|
|
* g.setStrokeStyle(1);
|
|
* g.beginStroke(createjs.Graphics.getRGB(0,0,0));
|
|
* g.beginFill(createjs.Graphics.getRGB(255,0,0));
|
|
* g.drawCircle(0,0,3);
|
|
*
|
|
* var s = new createjs.Shape(g);
|
|
* s.x = 100;
|
|
* s.y = 100;
|
|
*
|
|
* stage.addChild(s);
|
|
* stage.update();
|
|
*
|
|
* A tiny API method "dc" also exists.
|
|
* @method drawCircle
|
|
* @param {Number} x x coordinate center point of circle.
|
|
* @param {Number} y y coordinate center point of circle.
|
|
* @param {Number} radius Radius of circle.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.drawCircle = function(x, y, radius) {
|
|
return this.append(new G.Circle(x, y, radius));
|
|
};
|
|
|
|
/**
|
|
* Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}},
|
|
* except the width and height can be different. A tiny API method "de" also exists.
|
|
* @method drawEllipse
|
|
* @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
|
|
* which draws from center.
|
|
* @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
|
|
* which draws from the center.
|
|
* @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
|
|
* number.
|
|
* @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.drawEllipse = function(x, y, w, h) {
|
|
return this.append(new G.Ellipse(x, y, w, h));
|
|
};
|
|
|
|
/**
|
|
* Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of
|
|
* points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a
|
|
* radius of 50:
|
|
*
|
|
* myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90);
|
|
* // Note: -90 makes the first point vertical
|
|
*
|
|
* A tiny API method "dp" also exists.
|
|
*
|
|
* @method drawPolyStar
|
|
* @param {Number} x Position of the center of the shape.
|
|
* @param {Number} y Position of the center of the shape.
|
|
* @param {Number} radius The outer radius of the shape.
|
|
* @param {Number} sides The number of points on the star or sides on the polygon.
|
|
* @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
|
|
* polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
|
|
* @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
|
|
* directly to the right of the center.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.drawPolyStar = function(x, y, radius, sides, pointSize, angle) {
|
|
return this.append(new G.PolyStar(x, y, radius, sides, pointSize, angle));
|
|
};
|
|
|
|
/**
|
|
* Removed in favour of using custom command objects with {{#crossLink "Graphics/append"}}{{/crossLink}}.
|
|
* @method inject
|
|
* @deprecated
|
|
**/
|
|
|
|
/**
|
|
* Appends a graphics command object to the graphics queue. Command objects expose an "exec" method
|
|
* that accepts two parameters: the Context2D to operate on, and an arbitrary data object passed into
|
|
* {{#crossLink "Graphics/draw"}}{{/crossLink}}. The latter will usually be the Shape instance that called draw.
|
|
*
|
|
* This method is used internally by Graphics methods, such as drawCircle, but can also be used directly to insert
|
|
* built-in or custom graphics commands. For example:
|
|
*
|
|
* // attach data to our shape, so we can access it during the draw:
|
|
* myShape.color = "red";
|
|
*
|
|
* // append a Circle command object:
|
|
* myShape.graphics.append(new Graphics.Circle(50, 50, 30));
|
|
*
|
|
* // append a custom command object with an exec method that sets the fill style
|
|
* // based on the shape's data, and then fills the circle.
|
|
* myShape.graphics.append({exec:function(ctx, shape) {
|
|
* ctx.fillStyle = shape.color;
|
|
* ctx.fill();
|
|
* }});
|
|
*
|
|
* @method append
|
|
* @param {Object} command A graphics command object exposing an "exec" method.
|
|
* @param {boolean} clean The clean param is primarily for internal use. A value of true indicates that a command does not generate a path that should be stroked or filled.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.append = function(command, clean) {
|
|
this._activeInstructions.push(command);
|
|
this.command = command;
|
|
if (!clean) { this._dirty = true; }
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Decodes a compact encoded path string into a series of draw instructions.
|
|
* This format is not intended to be human readable, and is meant for use by authoring tools.
|
|
* The format uses a base64 character set, with each character representing 6 bits, to define a series of draw
|
|
* commands.
|
|
*
|
|
* Each command is comprised of a single "header" character followed by a variable number of alternating x and y
|
|
* position values. Reading the header bits from left to right (most to least significant): bits 1 to 3 specify the
|
|
* type of operation (0-moveTo, 1-lineTo, 2-quadraticCurveTo, 3-bezierCurveTo, 4-closePath, 5-7 unused). Bit 4
|
|
* indicates whether position values use 12 bits (2 characters) or 18 bits (3 characters), with a one indicating the
|
|
* latter. Bits 5 and 6 are currently unused.
|
|
*
|
|
* Following the header is a series of 0 (closePath), 2 (moveTo, lineTo), 4 (quadraticCurveTo), or 6 (bezierCurveTo)
|
|
* parameters. These parameters are alternating x/y positions represented by 2 or 3 characters (as indicated by the
|
|
* 4th bit in the command char). These characters consist of a 1 bit sign (1 is negative, 0 is positive), followed
|
|
* by an 11 (2 char) or 17 (3 char) bit integer value. All position values are in tenths of a pixel. Except in the
|
|
* case of move operations which are absolute, this value is a delta from the previous x or y position (as
|
|
* appropriate).
|
|
*
|
|
* For example, the string "A3cAAMAu4AAA" represents a line starting at -150,0 and ending at 150,0.
|
|
* <br />A - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per
|
|
* parameter.
|
|
* <br />n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits
|
|
* indicate 1500 tenths of a pixel.
|
|
* <br />AA - 000000000000. Absolute y position of 0.
|
|
* <br />I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter.
|
|
* <br />Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to
|
|
* provide an absolute position of +150.0px.
|
|
* <br />AAA - 000000000000000000. A y delta value of 0.
|
|
*
|
|
* A tiny API method "p" also exists.
|
|
* @method decodePath
|
|
* @param {String} str The path string to decode.
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.decodePath = function(str) {
|
|
var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath];
|
|
var paramCount = [2, 2, 4, 6, 0];
|
|
var i=0, l=str.length;
|
|
var params = [];
|
|
var x=0, y=0;
|
|
var base64 = Graphics.BASE_64;
|
|
|
|
while (i<l) {
|
|
var c = str.charAt(i);
|
|
var n = base64[c];
|
|
var fi = n>>3; // highest order bits 1-3 code for operation.
|
|
var f = instructions[fi];
|
|
// check that we have a valid instruction & that the unused bits are empty:
|
|
if (!f || (n&3)) { throw("bad path data (@"+i+"): "+c); }
|
|
var pl = paramCount[fi];
|
|
if (!fi) { x=y=0; } // move operations reset the position.
|
|
params.length = 0;
|
|
i++;
|
|
var charCount = (n>>2&1)+2; // 4th header bit indicates number size for this operation.
|
|
for (var p=0; p<pl; p++) {
|
|
var num = base64[str.charAt(i)];
|
|
var sign = (num>>5) ? -1 : 1;
|
|
num = ((num&31)<<6)|(base64[str.charAt(i+1)]);
|
|
if (charCount == 3) { num = (num<<6)|(base64[str.charAt(i+2)]); }
|
|
num = sign*num/10;
|
|
if (p%2) { x = (num += x); }
|
|
else { y = (num += y); }
|
|
params[p] = num;
|
|
i += charCount;
|
|
}
|
|
f.apply(this,params);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Stores all graphics commands so they won't be executed in future draws. Calling store() a second time adds to
|
|
* the existing store.
|
|
*
|
|
* This is useful in cases where you are creating vector graphics in an iterative manner, so that only new
|
|
* graphics need to be drawn (which can provide huge performance benefits), but you wish to retain all of
|
|
* the vector instructions for later use (ex. scaling, modifying, or exporting).
|
|
*
|
|
* Note that calling store() will force the active path (if any) to be ended in a manner similar to changing
|
|
* the fill or stroke.
|
|
*
|
|
* For example, consider a application where the user draws lines with the mouse. As each line segment (or collection of
|
|
* segments) are added to a Shape, it can be rasterized using {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}},
|
|
* and then stored, so that it can be redrawn at a different scale when the application is resized, or exported to SVG.
|
|
*
|
|
* // set up cache:
|
|
* myShape.cache(0,0,500,500,scale);
|
|
*
|
|
* // when the user drags, draw a new line:
|
|
* myShape.graphics.moveTo(oldX,oldY).lineTo(newX,newY);
|
|
* // then draw it into the existing cache:
|
|
* myShape.updateCache("source-over");
|
|
* // store the new line, so it isn't redrawn next time:
|
|
* myShape.store();
|
|
*
|
|
* // then, when the window resizes, we can re-render at a different scale:
|
|
* // first, unstore all our lines:
|
|
* myShape.unstore();
|
|
* // then cache using the new scale:
|
|
* myShape.cache(0,0,500,500,newScale);
|
|
* // finally, store the existing commands again:
|
|
* myShape.store();
|
|
*
|
|
* @method store
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.store = function() {
|
|
this._updateInstructions(true);
|
|
this._storeIndex = this._instructions.length;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Unstores any graphics commands that were previously stored using {{#crossLink "Graphics/store"}}{{/crossLink}}
|
|
* so that they will be executed in subsequent draw calls.
|
|
*
|
|
* @method unstore
|
|
* @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.unstore = function() {
|
|
this._storeIndex = 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns the graphics instructions array. Each entry is a graphics command object (ex. Graphics.Fill, Graphics.Rect)
|
|
* Modifying the array directly is very likely to result in unexpected behaviour.
|
|
*
|
|
* This method is mainly intended for introspection of the instructions (ex. for graphics export).
|
|
* @method getInstructions
|
|
* @return {Array} The graphics instructions array.
|
|
**/
|
|
p.getInstructions = function() {
|
|
this._updateInstructions();
|
|
return this._instructions;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Graphics instance. Note that the individual command objects are not cloned.
|
|
* @method clone
|
|
* @return {Graphics} A clone of the current Graphics instance.
|
|
**/
|
|
p.clone = function() {
|
|
var o = new Graphics();
|
|
o.command = this.command;
|
|
o._stroke = this._stroke;
|
|
o._strokeStyle = this._strokeStyle;
|
|
o._strokeIgnoreScale = this._strokeIgnoreScale;
|
|
o._fill = this._fill;
|
|
o._instructions = this._instructions.slice();
|
|
o._commitIndex = this._commitIndex;
|
|
o._activeInstructions = this._activeInstructions.slice();
|
|
o._dirty = this._dirty;
|
|
o._storeIndex = this._storeIndex;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Graphics]";
|
|
};
|
|
|
|
|
|
// tiny API:
|
|
/** Shortcut to moveTo.
|
|
* @method mt
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.mt = p.moveTo;
|
|
|
|
/** Shortcut to lineTo.
|
|
* @method lt
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.lt = p.lineTo;
|
|
|
|
/** Shortcut to arcTo.
|
|
* @method at
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.at = p.arcTo;
|
|
|
|
/** Shortcut to bezierCurveTo.
|
|
* @method bt
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.bt = p.bezierCurveTo;
|
|
|
|
/** Shortcut to quadraticCurveTo / curveTo.
|
|
* @method qt
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.qt = p.quadraticCurveTo;
|
|
|
|
/** Shortcut to arc.
|
|
* @method a
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.a = p.arc;
|
|
|
|
/** Shortcut to rect.
|
|
* @method r
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.r = p.rect;
|
|
|
|
/** Shortcut to closePath.
|
|
* @method cp
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.cp = p.closePath;
|
|
|
|
/** Shortcut to clear.
|
|
* @method c
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.c = p.clear;
|
|
|
|
/** Shortcut to beginFill.
|
|
* @method f
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.f = p.beginFill;
|
|
|
|
/** Shortcut to beginLinearGradientFill.
|
|
* @method lf
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.lf = p.beginLinearGradientFill;
|
|
|
|
/** Shortcut to beginRadialGradientFill.
|
|
* @method rf
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.rf = p.beginRadialGradientFill;
|
|
|
|
/** Shortcut to beginBitmapFill.
|
|
* @method bf
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.bf = p.beginBitmapFill;
|
|
|
|
/** Shortcut to endFill.
|
|
* @method ef
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.ef = p.endFill;
|
|
|
|
/** Shortcut to setStrokeStyle.
|
|
* @method ss
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.ss = p.setStrokeStyle;
|
|
|
|
/** Shortcut to beginStroke.
|
|
* @method s
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.s = p.beginStroke;
|
|
|
|
/** Shortcut to beginLinearGradientStroke.
|
|
* @method ls
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.ls = p.beginLinearGradientStroke;
|
|
|
|
/** Shortcut to beginRadialGradientStroke.
|
|
* @method rs
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.rs = p.beginRadialGradientStroke;
|
|
|
|
/** Shortcut to beginBitmapStroke.
|
|
* @method bs
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.bs = p.beginBitmapStroke;
|
|
|
|
/** Shortcut to endStroke.
|
|
* @method es
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.es = p.endStroke;
|
|
|
|
/** Shortcut to drawRect.
|
|
* @method dr
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.dr = p.drawRect;
|
|
|
|
/** Shortcut to drawRoundRect.
|
|
* @method rr
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.rr = p.drawRoundRect;
|
|
|
|
/** Shortcut to drawRoundRectComplex.
|
|
* @method rc
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.rc = p.drawRoundRectComplex;
|
|
|
|
/** Shortcut to drawCircle.
|
|
* @method dc
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.dc = p.drawCircle;
|
|
|
|
/** Shortcut to drawEllipse.
|
|
* @method de
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.de = p.drawEllipse;
|
|
|
|
/** Shortcut to drawPolyStar.
|
|
* @method dp
|
|
* @protected
|
|
* @type {Function}
|
|
**/
|
|
p.dp = p.drawPolyStar;
|
|
|
|
/** Shortcut to decodePath.
|
|
* @method p
|
|
* @protected
|
|
* @type Function
|
|
**/
|
|
p.p = p.decodePath;
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _updateInstructions
|
|
* @protected
|
|
**/
|
|
p._updateInstructions = function(commit) {
|
|
var instr = this._instructions, active = this._activeInstructions, commitIndex = this._commitIndex;
|
|
|
|
if (this._dirty && active.length) {
|
|
instr.length = commitIndex; // remove old, uncommitted commands
|
|
instr.push(Graphics.beginCmd);
|
|
instr.push.apply(instr, active);
|
|
|
|
if (this._fill) { instr.push(this._fill); }
|
|
if (this._stroke && this._strokeStyle) { instr.push(this._strokeStyle); }
|
|
if (this._stroke) { instr.push(this._stroke); }
|
|
|
|
this._dirty = false;
|
|
}
|
|
|
|
if (commit) {
|
|
active.length = 0;
|
|
this._commitIndex = instr.length;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _setFill
|
|
* @protected
|
|
**/
|
|
p._setFill = function(fill) {
|
|
this._updateInstructions(true);
|
|
if (this._fill = fill) { this.command = fill; }
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* @method _setStroke
|
|
* @protected
|
|
**/
|
|
p._setStroke = function(stroke) {
|
|
this._updateInstructions(true);
|
|
if (this._stroke = stroke) {
|
|
this.command = stroke;
|
|
stroke.ignoreScale = this._strokeIgnoreScale;
|
|
}
|
|
return this;
|
|
};
|
|
|
|
// Command Objects:
|
|
/**
|
|
* @namespace Graphics
|
|
*/
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class MoveTo
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
(G.LineTo = function(x, y) {
|
|
this.x = x; this.y = y;
|
|
}).prototype.exec = function(ctx) { ctx.lineTo(this.x,this.y); };
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class LineTo
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
(G.MoveTo = function(x, y) {
|
|
this.x = x; this.y = y;
|
|
}).prototype.exec = function(ctx) { ctx.moveTo(this.x, this.y); };
|
|
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class ArcTo
|
|
* @constructor
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} x2
|
|
* @param {Number} y2
|
|
* @param {Number} radius
|
|
**/
|
|
/**
|
|
* @property x1
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y1
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property x2
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y2
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radius
|
|
* @type Number
|
|
*/
|
|
(G.ArcTo = function(x1, y1, x2, y2, radius) {
|
|
this.x1 = x1; this.y1 = y1;
|
|
this.x2 = x2; this.y2 = y2;
|
|
this.radius = radius;
|
|
}).prototype.exec = function(ctx) { ctx.arcTo(this.x1, this.y1, this.x2, this.y2, this.radius); };
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Arc
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
* @param {Number} startAngle
|
|
* @param {Number} endAngle
|
|
* @param {Number} anticlockwise
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radius
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property startAngle
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property endAngle
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property anticlockwise
|
|
* @type Number
|
|
*/
|
|
(G.Arc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
|
|
this.x = x; this.y = y;
|
|
this.radius = radius;
|
|
this.startAngle = startAngle; this.endAngle = endAngle;
|
|
this.anticlockwise = !!anticlockwise;
|
|
}).prototype.exec = function(ctx) { ctx.arc(this.x, this.y, this.radius, this.startAngle, this.endAngle, this.anticlockwise); };
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class QuadraticCurveTo
|
|
* @constructor
|
|
* @param {Number} cpx
|
|
* @param {Number} cpy
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
**/
|
|
/**
|
|
* @property cpx
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property cpy
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
(G.QuadraticCurveTo = function(cpx, cpy, x, y) {
|
|
this.cpx = cpx; this.cpy = cpy;
|
|
this.x = x; this.y = y;
|
|
}).prototype.exec = function(ctx) { ctx.quadraticCurveTo(this.cpx, this.cpy, this.x, this.y); };
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class BezierCurveTo
|
|
* @constructor
|
|
* @param {Number} cp1x
|
|
* @param {Number} cp1y
|
|
* @param {Number} cp2x
|
|
* @param {Number} cp2y
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
**/
|
|
/**
|
|
* @property cp1x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property cp1y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property cp2x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property cp2y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
(G.BezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
this.cp1x = cp1x; this.cp1y = cp1y;
|
|
this.cp2x = cp2x; this.cp2y = cp2y;
|
|
this.x = x; this.y = y;
|
|
}).prototype.exec = function(ctx) { ctx.bezierCurveTo(this.cp1x, this.cp1y, this.cp2x, this.cp2y, this.x, this.y); };
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Rect
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w
|
|
* @param {Number} h
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property w
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property h
|
|
* @type Number
|
|
*/
|
|
(G.Rect = function(x, y, w, h) {
|
|
this.x = x; this.y = y;
|
|
this.w = w; this.h = h;
|
|
}).prototype.exec = function(ctx) { ctx.rect(this.x, this.y, this.w, this.h); };
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class ClosePath
|
|
* @constructor
|
|
**/
|
|
(G.ClosePath = function() {
|
|
}).prototype.exec = function(ctx) { ctx.closePath(); };
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class BeginPath
|
|
* @constructor
|
|
**/
|
|
(G.BeginPath = function() {
|
|
}).prototype.exec = function(ctx) { ctx.beginPath(); };
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Fill
|
|
* @constructor
|
|
* @param {Object} style A valid Context2D fillStyle.
|
|
* @param {Matrix2D} matrix
|
|
**/
|
|
/**
|
|
* A valid Context2D fillStyle.
|
|
* @property style
|
|
* @type Object
|
|
*/
|
|
/**
|
|
* @property matrix
|
|
* @type Matrix2D
|
|
*/
|
|
p = (G.Fill = function(style, matrix) {
|
|
this.style = style;
|
|
this.matrix = matrix;
|
|
}).prototype;
|
|
p.exec = function(ctx) {
|
|
if (!this.style) { return; }
|
|
ctx.fillStyle = this.style;
|
|
var mtx = this.matrix;
|
|
if (mtx) { ctx.save(); ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); }
|
|
ctx.fill();
|
|
if (mtx) { ctx.restore(); }
|
|
};
|
|
/**
|
|
* Creates a linear gradient style and assigns it to {{#crossLink "Fill/style:property"}}{{/crossLink}}.
|
|
* @method linearGradient
|
|
* @param {Array} colors
|
|
* @param {Array} ratios
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @return {Fill} Returns this Fill object for chaining or assignment.
|
|
*/
|
|
p.linearGradient = function(colors, ratios, x0, y0, x1, y1) {
|
|
var o = this.style = Graphics._ctx.createLinearGradient(x0, y0, x1, y1);
|
|
for (var i=0, l=colors.length; i<l; i++) { o.addColorStop(ratios[i], colors[i]); }
|
|
o.props = {colors:colors, ratios:ratios, x0:x0, y0:y0, x1:x1, y1:y1, type:"linear"};
|
|
return this;
|
|
};
|
|
/**
|
|
* Creates a radial gradient style and assigns it to {{#crossLink "Fill/style:property"}}{{/crossLink}}.
|
|
* @method radialGradient
|
|
* @param {Array} colors
|
|
* @param {Array} ratios
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} r0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} r1
|
|
* @return {Fill} Returns this Fill object for chaining or assignment.
|
|
*/
|
|
p.radialGradient = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
|
|
var o = this.style = Graphics._ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
|
|
for (var i=0, l=colors.length; i<l; i++) { o.addColorStop(ratios[i], colors[i]); }
|
|
o.props = {colors:colors, ratios:ratios, x0:x0, y0:y0, r0:r0, x1:x1, y1:y1, r1:r1, type:"radial"};
|
|
return this;
|
|
};
|
|
/**
|
|
* Creates a bitmap fill style and assigns it to {{#crossLink "Fill/style:property"}}{{/crossLink}}.
|
|
* @method bitmap
|
|
* @param {Image} image
|
|
* @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat.
|
|
* @return {Fill} Returns this Fill object for chaining or assignment.
|
|
*/
|
|
p.bitmap = function(image, repetition) {
|
|
var o = this.style = Graphics._ctx.createPattern(image, repetition||"");
|
|
o.props = {image:image, repetition:repetition, type:"bitmap"};
|
|
return this;
|
|
};
|
|
p.path = false;
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Stroke
|
|
* @constructor
|
|
* @param {Object} style A valid Context2D fillStyle.
|
|
* @param {Boolean} ignoreScale
|
|
**/
|
|
/**
|
|
* A valid Context2D strokeStyle.
|
|
* @property style
|
|
* @type Object
|
|
*/
|
|
/**
|
|
* @property ignoreScale
|
|
* @type Boolean
|
|
*/
|
|
p = (G.Stroke = function(style, ignoreScale) {
|
|
this.style = style;
|
|
this.ignoreScale = ignoreScale;
|
|
}).prototype;
|
|
p.exec = function(ctx) {
|
|
if (!this.style) { return; }
|
|
ctx.strokeStyle = this.style;
|
|
if (this.ignoreScale) { ctx.save(); ctx.setTransform(1,0,0,1,0,0); }
|
|
ctx.stroke();
|
|
if (this.ignoreScale) { ctx.restore(); }
|
|
};
|
|
/**
|
|
* Creates a linear gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
|
|
* @method linearGradient
|
|
* @param {Array} colors
|
|
* @param {Array} ratios
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @return {Fill} Returns this Stroke object for chaining or assignment.
|
|
*/
|
|
p.linearGradient = G.Fill.prototype.linearGradient;
|
|
/**
|
|
* Creates a radial gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
|
|
* @method radialGradient
|
|
* @param {Array} colors
|
|
* @param {Array} ratios
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} r0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} r1
|
|
* @return {Fill} Returns this Stroke object for chaining or assignment.
|
|
*/
|
|
p.radialGradient = G.Fill.prototype.radialGradient;
|
|
/**
|
|
* Creates a bitmap fill style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
|
|
* @method bitmap
|
|
* @param {Image} image
|
|
* @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat.
|
|
* @return {Fill} Returns this Stroke object for chaining or assignment.
|
|
*/
|
|
p.bitmap = G.Fill.prototype.bitmap;
|
|
p.path = false;
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class StrokeStyle
|
|
* @constructor
|
|
* @param {Number} width
|
|
* @param {String} caps
|
|
* @param {String} joints
|
|
* @param {Number} miterLimit
|
|
**/
|
|
/**
|
|
* @property width
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* One of: butt, round, square
|
|
* @property caps
|
|
* @type String
|
|
*/
|
|
/**
|
|
* One of: round, bevel, miter
|
|
* @property joints
|
|
* @type String
|
|
*/
|
|
/**
|
|
* @property miterLimit
|
|
* @type Number
|
|
*/
|
|
p = (G.StrokeStyle = function(width, caps, joints, miterLimit) {
|
|
this.width = width;
|
|
this.caps = caps;
|
|
this.joints = joints;
|
|
this.miterLimit = miterLimit;
|
|
}).prototype;
|
|
p.exec = function(ctx) {
|
|
ctx.lineWidth = (this.width == null ? "1" : this.width);
|
|
ctx.lineCap = (this.caps == null ? "butt" : this.caps);
|
|
ctx.lineJoin = (this.joints == null ? "miter" : this.joints);
|
|
ctx.miterLimit = (this.miterLimit == null ? "10" : this.miterLimit);
|
|
};
|
|
p.path = false;
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class RoundRect
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} w
|
|
* @param {Number} h
|
|
* @param {Number} radiusTL
|
|
* @param {Number} radiusTR
|
|
* @param {Number} radiusBR
|
|
* @param {Number} radiusBL
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property w
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property h
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radiusTL
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radiusTR
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radiusBR
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radiusBL
|
|
* @type Number
|
|
*/
|
|
(G.RoundRect = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
|
|
this.x = x; this.y = y;
|
|
this.w = w; this.h = h;
|
|
this.radiusTL = radiusTL; this.radiusTR = radiusTR;
|
|
this.radiusBR = radiusBR; this.radiusBL = radiusBL;
|
|
}).prototype.exec = function(ctx) {
|
|
var max = (w<h?w:h)/2;
|
|
var mTL=0, mTR=0, mBR=0, mBL=0;
|
|
var x = this.x, y = this.y, w = this.w, h = this.h;
|
|
var rTL = this.radiusTL, rTR = this.radiusTR, rBR = this.radiusBR, rBL = this.radiusBL;
|
|
|
|
if (rTL < 0) { rTL *= (mTL=-1); }
|
|
if (rTL > max) { rTL = max; }
|
|
if (rTR < 0) { rTR *= (mTR=-1); }
|
|
if (rTR > max) { rTR = max; }
|
|
if (rBR < 0) { rBR *= (mBR=-1); }
|
|
if (rBR > max) { rBR = max; }
|
|
if (rBL < 0) { rBL *= (mBL=-1); }
|
|
if (rBL > max) { rBL = max; }
|
|
|
|
ctx.moveTo(x+w-rTR, y);
|
|
ctx.arcTo(x+w+rTR*mTR, y-rTR*mTR, x+w, y+rTR, rTR);
|
|
ctx.lineTo(x+w, y+h-rBR);
|
|
ctx.arcTo(x+w+rBR*mBR, y+h+rBR*mBR, x+w-rBR, y+h, rBR);
|
|
ctx.lineTo(x+rBL, y+h);
|
|
ctx.arcTo(x-rBL*mBL, y+h+rBL*mBL, x, y+h-rBL, rBL);
|
|
ctx.lineTo(x, y+rTL);
|
|
ctx.arcTo(x-rTL*mTL, y-rTL*mTL, x+rTL, y, rTL);
|
|
ctx.closePath();
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class Circle
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radius
|
|
* @type Number
|
|
*/
|
|
(G.Circle = function(x, y, radius) {
|
|
this.x = x; this.y = y;
|
|
this.radius = radius;
|
|
}).prototype.exec = function(ctx) { ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); };
|
|
|
|
(G.Ellipse = function(x, y, w, h) {
|
|
this.x = x; this.y = y;
|
|
this.w = w; this.h = h;
|
|
}).prototype.exec = function(ctx) {
|
|
var x = this.x, y = this.y;
|
|
var w = this.w, h = this.h;
|
|
|
|
var k = 0.5522848;
|
|
var ox = (w / 2) * k;
|
|
var oy = (h / 2) * k;
|
|
var xe = x + w;
|
|
var ye = y + h;
|
|
var xm = x + w / 2;
|
|
var ym = y + h / 2;
|
|
|
|
ctx.moveTo(x, ym);
|
|
ctx.bezierCurveTo(x, ym-oy, xm-ox, y, xm, y);
|
|
ctx.bezierCurveTo(xm+ox, y, xe, ym-oy, xe, ym);
|
|
ctx.bezierCurveTo(xe, ym+oy, xm+ox, ye, xm, ye);
|
|
ctx.bezierCurveTo(xm-ox, ye, x, ym+oy, x, ym);
|
|
};
|
|
|
|
/**
|
|
* Graphics command object. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
|
|
* @class PolyStar
|
|
* @constructor
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} radius
|
|
* @param {Number} sides
|
|
* @param {Number} pointSize
|
|
* @param {Number} angle
|
|
**/
|
|
/**
|
|
* @property x
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property y
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property radius
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property sides
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property pointSize
|
|
* @type Number
|
|
*/
|
|
/**
|
|
* @property angle
|
|
* @type Number
|
|
*/
|
|
(G.PolyStar = function(x, y, radius, sides, pointSize, angle) {
|
|
this.x = x; this.y = y;
|
|
this.radius = radius;
|
|
this.sides = sides;
|
|
this.pointSize = pointSize;
|
|
this.angle = angle;
|
|
}).prototype.exec = function(ctx) {
|
|
var x = this.x, y = this.y;
|
|
var radius = this.radius;
|
|
var angle = (this.angle||0)/180*Math.PI;
|
|
var sides = this.sides;
|
|
var ps = 1-(this.pointSize||0);
|
|
var a = Math.PI/sides;
|
|
|
|
ctx.moveTo(x+Math.cos(angle)*radius, y+Math.sin(angle)*radius);
|
|
for (var i=0; i<sides; i++) {
|
|
angle += a;
|
|
if (ps != 1) {
|
|
ctx.lineTo(x+Math.cos(angle)*radius*ps, y+Math.sin(angle)*radius*ps);
|
|
}
|
|
angle += a;
|
|
ctx.lineTo(x+Math.cos(angle)*radius, y+Math.sin(angle)*radius);
|
|
}
|
|
ctx.closePath();
|
|
};
|
|
|
|
// docced above.
|
|
Graphics.beginCmd = new G.BeginPath(); // so we don't have to instantiate multiple instances.
|
|
|
|
|
|
createjs.Graphics = Graphics;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// DisplayObject.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* DisplayObject is an abstract class that should not be constructed directly. Instead construct subclasses such as
|
|
* {{#crossLink "Container"}}{{/crossLink}}, {{#crossLink "Bitmap"}}{{/crossLink}}, and {{#crossLink "Shape"}}{{/crossLink}}.
|
|
* DisplayObject is the base class for all display classes in the EaselJS library. It defines the core properties and
|
|
* methods that are shared between all display objects, such as transformation properties (x, y, scaleX, scaleY, etc),
|
|
* caching, and mouse handlers.
|
|
* @class DisplayObject
|
|
* @extends EventDispatcher
|
|
* @constructor
|
|
**/
|
|
function DisplayObject() {
|
|
this.EventDispatcher_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The alpha (transparency) for this display object. 0 is fully transparent, 1 is fully opaque.
|
|
* @property alpha
|
|
* @type {Number}
|
|
* @default 1
|
|
**/
|
|
this.alpha = 1;
|
|
|
|
/**
|
|
* If a cache is active, this returns the canvas that holds the cached version of this display object. See {{#crossLink "cache"}}{{/crossLink}}
|
|
* for more information.
|
|
* @property cacheCanvas
|
|
* @type {HTMLCanvasElement | Object}
|
|
* @default null
|
|
* @readonly
|
|
**/
|
|
this.cacheCanvas = null;
|
|
|
|
/**
|
|
* Returns an ID number that uniquely identifies the current cache for this display object. This can be used to
|
|
* determine if the cache has changed since a previous check.
|
|
* @property cacheID
|
|
* @type {Number}
|
|
* @default 0
|
|
*/
|
|
this.cacheID = 0;
|
|
|
|
/**
|
|
* Unique ID for this display object. Makes display objects easier for some uses.
|
|
* @property id
|
|
* @type {Number}
|
|
* @default -1
|
|
**/
|
|
this.id = createjs.UID.get();
|
|
|
|
/**
|
|
* Indicates whether to include this object when running mouse interactions. Setting this to `false` for children
|
|
* of a {{#crossLink "Container"}}{{/crossLink}} will cause events on the Container to not fire when that child is
|
|
* clicked. Setting this property to `false` does not prevent the {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}
|
|
* method from returning the child.
|
|
*
|
|
* <strong>Note:</strong> In EaselJS 0.7.0, the mouseEnabled property will not work properly with nested Containers. Please
|
|
* check out the latest NEXT version in <a href="https://github.com/CreateJS/EaselJS/tree/master/lib">GitHub</a> for an updated version with this issue resolved. The fix will be
|
|
* provided in the next release of EaselJS.
|
|
* @property mouseEnabled
|
|
* @type {Boolean}
|
|
* @default true
|
|
**/
|
|
this.mouseEnabled = true;
|
|
|
|
/**
|
|
* If false, the tick will not run on this display object (or its children). This can provide some performance benefits.
|
|
* In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
|
|
* on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling).
|
|
* @property tickEnabled
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.tickEnabled = true;
|
|
|
|
/**
|
|
* An optional name for this display object. Included in {{#crossLink "DisplayObject/toString"}}{{/crossLink}} . Useful for
|
|
* debugging.
|
|
* @property name
|
|
* @type {String}
|
|
* @default null
|
|
**/
|
|
this.name = null;
|
|
|
|
/**
|
|
* A reference to the {{#crossLink "Container"}}{{/crossLink}} or {{#crossLink "Stage"}}{{/crossLink}} object that
|
|
* contains this display object, or null if it has not been added
|
|
* to one.
|
|
* @property parent
|
|
* @final
|
|
* @type {Container}
|
|
* @default null
|
|
* @readonly
|
|
**/
|
|
this.parent = null;
|
|
|
|
/**
|
|
* The left offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate
|
|
* around its center, you would set regX and {{#crossLink "DisplayObject/regY:property"}}{{/crossLink}} to 50.
|
|
* @property regX
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.regX = 0;
|
|
|
|
/**
|
|
* The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around
|
|
* its center, you would set {{#crossLink "DisplayObject/regX:property"}}{{/crossLink}} and regY to 50.
|
|
* @property regY
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.regY = 0;
|
|
|
|
/**
|
|
* The rotation in degrees for this display object.
|
|
* @property rotation
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.rotation = 0;
|
|
|
|
/**
|
|
* The factor to stretch this display object horizontally. For example, setting scaleX to 2 will stretch the display
|
|
* object to twice its nominal width. To horizontally flip an object, set the scale to a negative number.
|
|
* @property scaleX
|
|
* @type {Number}
|
|
* @default 1
|
|
**/
|
|
this.scaleX = 1;
|
|
|
|
/**
|
|
* The factor to stretch this display object vertically. For example, setting scaleY to 0.5 will stretch the display
|
|
* object to half its nominal height. To vertically flip an object, set the scale to a negative number.
|
|
* @property scaleY
|
|
* @type {Number}
|
|
* @default 1
|
|
**/
|
|
this.scaleY = 1;
|
|
|
|
/**
|
|
* The factor to skew this display object horizontally.
|
|
* @property skewX
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.skewX = 0;
|
|
|
|
/**
|
|
* The factor to skew this display object vertically.
|
|
* @property skewY
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.skewY = 0;
|
|
|
|
/**
|
|
* A shadow object that defines the shadow to render on this display object. Set to `null` to remove a shadow. If
|
|
* null, this property is inherited from the parent container.
|
|
* @property shadow
|
|
* @type {Shadow}
|
|
* @default null
|
|
**/
|
|
this.shadow = null;
|
|
|
|
/**
|
|
* Indicates whether this display object should be rendered to the canvas and included when running the Stage
|
|
* {{#crossLink "Stage/getObjectsUnderPoint"}}{{/crossLink}} method.
|
|
* @property visible
|
|
* @type {Boolean}
|
|
* @default true
|
|
**/
|
|
this.visible = true;
|
|
|
|
/**
|
|
* The x (horizontal) position of the display object, relative to its parent.
|
|
* @property x
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.x = 0;
|
|
|
|
/** The y (vertical) position of the display object, relative to its parent.
|
|
* @property y
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.y = 0;
|
|
|
|
/**
|
|
* If set, defines the transformation for this display object, overriding all other transformation properties
|
|
* (x, y, rotation, scale, skew).
|
|
* @property transformMatrix
|
|
* @type {Matrix2D}
|
|
* @default null
|
|
**/
|
|
this.transformMatrix = null;
|
|
|
|
/**
|
|
* The composite operation indicates how the pixels of this display object will be composited with the elements
|
|
* behind it. If `null`, this property is inherited from the parent container. For more information, read the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing">
|
|
* whatwg spec on compositing</a>.
|
|
* @property compositeOperation
|
|
* @type {String}
|
|
* @default null
|
|
**/
|
|
this.compositeOperation = null;
|
|
|
|
/**
|
|
* Indicates whether the display object should be drawn to a whole pixel when
|
|
* {{#crossLink "Stage/snapToPixelEnabled"}}{{/crossLink}} is true. To enable/disable snapping on whole
|
|
* categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true).
|
|
* @property snapToPixel
|
|
* @type {Boolean}
|
|
* @default true
|
|
**/
|
|
this.snapToPixel = true;
|
|
|
|
/**
|
|
* An array of Filter objects to apply to this display object. Filters are only applied / updated when {{#crossLink "cache"}}{{/crossLink}}
|
|
* or {{#crossLink "updateCache"}}{{/crossLink}} is called on the display object, and only apply to the area that is
|
|
* cached.
|
|
* @property filters
|
|
* @type {Array}
|
|
* @default null
|
|
**/
|
|
this.filters = null;
|
|
|
|
/**
|
|
* A Shape instance that defines a vector mask (clipping path) for this display object. The shape's transformation
|
|
* will be applied relative to the display object's parent coordinates (as if it were a child of the parent).
|
|
* @property mask
|
|
* @type {Shape}
|
|
* @default null
|
|
*/
|
|
this.mask = null;
|
|
|
|
/**
|
|
* A display object that will be tested when checking mouse interactions or testing {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}.
|
|
* The hit area will have its transformation applied relative to this display object's coordinate space (as though
|
|
* the hit test object were a child of this display object and relative to its regX/Y). The hitArea will be tested
|
|
* using only its own `alpha` value regardless of the alpha value on the target display object, or the target's
|
|
* ancestors (parents).
|
|
*
|
|
* If set on a {{#crossLink "Container"}}{{/crossLink}}, children of the Container will not receive mouse events.
|
|
* This is similar to setting {{#crossLink "mouseChildren"}}{{/crossLink}} to false.
|
|
*
|
|
* Note that hitArea is NOT currently used by the `hitTest()` method, nor is it supported for {{#crossLink "Stage"}}{{/crossLink}}.
|
|
* @property hitArea
|
|
* @type {DisplayObject}
|
|
* @default null
|
|
*/
|
|
this.hitArea = null;
|
|
|
|
/**
|
|
* A CSS cursor (ex. "pointer", "help", "text", etc) that will be displayed when the user hovers over this display
|
|
* object. You must enable mouseover events using the {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} method to
|
|
* use this property. Setting a non-null cursor on a Container will override the cursor set on its descendants.
|
|
* @property cursor
|
|
* @type {String}
|
|
* @default null
|
|
*/
|
|
this.cursor = null;
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _cacheOffsetX
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this._cacheOffsetX = 0;
|
|
|
|
/**
|
|
* @property _cacheOffsetY
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this._cacheOffsetY = 0;
|
|
|
|
/**
|
|
* @property _cacheScale
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 1
|
|
**/
|
|
this._cacheScale = 1;
|
|
|
|
/**
|
|
* @property _cacheDataURLID
|
|
* @protected
|
|
* @type {Number}
|
|
* @default 0
|
|
*/
|
|
this._cacheDataURLID = 0;
|
|
|
|
/**
|
|
* @property _cacheDataURL
|
|
* @protected
|
|
* @type {String}
|
|
* @default null
|
|
*/
|
|
this._cacheDataURL = null;
|
|
|
|
/**
|
|
* @property _props
|
|
* @protected
|
|
* @type {DisplayObject}
|
|
* @default null
|
|
**/
|
|
this._props = new createjs.DisplayProps();
|
|
|
|
/**
|
|
* @property _rectangle
|
|
* @protected
|
|
* @type {Rectangle}
|
|
* @default null
|
|
**/
|
|
this._rectangle = new createjs.Rectangle();
|
|
|
|
/**
|
|
* @property _bounds
|
|
* @protected
|
|
* @type {Rectangle}
|
|
* @default null
|
|
**/
|
|
this._bounds = null;
|
|
}
|
|
var p = createjs.extend(DisplayObject, createjs.EventDispatcher);
|
|
|
|
|
|
// static properties:
|
|
/**
|
|
* Listing of mouse event names. Used in _hasMouseEventListener.
|
|
* @property _MOUSE_EVENTS
|
|
* @protected
|
|
* @static
|
|
* @type {Array}
|
|
**/
|
|
DisplayObject._MOUSE_EVENTS = ["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"];
|
|
|
|
/**
|
|
* Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}}
|
|
* with cross domain content.
|
|
* @property suppressCrossDomainErrors
|
|
* @static
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
DisplayObject.suppressCrossDomainErrors = false;
|
|
|
|
/**
|
|
* @property _snapToPixelEnabled
|
|
* @protected
|
|
* @static
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access.
|
|
|
|
/**
|
|
* @property _hitTestCanvas
|
|
* @type {HTMLCanvasElement | Object}
|
|
* @static
|
|
* @protected
|
|
**/
|
|
/**
|
|
* @property _hitTestContext
|
|
* @type {CanvasRenderingContext2D}
|
|
* @static
|
|
* @protected
|
|
**/
|
|
var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); // prevent errors on load in browsers without canvas.
|
|
if (canvas.getContext) {
|
|
DisplayObject._hitTestCanvas = canvas;
|
|
DisplayObject._hitTestContext = canvas.getContext("2d");
|
|
canvas.width = canvas.height = 1;
|
|
}
|
|
|
|
/**
|
|
* @property _nextCacheID
|
|
* @type {Number}
|
|
* @static
|
|
* @protected
|
|
**/
|
|
DisplayObject._nextCacheID = 1;
|
|
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when the user presses their left mouse button over the display object. See the
|
|
* {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event mousedown
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user presses their left mouse button and then releases it while over the display object.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event click
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user double clicks their left mouse button over this display object.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event dblclick
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user's mouse enters this display object. This event must be enabled using
|
|
* {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event mouseover
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user's mouse leaves this display object. This event must be enabled using
|
|
* {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event mouseout
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* This event is similar to {{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}, with the following
|
|
* differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
|
|
* aggregate of their content.
|
|
*
|
|
* For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
|
|
* shapeA and then directly on to shapeB. With a listener for {{#crossLink "mouseover:event"}}{{/crossLink}} on
|
|
* myContainer, two events would be received, each targeting a child element:<OL>
|
|
* <LI>when the mouse enters shapeA (target=shapeA)</LI>
|
|
* <LI>when the mouse enters shapeB (target=shapeB)</LI>
|
|
* </OL>
|
|
* However, with a listener for "rollover" instead, only a single event is received when the mouse first enters
|
|
* the aggregate myContainer content (target=myContainer).
|
|
*
|
|
* This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event rollover
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* This event is similar to {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}, with the following
|
|
* differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
|
|
* aggregate of their content.
|
|
*
|
|
* For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
|
|
* shapeA, then directly on to shapeB, then off both. With a listener for {{#crossLink "mouseout:event"}}{{/crossLink}}
|
|
* on myContainer, two events would be received, each targeting a child element:<OL>
|
|
* <LI>when the mouse leaves shapeA (target=shapeA)</LI>
|
|
* <LI>when the mouse leaves shapeB (target=shapeB)</LI>
|
|
* </OL>
|
|
* However, with a listener for "rollout" instead, only a single event is received when the mouse leaves
|
|
* the aggregate myContainer content (target=myContainer).
|
|
*
|
|
* This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event rollout
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressmove
|
|
* event will be generated on that object whenever the mouse moves until the mouse press is released. This can be
|
|
* useful for dragging and similar operations.
|
|
* @event pressmove
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressup event
|
|
* will be generated on that object when that mouse press is released. This can be useful for dragging and similar
|
|
* operations.
|
|
* @event pressup
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the display object is added to a parent container.
|
|
* @event added
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the display object is removed from its parent container.
|
|
* @event removed
|
|
*/
|
|
|
|
/**
|
|
* Dispatched on each display object on a stage whenever the stage updates. This occurs immediately before the
|
|
* rendering (draw) pass. When {{#crossLink "Stage/update"}}{{/crossLink}} is called, first all display objects on
|
|
* the stage dispatch the tick event, then all of the display objects are drawn to stage. Children will have their
|
|
* {{#crossLink "tick:event"}}{{/crossLink}} event dispatched in order of their depth prior to the event being
|
|
* dispatched on their parent.
|
|
* @event tick
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @param {Array} params An array containing any arguments that were passed to the Stage.update() method. For
|
|
* example if you called stage.update("hello"), then the params would be ["hello"].
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function() {
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns <code>true</code> if the draw was handled (useful for overriding functionality).
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
|
|
* used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
|
|
* @return {Boolean}
|
|
**/
|
|
p.draw = function(ctx, ignoreCache) {
|
|
var cacheCanvas = this.cacheCanvas;
|
|
if (ignoreCache || !cacheCanvas) { return false; }
|
|
var scale = this._cacheScale, offX = this._cacheOffsetX, offY = this._cacheOffsetY, fBounds;
|
|
if (fBounds = this._applyFilterBounds(offX, offY, 0, 0)) {
|
|
offX = fBounds.x;
|
|
offY = fBounds.y;
|
|
}
|
|
ctx.drawImage(cacheCanvas, offX, offY, cacheCanvas.width/scale, cacheCanvas.height/scale);
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Applies this display object's transformation, alpha, globalCompositeOperation, clipping path (mask), and shadow
|
|
* to the specified context. This is typically called prior to {{#crossLink "DisplayObject/draw"}}{{/crossLink}}.
|
|
* @method updateContext
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D to update.
|
|
**/
|
|
p.updateContext = function(ctx) {
|
|
var o=this, mask=o.mask, mtx= o._props.matrix;
|
|
|
|
if (mask && mask.graphics && !mask.graphics.isEmpty()) {
|
|
mask.getMatrix(mtx);
|
|
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
|
|
|
|
mask.graphics.drawAsPath(ctx);
|
|
ctx.clip();
|
|
|
|
mtx.invert();
|
|
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
|
|
}
|
|
|
|
this.getMatrix(mtx);
|
|
var tx = mtx.tx, ty = mtx.ty;
|
|
if (DisplayObject._snapToPixelEnabled && o.snapToPixel) {
|
|
tx = tx + (tx < 0 ? -0.5 : 0.5) | 0;
|
|
ty = ty + (ty < 0 ? -0.5 : 0.5) | 0;
|
|
}
|
|
ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty);
|
|
ctx.globalAlpha *= o.alpha;
|
|
if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; }
|
|
if (o.shadow) { this._applyShadow(ctx, o.shadow); }
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into a new canvas, which is then used for subsequent draws. For complex content
|
|
* that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape),
|
|
* this can provide for much faster rendering because the content does not need to be re-rendered each tick. The
|
|
* cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must
|
|
* manually update the cache by calling <code>updateCache()</code> or <code>cache()</code> again. You must specify
|
|
* the cache area via the x, y, w, and h parameters. This defines the rectangle that will be rendered and cached
|
|
* using this display object's coordinates.
|
|
*
|
|
* <h4>Example</h4>
|
|
* For example if you defined a Shape that drew a circle at 0, 0 with a radius of 25:
|
|
*
|
|
* var shape = new createjs.Shape();
|
|
* shape.graphics.beginFill("#ff0000").drawCircle(0, 0, 25);
|
|
* myShape.cache(-25, -25, 50, 50);
|
|
*
|
|
* Note that filters need to be defined <em>before</em> the cache is applied. Check out the {{#crossLink "Filter"}}{{/crossLink}}
|
|
* class for more information. Some filters (ex. BlurFilter) will not work as expected in conjunction with the scale param.
|
|
*
|
|
* Usually, the resulting cacheCanvas will have the dimensions width*scale by height*scale, however some filters (ex. BlurFilter)
|
|
* will add padding to the canvas dimensions.
|
|
*
|
|
* @method cache
|
|
* @param {Number} x The x coordinate origin for the cache region.
|
|
* @param {Number} y The y coordinate origin for the cache region.
|
|
* @param {Number} width The width of the cache region.
|
|
* @param {Number} height The height of the cache region.
|
|
* @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape using
|
|
* myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and rotate
|
|
* cached elements with greater fidelity. Default is 1.
|
|
**/
|
|
p.cache = function(x, y, width, height, scale) {
|
|
// draw to canvas.
|
|
scale = scale||1;
|
|
if (!this.cacheCanvas) { this.cacheCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); }
|
|
this._cacheWidth = width;
|
|
this._cacheHeight = height;
|
|
this._cacheOffsetX = x;
|
|
this._cacheOffsetY = y;
|
|
this._cacheScale = scale;
|
|
this.updateCache();
|
|
};
|
|
|
|
/**
|
|
* Redraws the display object to its cache. Calling updateCache without an active cache will throw an error.
|
|
* If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object
|
|
* will be drawn over the existing cache using the specified compositeOperation.
|
|
*
|
|
* <h4>Example</h4>
|
|
* Clear the current graphics of a cached shape, draw some new instructions, and then update the cache. The new line
|
|
* will be drawn on top of the old one.
|
|
*
|
|
* // Not shown: Creating the shape, and caching it.
|
|
* shapeInstance.clear();
|
|
* shapeInstance.setStrokeStyle(3).beginStroke("#ff0000").moveTo(100, 100).lineTo(200,200);
|
|
* shapeInstance.updateCache();
|
|
*
|
|
* @method updateCache
|
|
* @param {String} compositeOperation The compositeOperation to use, or null to clear the cache and redraw it.
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing">
|
|
* whatwg spec on compositing</a>.
|
|
**/
|
|
p.updateCache = function(compositeOperation) {
|
|
var cacheCanvas = this.cacheCanvas, scale = this._cacheScale, offX = this._cacheOffsetX*scale, offY = this._cacheOffsetY*scale;
|
|
var w = this._cacheWidth, h = this._cacheHeight, fBounds;
|
|
if (!cacheCanvas) { throw "cache() must be called before updateCache()"; }
|
|
var ctx = cacheCanvas.getContext("2d");
|
|
|
|
// update bounds based on filters:
|
|
if (fBounds = this._applyFilterBounds(offX, offY, w, h)) {
|
|
offX = fBounds.x;
|
|
offY = fBounds.y;
|
|
w = fBounds.width;
|
|
h = fBounds.height;
|
|
}
|
|
|
|
w = Math.ceil(w*scale);
|
|
h = Math.ceil(h*scale);
|
|
if (w != cacheCanvas.width || h != cacheCanvas.height) {
|
|
// TODO: it would be nice to preserve the content if there is a compositeOperation.
|
|
cacheCanvas.width = w;
|
|
cacheCanvas.height = h;
|
|
} else if (!compositeOperation) {
|
|
ctx.clearRect(0, 0, w+1, h+1);
|
|
}
|
|
|
|
ctx.save();
|
|
ctx.globalCompositeOperation = compositeOperation;
|
|
ctx.setTransform(scale, 0, 0, scale, -offX, -offY);
|
|
this.draw(ctx, true);
|
|
// TODO: filters and cache scale don't play well together at present.
|
|
this._applyFilters();
|
|
ctx.restore();
|
|
this.cacheID = DisplayObject._nextCacheID++;
|
|
};
|
|
|
|
/**
|
|
* Clears the current cache. See {{#crossLink "DisplayObject/cache"}}{{/crossLink}} for more information.
|
|
* @method uncache
|
|
**/
|
|
p.uncache = function() {
|
|
this._cacheDataURL = this.cacheCanvas = null;
|
|
this.cacheID = this._cacheOffsetX = this._cacheOffsetY = 0;
|
|
this._cacheScale = 1;
|
|
};
|
|
|
|
/**
|
|
* Returns a data URL for the cache, or null if this display object is not cached.
|
|
* Uses cacheID to ensure a new data URL is not generated if the cache has not changed.
|
|
* @method getCacheDataURL
|
|
* @return {String} The image data url for the cache.
|
|
**/
|
|
p.getCacheDataURL = function() {
|
|
if (!this.cacheCanvas) { return null; }
|
|
if (this.cacheID != this._cacheDataURLID) { this._cacheDataURL = this.cacheCanvas.toDataURL(); }
|
|
return this._cacheDataURL;
|
|
};
|
|
|
|
/**
|
|
* Returns the stage that this display object will be rendered on, or null if it has not been added to one.
|
|
* @method getStage
|
|
* @return {Stage} The Stage instance that the display object is a descendent of. null if the DisplayObject has not
|
|
* been added to a Stage.
|
|
**/
|
|
p.getStage = function() {
|
|
var o = this;
|
|
while (o.parent) {
|
|
o = o.parent;
|
|
}
|
|
// using dynamic access to avoid circular dependencies;
|
|
if (o instanceof createjs["Stage"]) { return o; }
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Transforms the specified x and y position from the coordinate space of the display object
|
|
* to the global (stage) coordinate space. For example, this could be used to position an HTML label
|
|
* over a specific point on a nested display object. Returns a Point instance with x and y properties
|
|
* correlating to the transformed coordinates on the stage.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* displayObject.x = 300;
|
|
* displayObject.y = 200;
|
|
* stage.addChild(displayObject);
|
|
* var point = myDisplayObject.localToGlobal(100, 100);
|
|
* // Results in x=400, y=300
|
|
*
|
|
* @method localToGlobal
|
|
* @param {Number} x The x position in the source display object to transform.
|
|
* @param {Number} y The y position in the source display object to transform.
|
|
* @return {Point} A Point instance with x and y properties correlating to the transformed coordinates
|
|
* on the stage.
|
|
**/
|
|
p.localToGlobal = function(x, y) {
|
|
var mtx = this.getConcatenatedMatrix(this._props.matrix).prepend(1, 0, 0, 1, x, y);
|
|
return new createjs.Point(mtx.tx, mtx.ty);
|
|
};
|
|
|
|
/**
|
|
* Transforms the specified x and y position from the global (stage) coordinate space to the
|
|
* coordinate space of the display object. For example, this could be used to determine
|
|
* the current mouse position within the display object. Returns a Point instance with x and y properties
|
|
* correlating to the transformed position in the display object's coordinate space.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* displayObject.x = 300;
|
|
* displayObject.y = 200;
|
|
* stage.addChild(displayObject);
|
|
* var point = myDisplayObject.globalToLocal(100, 100);
|
|
* // Results in x=-200, y=-100
|
|
*
|
|
* @method globalToLocal
|
|
* @param {Number} x The x position on the stage to transform.
|
|
* @param {Number} y The y position on the stage to transform.
|
|
* @return {Point} A Point instance with x and y properties correlating to the transformed position in the
|
|
* display object's coordinate space.
|
|
**/
|
|
p.globalToLocal = function(x, y) {
|
|
var mtx = this.getConcatenatedMatrix(this._props.matrix).invert().prepend(1, 0, 0, 1, x, y);
|
|
return new createjs.Point(mtx.tx, mtx.ty);
|
|
};
|
|
|
|
/**
|
|
* Transforms the specified x and y position from the coordinate space of this display object to the coordinate
|
|
* space of the target display object. Returns a Point instance with x and y properties correlating to the
|
|
* transformed position in the target's coordinate space. Effectively the same as using the following code with
|
|
* {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
|
|
*
|
|
* var pt = this.localToGlobal(x, y);
|
|
* pt = target.globalToLocal(pt.x, pt.y);
|
|
*
|
|
* @method localToLocal
|
|
* @param {Number} x The x position in the source display object to transform.
|
|
* @param {Number} y The y position on the source display object to transform.
|
|
* @param {DisplayObject} target The target display object to which the coordinates will be transformed.
|
|
* @return {Point} Returns a Point instance with x and y properties correlating to the transformed position
|
|
* in the target's coordinate space.
|
|
**/
|
|
p.localToLocal = function(x, y, target) {
|
|
var pt = this.localToGlobal(x, y);
|
|
return target.globalToLocal(pt.x, pt.y);
|
|
};
|
|
|
|
/**
|
|
* Shortcut method to quickly set the transform properties on the display object. All parameters are optional.
|
|
* Omitted parameters will have the default value set.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* displayObject.setTransform(100, 100, 2, 2);
|
|
*
|
|
* @method setTransform
|
|
* @param {Number} [x=0] The horizontal translation (x position) in pixels
|
|
* @param {Number} [y=0] The vertical translation (y position) in pixels
|
|
* @param {Number} [scaleX=1] The horizontal scale, as a percentage of 1
|
|
* @param {Number} [scaleY=1] the vertical scale, as a percentage of 1
|
|
* @param {Number} [rotation=0] The rotation, in degrees
|
|
* @param {Number} [skewX=0] The horizontal skew factor
|
|
* @param {Number} [skewY=0] The vertical skew factor
|
|
* @param {Number} [regX=0] The horizontal registration point in pixels
|
|
* @param {Number} [regY=0] The vertical registration point in pixels
|
|
* @return {DisplayObject} Returns this instance. Useful for chaining commands.
|
|
*/
|
|
p.setTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
|
|
this.x = x || 0;
|
|
this.y = y || 0;
|
|
this.scaleX = scaleX == null ? 1 : scaleX;
|
|
this.scaleY = scaleY == null ? 1 : scaleY;
|
|
this.rotation = rotation || 0;
|
|
this.skewX = skewX || 0;
|
|
this.skewY = skewY || 0;
|
|
this.regX = regX || 0;
|
|
this.regY = regY || 0;
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a matrix based on this object's current transform.
|
|
* @method getMatrix
|
|
* @param {Matrix2D} matrix Optional. A Matrix2D object to populate with the calculated values. If null, a new
|
|
* Matrix object is returned.
|
|
* @return {Matrix2D} A matrix representing this display object's transform.
|
|
**/
|
|
p.getMatrix = function(matrix) {
|
|
var o = this, mtx = matrix&&matrix.identity() || new createjs.Matrix2D();
|
|
return o.transformMatrix ? mtx.copy(o.transformMatrix) : mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
|
|
};
|
|
|
|
/**
|
|
* Generates a Matrix2D object representing the combined transform of the display object and all of its
|
|
* parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). This can
|
|
* be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
|
|
* and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
|
|
* @method getConcatenatedMatrix
|
|
* @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values.
|
|
* If null, a new Matrix2D object is returned.
|
|
* @return {Matrix2D} The combined matrix.
|
|
**/
|
|
p.getConcatenatedMatrix = function(matrix) {
|
|
var o = this, mtx = this.getMatrix(matrix);
|
|
while (o = o.parent) {
|
|
mtx.appendMatrix(o.getMatrix(o._props.matrix));
|
|
}
|
|
return mtx;
|
|
};
|
|
|
|
/**
|
|
* Generates a DisplayProps object representing the combined display properties of the object and all of its
|
|
* parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}).
|
|
* @method getConcatenatedDisplayProps
|
|
* @param {DisplayProps} [props] A {{#crossLink "DisplayProps"}}{{/crossLink}} object to populate with the calculated values.
|
|
* If null, a new DisplayProps object is returned.
|
|
* @return {DisplayProps} The combined display properties.
|
|
**/
|
|
p.getConcatenatedDisplayProps = function(props) {
|
|
props = props ? props.identity() : new createjs.DisplayProps();
|
|
var o = this, mtx = o.getMatrix(props.matrix);
|
|
do {
|
|
props.append(o.visible, o.alpha, o.shadow, o.compositeOperation);
|
|
|
|
// we do this to avoid problems with the matrix being used for both operations when o._props.matrix is passed in as the props param.
|
|
// this could be simplified (ie. just done as part of the append above) if we switched to using a pool.
|
|
if (o != this) { mtx.appendMatrix(o.getMatrix(o._props.matrix)); }
|
|
} while (o = o.parent);
|
|
return props;
|
|
};
|
|
|
|
/**
|
|
* Tests whether the display object intersects the specified point in local coordinates (ie. draws a pixel with alpha > 0 at
|
|
* the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation of the display object.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* stage.addEventListener("stagemousedown", handleMouseDown);
|
|
* function handleMouseDown(event) {
|
|
* var hit = myShape.hitTest(event.stageX, event.stageY);
|
|
* }
|
|
*
|
|
* Please note that shape-to-shape collision is not currently supported by EaselJS.
|
|
* @method hitTest
|
|
* @param {Number} x The x position to check in the display object's local coordinates.
|
|
* @param {Number} y The y position to check in the display object's local coordinates.
|
|
* @return {Boolean} A Boolean indicting whether a visible portion of the DisplayObject intersect the specified
|
|
* local Point.
|
|
*/
|
|
p.hitTest = function(x, y) {
|
|
var ctx = DisplayObject._hitTestContext;
|
|
ctx.setTransform(1, 0, 0, 1, -x, -y);
|
|
this.draw(ctx);
|
|
|
|
var hit = this._testHit(ctx);
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, 2, 2);
|
|
return hit;
|
|
};
|
|
|
|
/**
|
|
* Provides a chainable shortcut method for setting a number of properties on the instance.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var myGraphics = new createjs.Graphics().beginFill("#ff0000").drawCircle(0, 0, 25);
|
|
* var shape = stage.addChild(new Shape())
|
|
* .set({graphics:myGraphics, x:100, y:100, alpha:0.5});
|
|
*
|
|
* @method set
|
|
* @param {Object} props A generic object containing properties to copy to the DisplayObject instance.
|
|
* @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.)
|
|
*/
|
|
p.set = function(props) {
|
|
for (var n in props) { this[n] = props[n]; }
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a rectangle representing this object's bounds in its local coordinate system (ie. with no transformation).
|
|
* Objects that have been cached will return the bounds of the cache.
|
|
*
|
|
* Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use
|
|
* {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
|
|
* bounds.
|
|
*
|
|
* <table>
|
|
* <tr><td><b>All</b></td><td>
|
|
* All display objects support setting bounds manually using setBounds(). Likewise, display objects that
|
|
* have been cached using cache() will return the bounds of their cache. Manual and cache bounds will override
|
|
* the automatic calculations listed below.
|
|
* </td></tr>
|
|
* <tr><td><b>Bitmap</b></td><td>
|
|
* Returns the width and height of the sourceRect (if specified) or image, extending from (x=0,y=0).
|
|
* </td></tr>
|
|
* <tr><td><b>Sprite</b></td><td>
|
|
* Returns the bounds of the current frame. May have non-zero x/y if a frame registration point was specified
|
|
* in the spritesheet data. See also {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}}
|
|
* </td></tr>
|
|
* <tr><td><b>Container</b></td><td>
|
|
* Returns the aggregate (combined) bounds of all children that return a non-null value from getBounds().
|
|
* </td></tr>
|
|
* <tr><td><b>Shape</b></td><td>
|
|
* Does not currently support automatic bounds calculations. Use setBounds() to manually define bounds.
|
|
* </td></tr>
|
|
* <tr><td><b>Text</b></td><td>
|
|
* Returns approximate bounds. Horizontal values (x/width) are quite accurate, but vertical values (y/height) are
|
|
* not, especially when using textBaseline values other than "top".
|
|
* </td></tr>
|
|
* <tr><td><b>BitmapText</b></td><td>
|
|
* Returns approximate bounds. Values will be more accurate if spritesheet frame registration points are close
|
|
* to (x=0,y=0).
|
|
* </td></tr>
|
|
* </table>
|
|
*
|
|
* Bounds can be expensive to calculate for some objects (ex. text, or containers with many children), and
|
|
* are recalculated each time you call getBounds(). You can prevent recalculation on static objects by setting the
|
|
* bounds explicitly:
|
|
*
|
|
* var bounds = obj.getBounds();
|
|
* obj.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
|
|
* // getBounds will now use the set values, instead of recalculating
|
|
*
|
|
* To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
|
|
* values if you need to retain it.
|
|
*
|
|
* var myBounds = obj.getBounds().clone();
|
|
* // OR:
|
|
* myRect.copy(obj.getBounds());
|
|
*
|
|
* @method getBounds
|
|
* @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this
|
|
* object.
|
|
**/
|
|
p.getBounds = function() {
|
|
if (this._bounds) { return this._rectangle.copy(this._bounds); }
|
|
var cacheCanvas = this.cacheCanvas;
|
|
if (cacheCanvas) {
|
|
var scale = this._cacheScale;
|
|
return this._rectangle.setValues(this._cacheOffsetX, this._cacheOffsetY, cacheCanvas.width/scale, cacheCanvas.height/scale);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Returns a rectangle representing this object's bounds in its parent's coordinate system (ie. with transformations applied).
|
|
* Objects that have been cached will return the transformed bounds of the cache.
|
|
*
|
|
* Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use
|
|
* {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
|
|
* bounds.
|
|
*
|
|
* To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
|
|
* values if you need to retain it.
|
|
*
|
|
* Container instances calculate aggregate bounds for all children that return bounds via getBounds.
|
|
* @method getTransformedBounds
|
|
* @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this object.
|
|
**/
|
|
p.getTransformedBounds = function() {
|
|
return this._getBounds();
|
|
};
|
|
|
|
/**
|
|
* Allows you to manually specify the bounds of an object that either cannot calculate their own bounds (ex. Shape &
|
|
* Text) for future reference, or so the object can be included in Container bounds. Manually set bounds will always
|
|
* override calculated bounds.
|
|
*
|
|
* The bounds should be specified in the object's local (untransformed) coordinates. For example, a Shape instance
|
|
* with a 25px radius circle centered at 0,0 would have bounds of (-25, -25, 50, 50).
|
|
* @method setBounds
|
|
* @param {Number} x The x origin of the bounds. Pass null to remove the manual bounds.
|
|
* @param {Number} y The y origin of the bounds.
|
|
* @param {Number} width The width of the bounds.
|
|
* @param {Number} height The height of the bounds.
|
|
**/
|
|
p.setBounds = function(x, y, width, height) {
|
|
if (x == null) { this._bounds = x; }
|
|
this._bounds = (this._bounds || new createjs.Rectangle()).setValues(x, y, width, height);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this DisplayObject. Some properties that are specific to this instance's current context are
|
|
* reverted to their defaults (for example .parent). Caches are not maintained across clones, and some elements
|
|
* are copied by reference (masks, individual filter instances, hit area)
|
|
* @method clone
|
|
* @return {DisplayObject} A clone of the current DisplayObject instance.
|
|
**/
|
|
p.clone = function() {
|
|
return this._cloneProps(new DisplayObject());
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[DisplayObject (name="+ this.name +")]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
// separated so it can be used more easily in subclasses:
|
|
/**
|
|
* @method _cloneProps
|
|
* @param {DisplayObject} o The DisplayObject instance which will have properties from the current DisplayObject
|
|
* instance copied into.
|
|
* @return {DisplayObject} o
|
|
* @protected
|
|
**/
|
|
p._cloneProps = function(o) {
|
|
o.alpha = this.alpha;
|
|
o.mouseEnabled = this.mouseEnabled;
|
|
o.tickEnabled = this.tickEnabled;
|
|
o.name = this.name;
|
|
o.regX = this.regX;
|
|
o.regY = this.regY;
|
|
o.rotation = this.rotation;
|
|
o.scaleX = this.scaleX;
|
|
o.scaleY = this.scaleY;
|
|
o.shadow = this.shadow;
|
|
o.skewX = this.skewX;
|
|
o.skewY = this.skewY;
|
|
o.visible = this.visible;
|
|
o.x = this.x;
|
|
o.y = this.y;
|
|
o.compositeOperation = this.compositeOperation;
|
|
o.snapToPixel = this.snapToPixel;
|
|
o.filters = this.filters==null?null:this.filters.slice(0);
|
|
o.mask = this.mask;
|
|
o.hitArea = this.hitArea;
|
|
o.cursor = this.cursor;
|
|
o._bounds = this._bounds;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* @method _applyShadow
|
|
* @protected
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @param {Shadow} shadow
|
|
**/
|
|
p._applyShadow = function(ctx, shadow) {
|
|
shadow = shadow || Shadow.identity;
|
|
ctx.shadowColor = shadow.color;
|
|
ctx.shadowOffsetX = shadow.offsetX;
|
|
ctx.shadowOffsetY = shadow.offsetY;
|
|
ctx.shadowBlur = shadow.blur;
|
|
};
|
|
|
|
|
|
/**
|
|
* @method _tick
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* @protected
|
|
**/
|
|
p._tick = function(evtObj) {
|
|
// because tick can be really performance sensitive, check for listeners before calling dispatchEvent.
|
|
var ls = this._listeners;
|
|
if (ls && ls["tick"]) {
|
|
// reset & reuse the event object to avoid construction / GC costs:
|
|
evtObj.target = null;
|
|
evtObj.propagationStopped = evtObj.immediatePropagationStopped = false;
|
|
this.dispatchEvent(evtObj);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _testHit
|
|
* @protected
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @return {Boolean}
|
|
**/
|
|
p._testHit = function(ctx) {
|
|
try {
|
|
var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1;
|
|
} catch (e) {
|
|
if (!DisplayObject.suppressCrossDomainErrors) {
|
|
throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";
|
|
}
|
|
}
|
|
return hit;
|
|
};
|
|
|
|
/**
|
|
* @method _applyFilters
|
|
* @protected
|
|
**/
|
|
p._applyFilters = function() {
|
|
if (!this.filters || this.filters.length == 0 || !this.cacheCanvas) { return; }
|
|
var l = this.filters.length;
|
|
var ctx = this.cacheCanvas.getContext("2d");
|
|
var w = this.cacheCanvas.width;
|
|
var h = this.cacheCanvas.height;
|
|
for (var i=0; i<l; i++) {
|
|
this.filters[i].applyFilter(ctx, 0, 0, w, h);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _applyFilterBounds
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Number} width
|
|
* @param {Number} height
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._applyFilterBounds = function(x, y, width, height) {
|
|
var bounds, l, filters = this.filters;
|
|
if (!filters || !(l=filters.length)) { return null; }
|
|
|
|
for (var i=0; i<l; i++) {
|
|
var f = this.filters[i];
|
|
var fBounds = f.getBounds&&f.getBounds();
|
|
if (!fBounds) { continue; }
|
|
if (!bounds) { bounds = this._rectangle.setValues(x,y,width,height); }
|
|
bounds.x += fBounds.x;
|
|
bounds.y += fBounds.y;
|
|
bounds.width += fBounds.width;
|
|
bounds.height += fBounds.height;
|
|
}
|
|
return bounds;
|
|
};
|
|
|
|
/**
|
|
* @method _getBounds
|
|
* @param {Matrix2D} matrix
|
|
* @param {Boolean} ignoreTransform If true, does not apply this object's transform.
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._getBounds = function(matrix, ignoreTransform){
|
|
return this._transformBounds(this.getBounds(), matrix, ignoreTransform);
|
|
};
|
|
|
|
/**
|
|
* @method _transformBounds
|
|
* @param {Rectangle} bounds
|
|
* @param {Matrix2D} matrix
|
|
* @param {Boolean} ignoreTransform
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._transformBounds = function(bounds, matrix, ignoreTransform) {
|
|
if (!bounds) { return bounds; }
|
|
var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height, mtx = this._props.matrix;
|
|
mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
|
|
|
|
if (x || y) { mtx.prependTransform(0,0,1,1,0,0,0,-x,-y); } // TODO: simplify this.
|
|
if (matrix) { mtx.appendMatrix(matrix); }
|
|
|
|
var x_a = width*mtx.a, x_b = width*mtx.b;
|
|
var y_c = height*mtx.c, y_d = height*mtx.d;
|
|
var tx = mtx.tx, ty = mtx.ty;
|
|
|
|
var minX = tx, maxX = tx, minY = ty, maxY = ty;
|
|
|
|
if ((x = x_a + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
|
|
if ((x = x_a + y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
|
|
if ((x = y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
|
|
|
|
if ((y = x_b + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
|
|
if ((y = x_b + y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
|
|
if ((y = y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
|
|
|
|
return bounds.setValues(minX, minY, maxX-minX, maxY-minY);
|
|
};
|
|
|
|
/**
|
|
* Indicates whether the display object has any mouse event listeners or a cursor.
|
|
* @method _isMouseOpaque
|
|
* @return {Boolean}
|
|
* @protected
|
|
**/
|
|
p._hasMouseEventListener = function() {
|
|
var evts = DisplayObject._MOUSE_EVENTS;
|
|
for (var i= 0, l=evts.length; i<l; i++) {
|
|
if (this.hasEventListener(evts[i])) { return true; }
|
|
}
|
|
return !!this.cursor;
|
|
};
|
|
|
|
createjs.DisplayObject = createjs.promote(DisplayObject, "EventDispatcher");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Container.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* A Container is a nestable display list that allows you to work with compound display elements. For example you could
|
|
* group arm, leg, torso and head {{#crossLink "Bitmap"}}{{/crossLink}} instances together into a Person Container, and
|
|
* transform them as a group, while still being able to move the individual parts relative to each other. Children of
|
|
* containers have their <code>transform</code> and <code>alpha</code> properties concatenated with their parent
|
|
* Container.
|
|
*
|
|
* For example, a {{#crossLink "Shape"}}{{/crossLink}} with x=100 and alpha=0.5, placed in a Container with <code>x=50</code>
|
|
* and <code>alpha=0.7</code> will be rendered to the canvas at <code>x=150</code> and <code>alpha=0.35</code>.
|
|
* Containers have some overhead, so you generally shouldn't create a Container to hold a single child.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var container = new createjs.Container();
|
|
* container.addChild(bitmapInstance, shapeInstance);
|
|
* container.x = 100;
|
|
*
|
|
* @class Container
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
**/
|
|
function Container() {
|
|
this.DisplayObject_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The array of children in the display list. You should usually use the child management methods such as
|
|
* {{#crossLink "Container/addChild"}}{{/crossLink}}, {{#crossLink "Container/removeChild"}}{{/crossLink}},
|
|
* {{#crossLink "Container/swapChildren"}}{{/crossLink}}, etc, rather than accessing this directly, but it is
|
|
* included for advanced uses.
|
|
* @property children
|
|
* @type Array
|
|
* @default null
|
|
**/
|
|
this.children = [];
|
|
|
|
/**
|
|
* Indicates whether the children of this container are independently enabled for mouse/pointer interaction.
|
|
* If false, the children will be aggregated under the container - for example, a click on a child shape would
|
|
* trigger a click event on the container.
|
|
* @property mouseChildren
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.mouseChildren = true;
|
|
|
|
/**
|
|
* If false, the tick will not be propagated to children of this Container. This can provide some performance benefits.
|
|
* In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
|
|
* on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling).
|
|
* @property tickChildren
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.tickChildren = true;
|
|
}
|
|
var p = createjs.extend(Container, createjs.DisplayObject);
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Constructor alias for backwards compatibility. This method will be removed in future versions.
|
|
* Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
|
|
* @method initialize
|
|
* @deprecated in favour of `createjs.promote()`
|
|
**/
|
|
p.initialize = Container; // TODO: deprecated.
|
|
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function() {
|
|
var hasContent = this.cacheCanvas || this.children.length;
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
**/
|
|
p.draw = function(ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
|
|
|
|
// this ensures we don't have issues with display list changes that occur during a draw:
|
|
var list = this.children.slice(0);
|
|
for (var i=0,l=list.length; i<l; i++) {
|
|
var child = list[i];
|
|
if (!child.isVisible()) { continue; }
|
|
|
|
// draw the child:
|
|
ctx.save();
|
|
child.updateContext(ctx);
|
|
child.draw(ctx);
|
|
ctx.restore();
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Adds a child to the top of the display list.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.addChild(bitmapInstance);
|
|
*
|
|
* You can also add multiple children at once:
|
|
*
|
|
* container.addChild(bitmapInstance, shapeInstance, textInstance);
|
|
*
|
|
* @method addChild
|
|
* @param {DisplayObject} child The display object to add.
|
|
* @return {DisplayObject} The child that was added, or the last child if multiple children were added.
|
|
**/
|
|
p.addChild = function(child) {
|
|
if (child == null) { return child; }
|
|
var l = arguments.length;
|
|
if (l > 1) {
|
|
for (var i=0; i<l; i++) { this.addChild(arguments[i]); }
|
|
return arguments[l-1];
|
|
}
|
|
if (child.parent) { child.parent.removeChild(child); }
|
|
child.parent = this;
|
|
this.children.push(child);
|
|
child.dispatchEvent("added");
|
|
return child;
|
|
};
|
|
|
|
/**
|
|
* Adds a child to the display list at the specified index, bumping children at equal or greater indexes up one, and
|
|
* setting its parent to this Container.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* addChildAt(child1, index);
|
|
*
|
|
* You can also add multiple children, such as:
|
|
*
|
|
* addChildAt(child1, child2, ..., index);
|
|
*
|
|
* The index must be between 0 and numChildren. For example, to add myShape under otherShape in the display list,
|
|
* you could use:
|
|
*
|
|
* container.addChildAt(myShape, container.getChildIndex(otherShape));
|
|
*
|
|
* This would also bump otherShape's index up by one. Fails silently if the index is out of range.
|
|
*
|
|
* @method addChildAt
|
|
* @param {DisplayObject} child The display object to add.
|
|
* @param {Number} index The index to add the child at.
|
|
* @return {DisplayObject} Returns the last child that was added, or the last child if multiple children were added.
|
|
**/
|
|
p.addChildAt = function(child, index) {
|
|
var l = arguments.length;
|
|
var indx = arguments[l-1]; // can't use the same name as the index param or it replaces arguments[1]
|
|
if (indx < 0 || indx > this.children.length) { return arguments[l-2]; }
|
|
if (l > 2) {
|
|
for (var i=0; i<l-1; i++) { this.addChildAt(arguments[i], indx+i); }
|
|
return arguments[l-2];
|
|
}
|
|
if (child.parent) { child.parent.removeChild(child); }
|
|
child.parent = this;
|
|
this.children.splice(index, 0, child);
|
|
child.dispatchEvent("added");
|
|
return child;
|
|
};
|
|
|
|
/**
|
|
* Removes the specified child from the display list. Note that it is faster to use removeChildAt() if the index is
|
|
* already known.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.removeChild(child);
|
|
*
|
|
* You can also remove multiple children:
|
|
*
|
|
* removeChild(child1, child2, ...);
|
|
*
|
|
* Returns true if the child (or children) was removed, or false if it was not in the display list.
|
|
* @method removeChild
|
|
* @param {DisplayObject} child The child to remove.
|
|
* @return {Boolean} true if the child (or children) was removed, or false if it was not in the display list.
|
|
**/
|
|
p.removeChild = function(child) {
|
|
var l = arguments.length;
|
|
if (l > 1) {
|
|
var good = true;
|
|
for (var i=0; i<l; i++) { good = good && this.removeChild(arguments[i]); }
|
|
return good;
|
|
}
|
|
return this.removeChildAt(createjs.indexOf(this.children, child));
|
|
};
|
|
|
|
/**
|
|
* Removes the child at the specified index from the display list, and sets its parent to null.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.removeChildAt(2);
|
|
*
|
|
* You can also remove multiple children:
|
|
*
|
|
* container.removeChild(2, 7, ...)
|
|
*
|
|
* Returns true if the child (or children) was removed, or false if any index was out of range.
|
|
* @method removeChildAt
|
|
* @param {Number} index The index of the child to remove.
|
|
* @return {Boolean} true if the child (or children) was removed, or false if any index was out of range.
|
|
**/
|
|
p.removeChildAt = function(index) {
|
|
var l = arguments.length;
|
|
if (l > 1) {
|
|
var a = [];
|
|
for (var i=0; i<l; i++) { a[i] = arguments[i]; }
|
|
a.sort(function(a, b) { return b-a; });
|
|
var good = true;
|
|
for (var i=0; i<l; i++) { good = good && this.removeChildAt(a[i]); }
|
|
return good;
|
|
}
|
|
if (index < 0 || index > this.children.length-1) { return false; }
|
|
var child = this.children[index];
|
|
if (child) { child.parent = null; }
|
|
this.children.splice(index, 1);
|
|
child.dispatchEvent("removed");
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Removes all children from the display list.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.removeAlLChildren();
|
|
*
|
|
* @method removeAllChildren
|
|
**/
|
|
p.removeAllChildren = function() {
|
|
var kids = this.children;
|
|
while (kids.length) { this.removeChildAt(0); }
|
|
};
|
|
|
|
/**
|
|
* Returns the child at the specified index.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* container.getChildAt(2);
|
|
*
|
|
* @method getChildAt
|
|
* @param {Number} index The index of the child to return.
|
|
* @return {DisplayObject} The child at the specified index. Returns null if there is no child at the index.
|
|
**/
|
|
p.getChildAt = function(index) {
|
|
return this.children[index];
|
|
};
|
|
|
|
/**
|
|
* Returns the child with the specified name.
|
|
* @method getChildByName
|
|
* @param {String} name The name of the child to return.
|
|
* @return {DisplayObject} The child with the specified name.
|
|
**/
|
|
p.getChildByName = function(name) {
|
|
var kids = this.children;
|
|
for (var i=0,l=kids.length;i<l;i++) {
|
|
if(kids[i].name == name) { return kids[i]; }
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Performs an array sort operation on the child list.
|
|
*
|
|
* <h4>Example: Display children with a higher y in front.</h4>
|
|
*
|
|
* var sortFunction = function(obj1, obj2, options) {
|
|
* if (obj1.y > obj2.y) { return 1; }
|
|
* if (obj1.y < obj2.y) { return -1; }
|
|
* return 0;
|
|
* }
|
|
* container.sortChildren(sortFunction);
|
|
*
|
|
* @method sortChildren
|
|
* @param {Function} sortFunction the function to use to sort the child list. See JavaScript's <code>Array.sort</code>
|
|
* documentation for details.
|
|
**/
|
|
p.sortChildren = function(sortFunction) {
|
|
this.children.sort(sortFunction);
|
|
};
|
|
|
|
/**
|
|
* Returns the index of the specified child in the display list, or -1 if it is not in the display list.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var index = container.getChildIndex(child);
|
|
*
|
|
* @method getChildIndex
|
|
* @param {DisplayObject} child The child to return the index of.
|
|
* @return {Number} The index of the specified child. -1 if the child is not found.
|
|
**/
|
|
p.getChildIndex = function(child) {
|
|
return createjs.indexOf(this.children, child);
|
|
};
|
|
|
|
/**
|
|
* Returns the number of children in the display list.
|
|
* @method getNumChildren
|
|
* @return {Number} The number of children in the display list.
|
|
**/
|
|
p.getNumChildren = function() {
|
|
return this.children.length;
|
|
};
|
|
|
|
/**
|
|
* Swaps the children at the specified indexes. Fails silently if either index is out of range.
|
|
* @method swapChildrenAt
|
|
* @param {Number} index1
|
|
* @param {Number} index2
|
|
**/
|
|
p.swapChildrenAt = function(index1, index2) {
|
|
var kids = this.children;
|
|
var o1 = kids[index1];
|
|
var o2 = kids[index2];
|
|
if (!o1 || !o2) { return; }
|
|
kids[index1] = o2;
|
|
kids[index2] = o1;
|
|
};
|
|
|
|
/**
|
|
* Swaps the specified children's depth in the display list. Fails silently if either child is not a child of this
|
|
* Container.
|
|
* @method swapChildren
|
|
* @param {DisplayObject} child1
|
|
* @param {DisplayObject} child2
|
|
**/
|
|
p.swapChildren = function(child1, child2) {
|
|
var kids = this.children;
|
|
var index1,index2;
|
|
for (var i=0,l=kids.length;i<l;i++) {
|
|
if (kids[i] == child1) { index1 = i; }
|
|
if (kids[i] == child2) { index2 = i; }
|
|
if (index1 != null && index2 != null) { break; }
|
|
}
|
|
if (i==l) { return; } // TODO: throw error?
|
|
kids[index1] = child2;
|
|
kids[index2] = child1;
|
|
};
|
|
|
|
/**
|
|
* Changes the depth of the specified child. Fails silently if the child is not a child of this container, or the index is out of range.
|
|
* @param {DisplayObject} child
|
|
* @param {Number} index
|
|
* @method setChildIndex
|
|
**/
|
|
p.setChildIndex = function(child, index) {
|
|
var kids = this.children, l=kids.length;
|
|
if (child.parent != this || index < 0 || index >= l) { return; }
|
|
for (var i=0;i<l;i++) {
|
|
if (kids[i] == child) { break; }
|
|
}
|
|
if (i==l || i == index) { return; }
|
|
kids.splice(i,1);
|
|
kids.splice(index,0,child);
|
|
};
|
|
|
|
/**
|
|
* Returns true if the specified display object either is this container or is a descendent (child, grandchild, etc)
|
|
* of this container.
|
|
* @method contains
|
|
* @param {DisplayObject} child The DisplayObject to be checked.
|
|
* @return {Boolean} true if the specified display object either is this container or is a descendent.
|
|
**/
|
|
p.contains = function(child) {
|
|
while (child) {
|
|
if (child == this) { return true; }
|
|
child = child.parent;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Tests whether the display object intersects the specified local point (ie. draws a pixel with alpha > 0 at the
|
|
* specified position). This ignores the alpha, shadow and compositeOperation of the display object, and all
|
|
* transform properties including regX/Y.
|
|
* @method hitTest
|
|
* @param {Number} x The x position to check in the display object's local coordinates.
|
|
* @param {Number} y The y position to check in the display object's local coordinates.
|
|
* @return {Boolean} A Boolean indicating whether there is a visible section of a DisplayObject that overlaps the specified
|
|
* coordinates.
|
|
**/
|
|
p.hitTest = function(x, y) {
|
|
// TODO: optimize to use the fast cache check where possible.
|
|
return (this.getObjectUnderPoint(x, y) != null);
|
|
};
|
|
|
|
/**
|
|
* Returns an array of all display objects under the specified coordinates that are in this container's display
|
|
* list. This routine ignores any display objects with mouseEnabled set to false. The array will be sorted in order
|
|
* of visual depth, with the top-most display object at index 0. This uses shape based hit detection, and can be an
|
|
* expensive operation to run, so it is best to use it carefully. For example, if testing for objects under the
|
|
* mouse, test on tick (instead of on mousemove), and only if the mouse's position has changed.
|
|
*
|
|
* By default this method evaluates all display objects. By setting the `mode` parameter to `1`, the `mouseEnabled`
|
|
* and `mouseChildren` properties will be respected.
|
|
* Setting it to `2` additionally excludes display objects that do not have active mouse event listeners
|
|
* or a `cursor` property. That is, only objects that would normally intercept mouse interaction will be included.
|
|
* This can significantly improve performance in some cases by reducing the number of
|
|
* display objects that need to be tested.
|
|
*
|
|
* Accounts for both {{#crossLink "DisplayObject/hitArea:property"}}{{/crossLink}} and {{#crossLink "DisplayObject/mask:property"}}{{/crossLink}}.
|
|
* @method getObjectsUnderPoint
|
|
* @param {Number} x The x position in the container to test.
|
|
* @param {Number} y The y position in the container to test.
|
|
* @param {Number} mode The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
|
|
* @return {Array} An Array of DisplayObjects under the specified coordinates.
|
|
**/
|
|
p.getObjectsUnderPoint = function(x, y, mode) {
|
|
var arr = [];
|
|
var pt = this.localToGlobal(x, y);
|
|
this._getObjectsUnderPoint(pt.x, pt.y, arr, mode>0, mode==1);
|
|
return arr;
|
|
};
|
|
|
|
/**
|
|
* Similar to {{#crossLink "Container/getObjectsUnderPoint()"}}{{/crossLink}}, but returns only the top-most display
|
|
* object. This runs significantly faster than <code>getObjectsUnderPoint()</code>, but is still potentially an expensive
|
|
* operation. See {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} for more information.
|
|
* @method getObjectUnderPoint
|
|
* @param {Number} x The x position in the container to test.
|
|
* @param {Number} y The y position in the container to test.
|
|
* @param {Number} mode The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
|
|
* @return {DisplayObject} The top-most display object under the specified coordinates.
|
|
**/
|
|
p.getObjectUnderPoint = function(x, y, mode) {
|
|
var pt = this.localToGlobal(x, y);
|
|
return this._getObjectsUnderPoint(pt.x, pt.y, null, mode>0, mode==1);
|
|
};
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
*/
|
|
p.getBounds = function() {
|
|
return this._getBounds(null, true);
|
|
};
|
|
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
*/
|
|
p.getTransformedBounds = function() {
|
|
return this._getBounds();
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Container. Some properties that are specific to this instance's current context are
|
|
* reverted to their defaults (for example .parent).
|
|
* @method clone
|
|
* @param {Boolean} recursive If true, all of the descendants of this container will be cloned recursively. If false, the
|
|
* properties of the container will be cloned, but the new instance will not have any children.
|
|
* @return {Container} A clone of the current Container instance.
|
|
**/
|
|
p.clone = function(recursive) {
|
|
var o = this._cloneProps(new Container());
|
|
if (recursive) { this._cloneChildren(o); }
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Container (name="+ this.name +")]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _tick
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* @protected
|
|
**/
|
|
p._tick = function(evtObj) {
|
|
if (this.tickChildren) {
|
|
for (var i=this.children.length-1; i>=0; i--) {
|
|
var child = this.children[i];
|
|
if (child.tickEnabled && child._tick) { child._tick(evtObj); }
|
|
}
|
|
}
|
|
this.DisplayObject__tick(evtObj);
|
|
};
|
|
|
|
/**
|
|
* Recursively clones all children of this container, and adds them to the target container.
|
|
* @method cloneChildren
|
|
* @protected
|
|
* @param {Container} o The target container.
|
|
**/
|
|
p._cloneChildren = function(o) {
|
|
if (o.children.length) { o.removeAllChildren(); }
|
|
var arr = o.children;
|
|
for (var i=0, l=this.children.length; i<l; i++) {
|
|
var clone = this.children[i].clone(true);
|
|
clone.parent = o;
|
|
arr.push(clone);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _getObjectsUnderPoint
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @param {Array} arr
|
|
* @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and active listeners.
|
|
* @param {Boolean} activeListener If true, there is an active mouse event listener on a parent object.
|
|
* @param {Number} currentDepth Indicates the current depth of the search.
|
|
* @return {DisplayObject}
|
|
* @protected
|
|
**/
|
|
p._getObjectsUnderPoint = function(x, y, arr, mouse, activeListener, currentDepth) {
|
|
currentDepth = currentDepth || 0;
|
|
if (!currentDepth && !this._testMask(this, x, y)) { return null; }
|
|
var mtx, ctx = createjs.DisplayObject._hitTestContext;
|
|
activeListener = activeListener || (mouse&&this._hasMouseEventListener());
|
|
|
|
// draw children one at a time, and check if we get a hit:
|
|
var children = this.children, l = children.length;
|
|
for (var i=l-1; i>=0; i--) {
|
|
var child = children[i];
|
|
var hitArea = child.hitArea;
|
|
if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; }
|
|
if (!hitArea && !this._testMask(child, x, y)) { continue; }
|
|
|
|
// if a child container has a hitArea then we only need to check its hitArea, so we can treat it as a normal DO:
|
|
if (!hitArea && child instanceof Container) {
|
|
var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener, currentDepth+1);
|
|
if (!arr && result) { return (mouse && !this.mouseChildren) ? this : result; }
|
|
} else {
|
|
if (mouse && !activeListener && !child._hasMouseEventListener()) { continue; }
|
|
|
|
// TODO: can we pass displayProps forward, to avoid having to calculate this backwards every time? It's kind of a mixed bag. When we're only hunting for DOs with event listeners, it may not make sense.
|
|
var props = child.getConcatenatedDisplayProps(child._props);
|
|
mtx = props.matrix;
|
|
|
|
if (hitArea) {
|
|
mtx.prependMatrix(hitArea.getMatrix(hitArea._props.matrix));
|
|
props.alpha = hitArea.alpha;
|
|
}
|
|
|
|
ctx.globalAlpha = props.alpha;
|
|
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
|
|
(hitArea||child).draw(ctx);
|
|
if (!this._testHit(ctx)) { continue; }
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, 2, 2);
|
|
if (arr) { arr.push(child); }
|
|
else { return (mouse && !this.mouseChildren) ? this : child; }
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* @method _testMask
|
|
* @param {DisplayObject} target
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @return {Boolean} Indicates whether the x/y is within the masked region.
|
|
* @protected
|
|
**/
|
|
p._testMask = function(target, x, y) {
|
|
var mask = target.mask;
|
|
if (!mask || !mask.graphics || mask.graphics.isEmpty()) { return true; }
|
|
|
|
var mtx = this._props.matrix, parent = target.parent;
|
|
mtx = parent ? parent.getConcatenatedMatrix(mtx) : mtx.identity();
|
|
mtx = mask.getMatrix(mask._props.matrix).appendMatrix(mtx);
|
|
|
|
var ctx = createjs.DisplayObject._hitTestContext;
|
|
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
|
|
|
|
// draw the mask as a solid fill:
|
|
mask.graphics.drawAsPath(ctx);
|
|
ctx.fillStyle = "#000";
|
|
ctx.fill();
|
|
|
|
if (!this._testHit(ctx)) { return false; }
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, 2, 2);
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* @method _getBounds
|
|
* @param {Matrix2D} matrix
|
|
* @param {Boolean} ignoreTransform If true, does not apply this object's transform.
|
|
* @return {Rectangle}
|
|
* @protected
|
|
**/
|
|
p._getBounds = function(matrix, ignoreTransform) {
|
|
var bounds = this.DisplayObject_getBounds();
|
|
if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); }
|
|
|
|
var mtx = this._props.matrix;
|
|
mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
|
|
if (matrix) { mtx.appendMatrix(matrix); }
|
|
|
|
var l = this.children.length, rect=null;
|
|
for (var i=0; i<l; i++) {
|
|
var child = this.children[i];
|
|
if (!child.visible || !(bounds = child._getBounds(mtx))) { continue; }
|
|
if (rect) { rect.extend(bounds.x, bounds.y, bounds.width, bounds.height); }
|
|
else { rect = bounds.clone(); }
|
|
}
|
|
return rect;
|
|
};
|
|
|
|
|
|
createjs.Container = createjs.promote(Container, "DisplayObject");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Stage.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* A stage is the root level {{#crossLink "Container"}}{{/crossLink}} for a display list. Each time its {{#crossLink "Stage/tick"}}{{/crossLink}}
|
|
* method is called, it will render its display list to its target canvas.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example creates a stage, adds a child to it, then uses {{#crossLink "Ticker"}}{{/crossLink}} to update the child
|
|
* and redraw the stage using {{#crossLink "Stage/update"}}{{/crossLink}}.
|
|
*
|
|
* var stage = new createjs.Stage("canvasElementId");
|
|
* var image = new createjs.Bitmap("imagePath.png");
|
|
* stage.addChild(image);
|
|
* createjs.Ticker.addEventListener("tick", handleTick);
|
|
* function handleTick(event) {
|
|
* image.x += 10;
|
|
* stage.update();
|
|
* }
|
|
*
|
|
* @class Stage
|
|
* @extends Container
|
|
* @constructor
|
|
* @param {HTMLCanvasElement | String | Object} canvas A canvas object that the Stage will render to, or the string id
|
|
* of a canvas object in the current document.
|
|
**/
|
|
function Stage(canvas) {
|
|
this.Container_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* Indicates whether the stage should automatically clear the canvas before each render. You can set this to <code>false</code>
|
|
* to manually control clearing (for generative art, or when pointing multiple stages at the same canvas for
|
|
* example).
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var stage = new createjs.Stage("canvasId");
|
|
* stage.autoClear = false;
|
|
*
|
|
* @property autoClear
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.autoClear = true;
|
|
|
|
/**
|
|
* The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the
|
|
* first stage that will be ticked (or they will clear each other's render).
|
|
*
|
|
* When changing the canvas property you must disable the events on the old canvas, and enable events on the
|
|
* new canvas or mouse events will not work as expected. For example:
|
|
*
|
|
* myStage.enableDOMEvents(false);
|
|
* myStage.canvas = anotherCanvas;
|
|
* myStage.enableDOMEvents(true);
|
|
*
|
|
* @property canvas
|
|
* @type HTMLCanvasElement | Object
|
|
**/
|
|
this.canvas = (typeof canvas == "string") ? document.getElementById(canvas) : canvas;
|
|
|
|
/**
|
|
* The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
|
|
* position over the canvas, and mouseInBounds will be set to false.
|
|
* @property mouseX
|
|
* @type Number
|
|
* @readonly
|
|
**/
|
|
this.mouseX = 0;
|
|
|
|
/**
|
|
* The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
|
|
* position over the canvas, and mouseInBounds will be set to false.
|
|
* @property mouseY
|
|
* @type Number
|
|
* @readonly
|
|
**/
|
|
this.mouseY = 0;
|
|
|
|
/**
|
|
* Specifies the area of the stage to affect when calling update. This can be use to selectively
|
|
* re-draw specific regions of the canvas. If null, the whole canvas area is drawn.
|
|
* @property drawRect
|
|
* @type {Rectangle}
|
|
*/
|
|
this.drawRect = null;
|
|
|
|
/**
|
|
* Indicates whether display objects should be rendered on whole pixels. You can set the
|
|
* {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of
|
|
* display objects to false to enable/disable this behaviour on a per instance basis.
|
|
* @property snapToPixelEnabled
|
|
* @type Boolean
|
|
* @default false
|
|
**/
|
|
this.snapToPixelEnabled = false;
|
|
|
|
/**
|
|
* Indicates whether the mouse is currently within the bounds of the canvas.
|
|
* @property mouseInBounds
|
|
* @type Boolean
|
|
* @default false
|
|
**/
|
|
this.mouseInBounds = false;
|
|
|
|
/**
|
|
* If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas.
|
|
* @property tickOnUpdate
|
|
* @type Boolean
|
|
* @default true
|
|
**/
|
|
this.tickOnUpdate = true;
|
|
|
|
/**
|
|
* If true, mouse move events will continue to be called when the mouse leaves the target canvas. See
|
|
* {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}}
|
|
* x/y/rawX/rawY.
|
|
* @property mouseMoveOutside
|
|
* @type Boolean
|
|
* @default false
|
|
**/
|
|
this.mouseMoveOutside = false;
|
|
|
|
/**
|
|
* The hitArea property is not supported for Stage.
|
|
* @property hitArea
|
|
* @type {DisplayObject}
|
|
* @default null
|
|
*/
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* Holds objects with data for each active pointer id. Each object has the following properties:
|
|
* x, y, event, target, overTarget, overX, overY, inBounds, posEvtObj (native event that last updated position)
|
|
* @property _pointerData
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._pointerData = {};
|
|
|
|
/**
|
|
* Number of active pointers.
|
|
* @property _pointerCount
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._pointerCount = 0;
|
|
|
|
/**
|
|
* The ID of the primary pointer.
|
|
* @property _primaryPointerID
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this._primaryPointerID = null;
|
|
|
|
/**
|
|
* @property _mouseOverIntervalID
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._mouseOverIntervalID = null;
|
|
|
|
/**
|
|
* @property _nextStage
|
|
* @protected
|
|
* @type Stage
|
|
**/
|
|
this._nextStage = null;
|
|
|
|
/**
|
|
* @property _prevStage
|
|
* @protected
|
|
* @type Stage
|
|
**/
|
|
this._prevStage = null;
|
|
|
|
|
|
// initialize:
|
|
this.enableDOMEvents(true);
|
|
}
|
|
var p = createjs.extend(Stage, createjs.Container);
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when the user moves the mouse over the canvas.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event stagemousemove
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user presses their left mouse button on the canvas. See the {{#crossLink "MouseEvent"}}{{/crossLink}}
|
|
* class for a listing of event properties.
|
|
* @event stagemousedown
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the user the user releases the mouse button anywhere that the page can detect it (this varies slightly between browsers).
|
|
* You can use {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}} to check whether the mouse is currently within the stage bounds.
|
|
* See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
|
|
* @event stagemouseup
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the mouse moves from within the canvas area (mouseInBounds == true) to outside it (mouseInBounds == false).
|
|
* This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
|
|
* class for a listing of event properties.
|
|
* @event mouseleave
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when the mouse moves into the canvas area (mouseInBounds == false) from outside it (mouseInBounds == true).
|
|
* This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
|
|
* class for a listing of event properties.
|
|
* @event mouseenter
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched each update immediately before the tick event is propagated through the display list.
|
|
* You can call preventDefault on the event object to cancel propagating the tick event.
|
|
* @event tickstart
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if
|
|
* tickOnUpdate is false. Precedes the "drawstart" event.
|
|
* @event tickend
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched each update immediately before the canvas is cleared and the display list is drawn to it.
|
|
* You can call preventDefault on the event object to cancel the draw.
|
|
* @event drawstart
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored.
|
|
* @event drawend
|
|
* @since 0.7.0
|
|
*/
|
|
|
|
|
|
// getter / setters:
|
|
/**
|
|
* Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them.
|
|
* This can be useful in cases where you have multiple layered canvases and want user interactions
|
|
* events to pass through. For example, this would relay mouse events from topStage to bottomStage:
|
|
*
|
|
* topStage.nextStage = bottomStage;
|
|
*
|
|
* To disable relaying, set nextStage to null.
|
|
*
|
|
* MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings
|
|
* of the top-most stage, but are only processed if the target stage has mouse over interactions enabled.
|
|
* Considerations when using roll over in relay targets:<OL>
|
|
* <LI> The top-most (first) stage must have mouse over interactions enabled (via enableMouseOver)</LI>
|
|
* <LI> All stages that wish to participate in mouse over interaction must enable them via enableMouseOver</LI>
|
|
* <LI> All relay targets will share the frequency value of the top-most stage</LI>
|
|
* </OL>
|
|
* To illustrate, in this example the targetStage would process mouse over interactions at 10hz (despite passing
|
|
* 30 as it's desired frequency):
|
|
* topStage.nextStage = targetStage;
|
|
* topStage.enableMouseOver(10);
|
|
* targetStage.enableMouseOver(30);
|
|
*
|
|
* If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its
|
|
* DOM events using:
|
|
*
|
|
* targetStage.enableDOMEvents(false);
|
|
*
|
|
* @property nextStage
|
|
* @type {Stage}
|
|
**/
|
|
p._get_nextStage = function() {
|
|
return this._nextStage;
|
|
};
|
|
p._set_nextStage = function(value) {
|
|
if (this._nextStage) { this._nextStage._prevStage = null; }
|
|
if (value) { value._prevStage = this; }
|
|
this._nextStage = value;
|
|
};
|
|
|
|
try {
|
|
Object.defineProperties(p, {
|
|
nextStage: { get: p._get_nextStage, set: p._set_nextStage }
|
|
});
|
|
} catch (e) {} // TODO: use Log
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Each time the update method is called, the stage will call {{#crossLink "Stage/tick"}}{{/crossLink}}
|
|
* unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false,
|
|
* and then render the display list to the canvas.
|
|
*
|
|
* @method update
|
|
* @param {Object} [props] Props object to pass to `tick()`. Should usually be a {{#crossLink "Ticker"}}{{/crossLink}} event object, or similar object with a delta property.
|
|
**/
|
|
p.update = function(props) {
|
|
if (!this.canvas) { return; }
|
|
if (this.tickOnUpdate) { this.tick(props); }
|
|
if (this.dispatchEvent("drawstart")) { return; }
|
|
createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled;
|
|
var r = this.drawRect, ctx = this.canvas.getContext("2d");
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
if (this.autoClear) {
|
|
if (r) { ctx.clearRect(r.x, r.y, r.width, r.height); }
|
|
else { ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); }
|
|
}
|
|
ctx.save();
|
|
if (this.drawRect) {
|
|
ctx.beginPath();
|
|
ctx.rect(r.x, r.y, r.width, r.height);
|
|
ctx.clip();
|
|
}
|
|
this.updateContext(ctx);
|
|
this.draw(ctx, false);
|
|
ctx.restore();
|
|
this.dispatchEvent("drawend");
|
|
};
|
|
|
|
/**
|
|
* Propagates a tick event through the display list. This is automatically called by {{#crossLink "Stage/update"}}{{/crossLink}}
|
|
* unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false.
|
|
*
|
|
* If a props object is passed to `tick()`, then all of its properties will be copied to the event object that is
|
|
* propagated to listeners.
|
|
*
|
|
* Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that
|
|
* a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or equivalent object with a delta property) be
|
|
* passed as the `props` parameter to `tick()`. For example:
|
|
*
|
|
* Ticker.on("tick", handleTick);
|
|
* function handleTick(evtObj) {
|
|
* // clone the event object from Ticker, and add some custom data to it:
|
|
* var evt = evtObj.clone().set({greeting:"hello", name:"world"});
|
|
*
|
|
* // pass it to stage.update():
|
|
* myStage.update(evt); // subsequently calls tick() with the same param
|
|
* }
|
|
*
|
|
* // ...
|
|
* myDisplayObject.on("tick", handleDisplayObjectTick);
|
|
* function handleDisplayObjectTick(evt) {
|
|
* console.log(evt.delta); // the delta property from the Ticker tick event object
|
|
* console.log(evt.greeting, evt.name); // custom data: "hello world"
|
|
* }
|
|
*
|
|
* @method tick
|
|
* @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property.
|
|
**/
|
|
p.tick = function(props) {
|
|
if (!this.tickEnabled || this.dispatchEvent("tickstart")) { return; }
|
|
var evtObj = new createjs.Event("tick");
|
|
if (props) {
|
|
for (var n in props) {
|
|
if (props.hasOwnProperty(n)) { evtObj[n] = props[n]; }
|
|
}
|
|
}
|
|
this._tick(evtObj);
|
|
this.dispatchEvent("tickend");
|
|
};
|
|
|
|
/**
|
|
* Default event handler that calls the Stage {{#crossLink "Stage/update"}}{{/crossLink}} method when a {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}}
|
|
* event is received. This allows you to register a Stage instance as a event listener on {{#crossLink "Ticker"}}{{/crossLink}}
|
|
* directly, using:
|
|
*
|
|
* Ticker.addEventListener("tick", myStage");
|
|
*
|
|
* Note that if you subscribe to ticks using this pattern, then the tick event object will be passed through to
|
|
* display object tick handlers, instead of <code>delta</code> and <code>paused</code> parameters.
|
|
* @property handleEvent
|
|
* @type Function
|
|
**/
|
|
p.handleEvent = function(evt) {
|
|
if (evt.type == "tick") { this.update(evt); }
|
|
};
|
|
|
|
/**
|
|
* Clears the target canvas. Useful if {{#crossLink "Stage/autoClear:property"}}{{/crossLink}} is set to `false`.
|
|
* @method clear
|
|
**/
|
|
p.clear = function() {
|
|
if (!this.canvas) { return; }
|
|
var ctx = this.canvas.getContext("2d");
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1);
|
|
};
|
|
|
|
/**
|
|
* Returns a data url that contains a Base64-encoded image of the contents of the stage. The returned data url can
|
|
* be specified as the src value of an image element.
|
|
* @method toDataURL
|
|
* @param {String} [backgroundColor] The background color to be used for the generated image. Any valid CSS color
|
|
* value is allowed. The default value is a transparent background.
|
|
* @param {String} [mimeType="image/png"] The MIME type of the image format to be create. The default is "image/png". If an unknown MIME type
|
|
* is passed in, or if the browser does not support the specified MIME type, the default value will be used.
|
|
* @return {String} a Base64 encoded image.
|
|
**/
|
|
p.toDataURL = function(backgroundColor, mimeType) {
|
|
var data, ctx = this.canvas.getContext('2d'), w = this.canvas.width, h = this.canvas.height;
|
|
|
|
if (backgroundColor) {
|
|
data = ctx.getImageData(0, 0, w, h);
|
|
var compositeOperation = ctx.globalCompositeOperation;
|
|
ctx.globalCompositeOperation = "destination-over";
|
|
|
|
ctx.fillStyle = backgroundColor;
|
|
ctx.fillRect(0, 0, w, h);
|
|
}
|
|
|
|
var dataURL = this.canvas.toDataURL(mimeType||"image/png");
|
|
|
|
if(backgroundColor) {
|
|
ctx.putImageData(data, 0, 0);
|
|
ctx.globalCompositeOperation = compositeOperation;
|
|
}
|
|
|
|
return dataURL;
|
|
};
|
|
|
|
/**
|
|
* Enables or disables (by passing a frequency of 0) mouse over ({{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}
|
|
* and {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}) and roll over events ({{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}
|
|
* and {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}) for this stage's display list. These events can
|
|
* be expensive to generate, so they are disabled by default. The frequency of the events can be controlled
|
|
* independently of mouse move events via the optional `frequency` parameter.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var stage = new createjs.Stage("canvasId");
|
|
* stage.enableMouseOver(10); // 10 updates per second
|
|
*
|
|
* @method enableMouseOver
|
|
* @param {Number} [frequency=20] Optional param specifying the maximum number of times per second to broadcast
|
|
* mouse over/out events. Set to 0 to disable mouse over events completely. Maximum is 50. A lower frequency is less
|
|
* responsive, but uses less CPU.
|
|
**/
|
|
p.enableMouseOver = function(frequency) {
|
|
if (this._mouseOverIntervalID) {
|
|
clearInterval(this._mouseOverIntervalID);
|
|
this._mouseOverIntervalID = null;
|
|
if (frequency == 0) {
|
|
this._testMouseOver(true);
|
|
}
|
|
}
|
|
if (frequency == null) { frequency = 20; }
|
|
else if (frequency <= 0) { return; }
|
|
var o = this;
|
|
this._mouseOverIntervalID = setInterval(function(){ o._testMouseOver(); }, 1000/Math.min(50,frequency));
|
|
};
|
|
|
|
/**
|
|
* Enables or disables the event listeners that stage adds to DOM elements (window, document and canvas). It is good
|
|
* practice to disable events when disposing of a Stage instance, otherwise the stage will continue to receive
|
|
* events from the page.
|
|
*
|
|
* When changing the canvas property you must disable the events on the old canvas, and enable events on the
|
|
* new canvas or mouse events will not work as expected. For example:
|
|
*
|
|
* myStage.enableDOMEvents(false);
|
|
* myStage.canvas = anotherCanvas;
|
|
* myStage.enableDOMEvents(true);
|
|
*
|
|
* @method enableDOMEvents
|
|
* @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true.
|
|
**/
|
|
p.enableDOMEvents = function(enable) {
|
|
if (enable == null) { enable = true; }
|
|
var n, o, ls = this._eventListeners;
|
|
if (!enable && ls) {
|
|
for (n in ls) {
|
|
o = ls[n];
|
|
o.t.removeEventListener(n, o.f, false);
|
|
}
|
|
this._eventListeners = null;
|
|
} else if (enable && !ls && this.canvas) {
|
|
var t = window.addEventListener ? window : document;
|
|
var _this = this;
|
|
ls = this._eventListeners = {};
|
|
ls["mouseup"] = {t:t, f:function(e) { _this._handleMouseUp(e)} };
|
|
ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} };
|
|
ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} };
|
|
ls["mousedown"] = {t:this.canvas, f:function(e) { _this._handleMouseDown(e)} };
|
|
|
|
for (n in ls) {
|
|
o = ls[n];
|
|
o.t.addEventListener(n, o.f, false);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Stage instances cannot be cloned.
|
|
* @method clone
|
|
**/
|
|
p.clone = function() {
|
|
throw("Stage cannot be cloned.");
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Stage (name="+ this.name +")]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _getElementRect
|
|
* @protected
|
|
* @param {HTMLElement} e
|
|
**/
|
|
p._getElementRect = function(e) {
|
|
var bounds;
|
|
try { bounds = e.getBoundingClientRect(); } // this can fail on disconnected DOM elements in IE9
|
|
catch (err) { bounds = {top: e.offsetTop, left: e.offsetLeft, width:e.offsetWidth, height:e.offsetHeight}; }
|
|
|
|
var offX = (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || document.body.clientLeft || 0);
|
|
var offY = (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || document.body.clientTop || 0);
|
|
|
|
var styles = window.getComputedStyle ? getComputedStyle(e,null) : e.currentStyle; // IE <9 compatibility.
|
|
var padL = parseInt(styles.paddingLeft)+parseInt(styles.borderLeftWidth);
|
|
var padT = parseInt(styles.paddingTop)+parseInt(styles.borderTopWidth);
|
|
var padR = parseInt(styles.paddingRight)+parseInt(styles.borderRightWidth);
|
|
var padB = parseInt(styles.paddingBottom)+parseInt(styles.borderBottomWidth);
|
|
|
|
// note: in some browsers bounds properties are read only.
|
|
return {
|
|
left: bounds.left+offX+padL,
|
|
right: bounds.right+offX-padR,
|
|
top: bounds.top+offY+padT,
|
|
bottom: bounds.bottom+offY-padB
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _getPointerData
|
|
* @protected
|
|
* @param {Number} id
|
|
**/
|
|
p._getPointerData = function(id) {
|
|
var data = this._pointerData[id];
|
|
if (!data) {
|
|
data = this._pointerData[id] = {x:0,y:0};
|
|
// if it's the first new touch, then make it the primary pointer id:
|
|
if (this._primaryPointerID == null) { this._primaryPointerID = id; }
|
|
// if it's the mouse (id == -1) or the first new touch, then make it the primary pointer id:
|
|
if (this._primaryPointerID == null || this._primaryPointerID == -1) { this._primaryPointerID = id; }
|
|
}
|
|
return data;
|
|
};
|
|
|
|
/**
|
|
* @method _handleMouseMove
|
|
* @protected
|
|
* @param {MouseEvent} e
|
|
**/
|
|
p._handleMouseMove = function(e) {
|
|
if(!e){ e = window.event; }
|
|
this._handlePointerMove(-1, e, e.pageX, e.pageY);
|
|
};
|
|
|
|
/**
|
|
* @method _handlePointerMove
|
|
* @protected
|
|
* @param {Number} id
|
|
* @param {Event} e
|
|
* @param {Number} pageX
|
|
* @param {Number} pageY
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
**/
|
|
p._handlePointerMove = function(id, e, pageX, pageY, owner) {
|
|
if (this._prevStage && owner === undefined) { return; } // redundant listener.
|
|
if (!this.canvas) { return; }
|
|
var nextStage=this._nextStage, o=this._getPointerData(id);
|
|
|
|
var inBounds = o.inBounds;
|
|
this._updatePointerPosition(id, e, pageX, pageY);
|
|
if (inBounds || o.inBounds || this.mouseMoveOutside) {
|
|
if (id == -1 && o.inBounds == !inBounds) {
|
|
this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e);
|
|
}
|
|
|
|
this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e);
|
|
this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e);
|
|
}
|
|
|
|
nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null);
|
|
};
|
|
|
|
/**
|
|
* @method _updatePointerPosition
|
|
* @protected
|
|
* @param {Number} id
|
|
* @param {Event} e
|
|
* @param {Number} pageX
|
|
* @param {Number} pageY
|
|
**/
|
|
p._updatePointerPosition = function(id, e, pageX, pageY) {
|
|
var rect = this._getElementRect(this.canvas);
|
|
pageX -= rect.left;
|
|
pageY -= rect.top;
|
|
|
|
var w = this.canvas.width;
|
|
var h = this.canvas.height;
|
|
pageX /= (rect.right-rect.left)/w;
|
|
pageY /= (rect.bottom-rect.top)/h;
|
|
var o = this._getPointerData(id);
|
|
if (o.inBounds = (pageX >= 0 && pageY >= 0 && pageX <= w-1 && pageY <= h-1)) {
|
|
o.x = pageX;
|
|
o.y = pageY;
|
|
} else if (this.mouseMoveOutside) {
|
|
o.x = pageX < 0 ? 0 : (pageX > w-1 ? w-1 : pageX);
|
|
o.y = pageY < 0 ? 0 : (pageY > h-1 ? h-1 : pageY);
|
|
}
|
|
|
|
o.posEvtObj = e;
|
|
o.rawX = pageX;
|
|
o.rawY = pageY;
|
|
|
|
if (id == this._primaryPointerID) {
|
|
this.mouseX = o.x;
|
|
this.mouseY = o.y;
|
|
this.mouseInBounds = o.inBounds;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _handleMouseUp
|
|
* @protected
|
|
* @param {MouseEvent} e
|
|
**/
|
|
p._handleMouseUp = function(e) {
|
|
this._handlePointerUp(-1, e, false);
|
|
};
|
|
|
|
/**
|
|
* @method _handlePointerUp
|
|
* @protected
|
|
* @param {Number} id
|
|
* @param {Event} e
|
|
* @param {Boolean} clear
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
**/
|
|
p._handlePointerUp = function(id, e, clear, owner) {
|
|
var nextStage = this._nextStage, o = this._getPointerData(id);
|
|
if (this._prevStage && owner === undefined) { return; } // redundant listener.
|
|
|
|
this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e);
|
|
|
|
var target=null, oTarget = o.target;
|
|
if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); }
|
|
if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); }
|
|
this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e);
|
|
|
|
if (clear) {
|
|
if (id==this._primaryPointerID) { this._primaryPointerID = null; }
|
|
delete(this._pointerData[id]);
|
|
} else { o.target = null; }
|
|
|
|
nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this);
|
|
};
|
|
|
|
/**
|
|
* @method _handleMouseDown
|
|
* @protected
|
|
* @param {MouseEvent} e
|
|
**/
|
|
p._handleMouseDown = function(e) {
|
|
this._handlePointerDown(-1, e, e.pageX, e.pageY);
|
|
};
|
|
|
|
/**
|
|
* @method _handlePointerDown
|
|
* @protected
|
|
* @param {Number} id
|
|
* @param {Event} e
|
|
* @param {Number} pageX
|
|
* @param {Number} pageY
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
**/
|
|
p._handlePointerDown = function(id, e, pageX, pageY, owner) {
|
|
if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); }
|
|
var target = null, nextStage = this._nextStage, o = this._getPointerData(id);
|
|
|
|
if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e); }
|
|
|
|
|
|
if (!owner) {
|
|
target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true);
|
|
this._dispatchMouseEvent(o.target, "mousedown", true, id, o, e);
|
|
}
|
|
|
|
nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this);
|
|
};
|
|
|
|
/**
|
|
* @method _testMouseOver
|
|
* @param {Boolean} clear If true, clears the mouseover / rollover (ie. no target)
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
* @param {Stage} eventTarget The stage that the cursor is actively over.
|
|
* @protected
|
|
**/
|
|
p._testMouseOver = function(clear, owner, eventTarget) {
|
|
if (this._prevStage && owner === undefined) { return; } // redundant listener.
|
|
|
|
var nextStage = this._nextStage;
|
|
if (!this._mouseOverIntervalID) {
|
|
// not enabled for mouseover, but should still relay the event.
|
|
nextStage&&nextStage._testMouseOver(clear, owner, eventTarget);
|
|
return;
|
|
}
|
|
|
|
// only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs.
|
|
if (this._primaryPointerID != -1 || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; }
|
|
|
|
var o = this._getPointerData(-1), e = o.posEvtObj;
|
|
var isEventTarget = eventTarget || e&&(e.target == this.canvas);
|
|
var target=null, common = -1, cursor="", t, i, l;
|
|
|
|
if (!owner && (clear || this.mouseInBounds && isEventTarget)) {
|
|
target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true);
|
|
this._mouseOverX = this.mouseX;
|
|
this._mouseOverY = this.mouseY;
|
|
}
|
|
|
|
var oldList = this._mouseOverTarget||[];
|
|
var oldTarget = oldList[oldList.length-1];
|
|
var list = this._mouseOverTarget = [];
|
|
|
|
// generate ancestor list and check for cursor:
|
|
t = target;
|
|
while (t) {
|
|
list.unshift(t);
|
|
if (t.cursor != null) { cursor = t.cursor; }
|
|
t = t.parent;
|
|
}
|
|
this.canvas.style.cursor = cursor;
|
|
if (!owner && eventTarget) { eventTarget.canvas.style.cursor = cursor; }
|
|
|
|
// find common ancestor:
|
|
for (i=0,l=list.length; i<l; i++) {
|
|
if (list[i] != oldList[i]) { break; }
|
|
common = i;
|
|
}
|
|
|
|
if (oldTarget != target) {
|
|
this._dispatchMouseEvent(oldTarget, "mouseout", true, -1, o, e);
|
|
}
|
|
|
|
for (i=oldList.length-1; i>common; i--) {
|
|
this._dispatchMouseEvent(oldList[i], "rollout", false, -1, o, e);
|
|
}
|
|
|
|
for (i=list.length-1; i>common; i--) {
|
|
this._dispatchMouseEvent(list[i], "rollover", false, -1, o, e);
|
|
}
|
|
|
|
if (oldTarget != target) {
|
|
this._dispatchMouseEvent(target, "mouseover", true, -1, o, e);
|
|
}
|
|
|
|
nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this);
|
|
};
|
|
|
|
/**
|
|
* @method _handleDoubleClick
|
|
* @protected
|
|
* @param {MouseEvent} e
|
|
* @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
|
|
**/
|
|
p._handleDoubleClick = function(e, owner) {
|
|
var target=null, nextStage=this._nextStage, o=this._getPointerData(-1);
|
|
if (!owner) {
|
|
target = this._getObjectsUnderPoint(o.x, o.y, null, true);
|
|
this._dispatchMouseEvent(target, "dblclick", true, -1, o, e);
|
|
}
|
|
nextStage&&nextStage._handleDoubleClick(e, owner || target && this);
|
|
};
|
|
|
|
/**
|
|
* @method _dispatchMouseEvent
|
|
* @protected
|
|
* @param {DisplayObject} target
|
|
* @param {String} type
|
|
* @param {Boolean} bubbles
|
|
* @param {Number} pointerId
|
|
* @param {Object} o
|
|
* @param {MouseEvent} [nativeEvent]
|
|
**/
|
|
p._dispatchMouseEvent = function(target, type, bubbles, pointerId, o, nativeEvent) {
|
|
// TODO: might be worth either reusing MouseEvent instances, or adding a willTrigger method to avoid GC.
|
|
if (!target || (!bubbles && !target.hasEventListener(type))) { return; }
|
|
/*
|
|
// TODO: account for stage transformations:
|
|
this._mtx = this.getConcatenatedMatrix(this._mtx).invert();
|
|
var pt = this._mtx.transformPoint(o.x, o.y);
|
|
var evt = new createjs.MouseEvent(type, bubbles, false, pt.x, pt.y, nativeEvent, pointerId, pointerId==this._primaryPointerID, o.rawX, o.rawY);
|
|
*/
|
|
var evt = new createjs.MouseEvent(type, bubbles, false, o.x, o.y, nativeEvent, pointerId, pointerId==this._primaryPointerID, o.rawX, o.rawY);
|
|
target.dispatchEvent(evt);
|
|
};
|
|
|
|
|
|
createjs.Stage = createjs.promote(Stage, "Container");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Bitmap.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
|
|
/**
|
|
* A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing
|
|
* HTML element, or a string.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var bitmap = new createjs.Bitmap("imagePath.jpg");
|
|
*
|
|
* <strong>Notes:</strong>
|
|
* <ol>
|
|
* <li>When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it
|
|
* will be displayed.</li>
|
|
* <li>Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this,
|
|
* the Bitmap can be cached.</li>
|
|
* <li>Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This
|
|
* happens in all browsers except recent Firefox builds.</li>
|
|
* <li>Images loaded cross-origin will throw cross-origin security errors when interacted with using a mouse, using
|
|
* methods such as `getObjectUnderPoint`, or using filters, or caching. You can get around this by setting
|
|
* `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`</li>
|
|
* </ol>
|
|
*
|
|
* @class Bitmap
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {Image | HTMLCanvasElement | HTMLVideoElement | String} imageOrUri The source object or URI to an image to
|
|
* display. This can be either an Image, Canvas, or Video object, or a string URI to an image file to load and use.
|
|
* If it is a URI, a new Image object will be constructed and assigned to the .image property.
|
|
**/
|
|
function Bitmap(imageOrUri) {
|
|
this.DisplayObject_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The image to render. This can be an Image, a Canvas, or a Video.
|
|
* @property image
|
|
* @type Image | HTMLCanvasElement | HTMLVideoElement
|
|
**/
|
|
if (typeof imageOrUri == "string") {
|
|
this.image = document.createElement("img");
|
|
this.image.src = imageOrUri;
|
|
} else {
|
|
this.image = imageOrUri;
|
|
}
|
|
|
|
/**
|
|
* Specifies an area of the source image to draw. If omitted, the whole image will be drawn.
|
|
* @property sourceRect
|
|
* @type Rectangle
|
|
* @default null
|
|
*/
|
|
this.sourceRect = null;
|
|
}
|
|
var p = createjs.extend(Bitmap, createjs.DisplayObject);
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function() {
|
|
var hasContent = this.cacheCanvas || (this.image && (this.image.complete || this.image.getContext || this.image.readyState >= 2));
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
*
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
* @return {Boolean}
|
|
**/
|
|
p.draw = function(ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache) || !this.image.complete) { return true; }
|
|
var img = this.image, rect = this.sourceRect;
|
|
if (rect) {
|
|
// some browsers choke on out of bound values, so we'll fix them:
|
|
var x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height, x = 0, y = 0, w = img.width, h = img.height;
|
|
if (x1 < 0) { x -= x1; x1 = 0; }
|
|
if (x2 > w) { x2 = w; }
|
|
if (y1 < 0) { y -= y1; y1 = 0; }
|
|
if (y2 > h) { y2 = h; }
|
|
ctx.drawImage(img, x1, y1, x2-x1, y2-y1, x, y, x2-x1, y2-y1);
|
|
} else {
|
|
ctx.drawImage(img, 0, 0);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
//Note, the doc sections below document using the specified APIs (from DisplayObject) from
|
|
//Bitmap. This is why they have no method implementations.
|
|
|
|
/**
|
|
* Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
|
|
* You should <b>not</b> cache Bitmap instances as it can degrade performance.
|
|
*
|
|
* <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
|
|
* To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
|
|
* method.
|
|
* @method cache
|
|
**/
|
|
|
|
/**
|
|
* Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
|
|
* You should <b>not</b> cache Bitmap instances as it can degrade performance.
|
|
*
|
|
* <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
|
|
* To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
|
|
* method.
|
|
* @method updateCache
|
|
**/
|
|
|
|
/**
|
|
* Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
|
|
* You should <b>not</b> cache Bitmap instances as it can degrade performance.
|
|
*
|
|
* <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
|
|
* To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
|
|
* method.
|
|
* @method uncache
|
|
**/
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
*/
|
|
p.getBounds = function() {
|
|
var rect = this.DisplayObject_getBounds();
|
|
if (rect) { return rect; }
|
|
var o = this.sourceRect || this.image;
|
|
var hasContent = (this.image && (this.image.complete || this.image.getContext || this.image.readyState >= 2));
|
|
return hasContent ? this._rectangle.setValues(0, 0, o.width, o.height) : null;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Bitmap instance.
|
|
* @method clone
|
|
* @return {Bitmap} a clone of the Bitmap instance.
|
|
**/
|
|
p.clone = function() {
|
|
var o = new Bitmap(this.image);
|
|
if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); }
|
|
this._cloneProps(o);
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Bitmap (name="+ this.name +")]";
|
|
};
|
|
|
|
|
|
createjs.Bitmap = createjs.promote(Bitmap, "DisplayObject");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Sprite.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Displays a frame or sequence of frames (ie. an animation) from a SpriteSheet instance. A sprite sheet is a series of
|
|
* images (usually animation frames) combined into a single image. For example, an animation consisting of 8 100x100
|
|
* images could be combined into a 400x200 sprite sheet (4 frames across by 2 high). You can display individual frames,
|
|
* play frames as an animation, and even sequence animations together.
|
|
*
|
|
* See the {{#crossLink "SpriteSheet"}}{{/crossLink}} class for more information on setting up frames and animations.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var instance = new createjs.Sprite(spriteSheet);
|
|
* instance.gotoAndStop("frameName");
|
|
*
|
|
* Until {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} is called,
|
|
* only the first defined frame defined in the sprite sheet will be displayed.
|
|
*
|
|
* @class Sprite
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {SpriteSheet} spriteSheet The SpriteSheet instance to play back. This includes the source image(s), frame
|
|
* dimensions, and frame data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
|
|
* @param {String|Number} [frameOrAnimation] The frame number or animation to play initially.
|
|
**/
|
|
function Sprite(spriteSheet, frameOrAnimation) {
|
|
this.DisplayObject_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The frame index that will be drawn when draw is called. Note that with some {{#crossLink "SpriteSheet"}}{{/crossLink}}
|
|
* definitions, this will advance non-sequentially. This will always be an integer value.
|
|
* @property currentFrame
|
|
* @type {Number}
|
|
* @default 0
|
|
* @readonly
|
|
**/
|
|
this.currentFrame = 0;
|
|
|
|
/**
|
|
* Returns the name of the currently playing animation.
|
|
* @property currentAnimation
|
|
* @type {String}
|
|
* @final
|
|
* @readonly
|
|
**/
|
|
this.currentAnimation = null;
|
|
|
|
/**
|
|
* Prevents the animation from advancing each tick automatically. For example, you could create a sprite
|
|
* sheet of icons, set paused to true, and display the appropriate icon by setting <code>currentFrame</code>.
|
|
* @property paused
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
this.paused = true;
|
|
|
|
/**
|
|
* The SpriteSheet instance to play back. This includes the source image, frame dimensions, and frame
|
|
* data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
|
|
* @property spriteSheet
|
|
* @type {SpriteSheet}
|
|
* @readonly
|
|
**/
|
|
this.spriteSheet = spriteSheet;
|
|
|
|
/**
|
|
* Specifies the current frame index within the currently playing animation. When playing normally, this will increase
|
|
* from 0 to n-1, where n is the number of frames in the current animation.
|
|
*
|
|
* This could be a non-integer value if
|
|
* using time-based playback (see {{#crossLink "Sprite/framerate"}}{{/crossLink}}, or if the animation's speed is
|
|
* not an integer.
|
|
* @property currentAnimationFrame
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.currentAnimationFrame = 0;
|
|
|
|
/**
|
|
* By default Sprite instances advance one frame per tick. Specifying a framerate for the Sprite (or its related
|
|
* SpriteSheet) will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
|
|
* framerate.
|
|
*
|
|
* For example, if a Sprite with a framerate of 10 is placed on a Stage being updated at 40fps, then the Sprite will
|
|
* advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
|
|
* vary slightly between frames.
|
|
*
|
|
* This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
|
|
* passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
|
|
* @property framerate
|
|
* @type {Number}
|
|
* @default 0
|
|
**/
|
|
this.framerate = 0;
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* Current animation object.
|
|
* @property _animation
|
|
* @protected
|
|
* @type {Object}
|
|
* @default null
|
|
**/
|
|
this._animation = null;
|
|
|
|
/**
|
|
* Current frame index.
|
|
* @property _currentFrame
|
|
* @protected
|
|
* @type {Number}
|
|
* @default null
|
|
**/
|
|
this._currentFrame = null;
|
|
|
|
/**
|
|
* Skips the next auto advance. Used by gotoAndPlay to avoid immediately jumping to the next frame
|
|
* @property _skipAdvance
|
|
* @protected
|
|
* @type {Boolean}
|
|
* @default false
|
|
**/
|
|
this._skipAdvance = false;
|
|
|
|
|
|
if (frameOrAnimation) { this.gotoAndPlay(frameOrAnimation); }
|
|
}
|
|
var p = createjs.extend(Sprite, createjs.DisplayObject);
|
|
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when an animation reaches its ends.
|
|
* @event animationend
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @param {String} name The name of the animation that just ended.
|
|
* @param {String} next The name of the next animation that will be played, or null. This will be the same as name if the animation is looping.
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched any time the current frame changes. For example, this could be due to automatic advancement on a tick,
|
|
* or calling gotoAndPlay() or gotoAndStop().
|
|
* @event change
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
*/
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function() {
|
|
var hasContent = this.cacheCanvas || this.spriteSheet.complete;
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
**/
|
|
p.draw = function(ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
|
|
this._normalizeFrame();
|
|
var o = this.spriteSheet.getFrame(this._currentFrame|0);
|
|
if (!o) { return false; }
|
|
var rect = o.rect;
|
|
if (rect.width && rect.height) { ctx.drawImage(o.image, rect.x, rect.y, rect.width, rect.height, -o.regX, -o.regY, rect.width, rect.height); }
|
|
return true;
|
|
};
|
|
|
|
//Note, the doc sections below document using the specified APIs (from DisplayObject) from
|
|
//Bitmap. This is why they have no method implementations.
|
|
|
|
/**
|
|
* Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
|
|
* You should not cache Sprite instances as it can degrade performance.
|
|
* @method cache
|
|
**/
|
|
|
|
/**
|
|
* Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
|
|
* You should not cache Sprite instances as it can degrade performance.
|
|
* @method updateCache
|
|
**/
|
|
|
|
/**
|
|
* Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
|
|
* You should not cache Sprite instances as it can degrade performance.
|
|
* @method uncache
|
|
**/
|
|
|
|
/**
|
|
* Play (unpause) the current animation. The Sprite will be paused if either {{#crossLink "Sprite/stop"}}{{/crossLink}}
|
|
* or {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} is called. Single frame animations will remain
|
|
* unchanged.
|
|
* @method play
|
|
**/
|
|
p.play = function() {
|
|
this.paused = false;
|
|
};
|
|
|
|
/**
|
|
* Stop playing a running animation. The Sprite will be playing if {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}
|
|
* is called. Note that calling {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} or {{#crossLink "Sprite/play"}}{{/crossLink}}
|
|
* will resume playback.
|
|
* @method stop
|
|
**/
|
|
p.stop = function() {
|
|
this.paused = true;
|
|
};
|
|
|
|
/**
|
|
* Sets paused to false and plays the specified animation name, named frame, or frame number.
|
|
* @method gotoAndPlay
|
|
* @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
|
|
* and begin playing.
|
|
**/
|
|
p.gotoAndPlay = function(frameOrAnimation) {
|
|
this.paused = false;
|
|
this._skipAdvance = true;
|
|
this._goto(frameOrAnimation);
|
|
};
|
|
|
|
/**
|
|
* Sets paused to true and seeks to the specified animation name, named frame, or frame number.
|
|
* @method gotoAndStop
|
|
* @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
|
|
* and stop.
|
|
**/
|
|
p.gotoAndStop = function(frameOrAnimation) {
|
|
this.paused = true;
|
|
this._goto(frameOrAnimation);
|
|
};
|
|
|
|
/**
|
|
* Advances the playhead. This occurs automatically each tick by default.
|
|
* @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set on the Sprite
|
|
* or its SpriteSheet.
|
|
* @method advance
|
|
*/
|
|
p.advance = function(time) {
|
|
var fps = this.framerate || this.spriteSheet.framerate;
|
|
var t = (fps && time != null) ? time/(1000/fps) : 1;
|
|
this._normalizeFrame(t);
|
|
};
|
|
|
|
/**
|
|
* Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the current frame relative to
|
|
* the origin. For example, a 90 x 70 frame with <code>regX=50</code> and <code>regY=40</code> would return a
|
|
* rectangle with [x=-50, y=-40, width=90, height=70]. This ignores transformations on the display object.
|
|
*
|
|
* Also see the SpriteSheet {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} method.
|
|
* @method getBounds
|
|
* @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully
|
|
* loaded.
|
|
**/
|
|
p.getBounds = function() {
|
|
// TODO: should this normalizeFrame?
|
|
return this.DisplayObject_getBounds() || this.spriteSheet.getFrameBounds(this.currentFrame, this._rectangle);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Sprite instance. Note that the same SpriteSheet is shared between cloned
|
|
* instances.
|
|
* @method clone
|
|
* @return {Sprite} a clone of the Sprite instance.
|
|
**/
|
|
p.clone = function() {
|
|
return this._cloneProps(new Sprite(this.spriteSheet));
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Sprite (name="+ this.name +")]";
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _cloneProps
|
|
* @param {Sprite} o
|
|
* @return {Sprite} o
|
|
* @protected
|
|
**/
|
|
p._cloneProps = function(o) {
|
|
this.DisplayObject__cloneProps(o);
|
|
o.currentFrame = this.currentFrame;
|
|
o.currentAnimation = this.currentAnimation;
|
|
o.paused = this.paused;
|
|
o.currentAnimationFrame = this.currentAnimationFrame;
|
|
o.framerate = this.framerate;
|
|
|
|
o._animation = this._animation;
|
|
o._currentFrame = this._currentFrame;
|
|
o._skipAdvance = this._skipAdvance;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* Advances the <code>currentFrame</code> if paused is not true. This is called automatically when the {{#crossLink "Stage"}}{{/crossLink}}
|
|
* ticks.
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* @protected
|
|
* @method _tick
|
|
**/
|
|
p._tick = function(evtObj) {
|
|
if (!this.paused) {
|
|
if (!this._skipAdvance) { this.advance(evtObj&&evtObj.delta); }
|
|
this._skipAdvance = false;
|
|
}
|
|
this.DisplayObject__tick(evtObj);
|
|
};
|
|
|
|
|
|
/**
|
|
* Normalizes the current frame, advancing animations and dispatching callbacks as appropriate.
|
|
* @protected
|
|
* @method _normalizeFrame
|
|
**/
|
|
p._normalizeFrame = function(frameDelta) {
|
|
frameDelta = frameDelta || 0;
|
|
var animation = this._animation;
|
|
var paused = this.paused;
|
|
var frame = this._currentFrame;
|
|
var l;
|
|
|
|
if (animation) {
|
|
var speed = animation.speed || 1;
|
|
var animFrame = this.currentAnimationFrame;
|
|
l = animation.frames.length;
|
|
if (animFrame + frameDelta * speed >= l) {
|
|
var next = animation.next;
|
|
if (this._dispatchAnimationEnd(animation, frame, paused, next, l - 1)) {
|
|
// something changed in the event stack, so we shouldn't make any more changes here.
|
|
return;
|
|
} else if (next) {
|
|
// sequence. Automatically calls _normalizeFrame again with the remaining frames.
|
|
return this._goto(next, frameDelta - (l - animFrame) / speed);
|
|
} else {
|
|
// end.
|
|
this.paused = true;
|
|
animFrame = animation.frames.length - 1;
|
|
}
|
|
} else {
|
|
animFrame += frameDelta * speed;
|
|
}
|
|
this.currentAnimationFrame = animFrame;
|
|
this._currentFrame = animation.frames[animFrame | 0]
|
|
} else {
|
|
frame = (this._currentFrame += frameDelta);
|
|
l = this.spriteSheet.getNumFrames();
|
|
if (frame >= l && l > 0) {
|
|
if (!this._dispatchAnimationEnd(animation, frame, paused, l - 1)) {
|
|
// looped.
|
|
if ((this._currentFrame -= l) >= l) { return this._normalizeFrame(); }
|
|
}
|
|
}
|
|
}
|
|
frame = this._currentFrame | 0;
|
|
if (this.currentFrame != frame) {
|
|
this.currentFrame = frame;
|
|
this.dispatchEvent("change");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Dispatches the "animationend" event. Returns true if a handler changed the animation (ex. calling {{#crossLink "Sprite/stop"}}{{/crossLink}},
|
|
* {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, etc.)
|
|
* @property _dispatchAnimationEnd
|
|
* @private
|
|
* @type {Function}
|
|
**/
|
|
p._dispatchAnimationEnd = function(animation, frame, paused, next, end) {
|
|
var name = animation ? animation.name : null;
|
|
if (this.hasEventListener("animationend")) {
|
|
var evt = new createjs.Event("animationend");
|
|
evt.name = name;
|
|
evt.next = next;
|
|
this.dispatchEvent(evt);
|
|
}
|
|
// did the animation get changed in the event stack?:
|
|
var changed = (this._animation != animation || this._currentFrame != frame);
|
|
// if the animation hasn't changed, but the sprite was paused, then we want to stick to the last frame:
|
|
if (!changed && !paused && this.paused) { this.currentAnimationFrame = end; changed = true; }
|
|
return changed;
|
|
};
|
|
|
|
/**
|
|
* Moves the playhead to the specified frame number or animation.
|
|
* @method _goto
|
|
* @param {String|Number} frameOrAnimation The frame number or animation that the playhead should move to.
|
|
* @param {Boolean} [frame] The frame of the animation to go to. Defaults to 0.
|
|
* @protected
|
|
**/
|
|
p._goto = function(frameOrAnimation, frame) {
|
|
this.currentAnimationFrame = 0;
|
|
if (isNaN(frameOrAnimation)) {
|
|
var data = this.spriteSheet.getAnimation(frameOrAnimation);
|
|
if (data) {
|
|
this._animation = data;
|
|
this.currentAnimation = frameOrAnimation;
|
|
this._normalizeFrame(frame);
|
|
}
|
|
} else {
|
|
this.currentAnimation = this._animation = null;
|
|
this._currentFrame = frameOrAnimation;
|
|
this._normalizeFrame();
|
|
}
|
|
};
|
|
|
|
|
|
createjs.Sprite = createjs.promote(Sprite, "DisplayObject");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Shape.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* A Shape allows you to display vector art in the display list. It composites a {{#crossLink "Graphics"}}{{/crossLink}}
|
|
* instance which exposes all of the vector drawing methods. The Graphics instance can be shared between multiple Shape
|
|
* instances to display the same vector graphics with different positions or transforms.
|
|
*
|
|
* If the vector art will not
|
|
* change between draws, you may want to use the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method to reduce the
|
|
* rendering cost.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var graphics = new createjs.Graphics().beginFill("#ff0000").drawRect(0, 0, 100, 100);
|
|
* var shape = new createjs.Shape(graphics);
|
|
*
|
|
* //Alternatively use can also use the graphics property of the Shape class to renderer the same as above.
|
|
* var shape = new createjs.Shape();
|
|
* shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100);
|
|
*
|
|
* @class Shape
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {Graphics} graphics Optional. The graphics instance to display. If null, a new Graphics instance will be created.
|
|
**/
|
|
function Shape(graphics) {
|
|
this.DisplayObject_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The graphics instance to display.
|
|
* @property graphics
|
|
* @type Graphics
|
|
**/
|
|
this.graphics = graphics ? graphics : new createjs.Graphics();
|
|
}
|
|
var p = createjs.extend(Shape, createjs.DisplayObject);
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the Shape would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the Shape would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function() {
|
|
var hasContent = this.cacheCanvas || (this.graphics && !this.graphics.isEmpty());
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the Shape into the specified context ignoring its visible, alpha, shadow, and transform. Returns true if
|
|
* the draw was handled (useful for overriding functionality).
|
|
*
|
|
* <i>NOTE: This method is mainly for internal use, though it may be useful for advanced uses.</i>
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
|
|
* used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
|
|
* @return {Boolean}
|
|
**/
|
|
p.draw = function(ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
|
|
this.graphics.draw(ctx, this);
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Shape. Some properties that are specific to this instance's current context are reverted to
|
|
* their defaults (for example .parent).
|
|
* @method clone
|
|
* @param {Boolean} recursive If true, this Shape's {{#crossLink "Graphics"}}{{/crossLink}} instance will also be
|
|
* cloned. If false, the Graphics instance will be shared with the new Shape.
|
|
**/
|
|
p.clone = function(recursive) {
|
|
var g = (recursive && this.graphics) ? this.graphics.clone() : this.graphics;
|
|
return this._cloneProps(new Shape(g));
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Shape (name="+ this.name +")]";
|
|
};
|
|
|
|
|
|
createjs.Shape = createjs.promote(Shape, "DisplayObject");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Text.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Display one or more lines of dynamic text (not user editable) in the display list. Line wrapping support (using the
|
|
* lineWidth) is very basic, wrapping on spaces and tabs only. Note that as an alternative to Text, you can position HTML
|
|
* text above or below the canvas relative to items in the display list using the {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
|
|
* method, or using {{#crossLink "DOMElement"}}{{/crossLink}}.
|
|
*
|
|
* <b>Please note that Text does not support HTML text, and can only display one font style at a time.</b> To use
|
|
* multiple font styles, you will need to create multiple text instances, and position them manually.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var text = new createjs.Text("Hello World", "20px Arial", "#ff7700");
|
|
* text.x = 100;
|
|
* text.textBaseline = "alphabetic";
|
|
*
|
|
* CreateJS Text supports web fonts (the same rules as Canvas). The font must be loaded and supported by the browser
|
|
* before it can be displayed.
|
|
*
|
|
* <strong>Note:</strong> Text can be expensive to generate, so cache instances where possible. Be aware that not all
|
|
* browsers will render Text exactly the same.
|
|
* @class Text
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {String} [text] The text to display.
|
|
* @param {String} [font] The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold
|
|
* 36px Arial").
|
|
* @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex.
|
|
* "#F00", "red", or "#FF0000").
|
|
**/
|
|
function Text(text, font, color) {
|
|
this.DisplayObject_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The text to display.
|
|
* @property text
|
|
* @type String
|
|
**/
|
|
this.text = text;
|
|
|
|
/**
|
|
* The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold 36px Arial").
|
|
* @property font
|
|
* @type String
|
|
**/
|
|
this.font = font;
|
|
|
|
/**
|
|
* The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. "#F00"). Default is "#000".
|
|
* It will also accept valid canvas fillStyle values.
|
|
* @property color
|
|
* @type String
|
|
**/
|
|
this.color = color;
|
|
|
|
/**
|
|
* The horizontal text alignment. Any of "start", "end", "left", "right", and "center". For detailed
|
|
* information view the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
|
|
* whatwg spec</a>. Default is "left".
|
|
* @property textAlign
|
|
* @type String
|
|
**/
|
|
this.textAlign = "left";
|
|
|
|
/**
|
|
* The vertical alignment point on the font. Any of "top", "hanging", "middle", "alphabetic", "ideographic", or
|
|
* "bottom". For detailed information view the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
|
|
* whatwg spec</a>. Default is "top".
|
|
* @property textBaseline
|
|
* @type String
|
|
*/
|
|
this.textBaseline = "top";
|
|
|
|
/**
|
|
* The maximum width to draw the text. If maxWidth is specified (not null), the text will be condensed or
|
|
* shrunk to make it fit in this width. For detailed information view the
|
|
* <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
|
|
* whatwg spec</a>.
|
|
* @property maxWidth
|
|
* @type Number
|
|
*/
|
|
this.maxWidth = null;
|
|
|
|
/**
|
|
* If greater than 0, the text will be drawn as a stroke (outline) of the specified width.
|
|
* @property outline
|
|
* @type Number
|
|
**/
|
|
this.outline = 0;
|
|
|
|
/**
|
|
* Indicates the line height (vertical distance between baselines) for multi-line text. If null or 0,
|
|
* the value of getMeasuredLineHeight is used.
|
|
* @property lineHeight
|
|
* @type Number
|
|
**/
|
|
this.lineHeight = 0;
|
|
|
|
/**
|
|
* Indicates the maximum width for a line of text before it is wrapped to multiple lines. If null,
|
|
* the text will not be wrapped.
|
|
* @property lineWidth
|
|
* @type Number
|
|
**/
|
|
this.lineWidth = null;
|
|
}
|
|
var p = createjs.extend(Text, createjs.DisplayObject);
|
|
|
|
|
|
// static properties:
|
|
/**
|
|
* @property _workingContext
|
|
* @type CanvasRenderingContext2D
|
|
* @private
|
|
**/
|
|
var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
|
|
if (canvas.getContext) { Text._workingContext = canvas.getContext("2d"); canvas.width = canvas.height = 1; }
|
|
|
|
|
|
// constants:
|
|
/**
|
|
* Lookup table for the ratio to offset bounds x calculations based on the textAlign property.
|
|
* @property H_OFFSETS
|
|
* @type Object
|
|
* @protected
|
|
* @static
|
|
**/
|
|
Text.H_OFFSETS = {start: 0, left: 0, center: -0.5, end: -1, right: -1};
|
|
|
|
/**
|
|
* Lookup table for the ratio to offset bounds y calculations based on the textBaseline property.
|
|
* @property H_OFFSETS
|
|
* @type Object
|
|
* @protected
|
|
* @static
|
|
**/
|
|
Text.V_OFFSETS = {top: 0, hanging: -0.01, middle: -0.4, alphabetic: -0.8, ideographic: -0.85, bottom: -1};
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function() {
|
|
var hasContent = this.cacheCanvas || (this.text != null && this.text !== "");
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
/**
|
|
* Draws the Text into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
**/
|
|
p.draw = function(ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
|
|
|
|
var col = this.color || "#000";
|
|
if (this.outline) { ctx.strokeStyle = col; ctx.lineWidth = this.outline*1; }
|
|
else { ctx.fillStyle = col; }
|
|
|
|
this._drawText(this._prepContext(ctx));
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Returns the measured, untransformed width of the text without wrapping. Use getBounds for a more robust value.
|
|
* @method getMeasuredWidth
|
|
* @return {Number} The measured, untransformed width of the text.
|
|
**/
|
|
p.getMeasuredWidth = function() {
|
|
return this._getMeasuredWidth(this.text);
|
|
};
|
|
|
|
/**
|
|
* Returns an approximate line height of the text, ignoring the lineHeight property. This is based on the measured
|
|
* width of a "M" character multiplied by 1.2, which provides an approximate line height for most fonts.
|
|
* @method getMeasuredLineHeight
|
|
* @return {Number} an approximate line height of the text, ignoring the lineHeight property. This is
|
|
* based on the measured width of a "M" character multiplied by 1.2, which approximates em for most fonts.
|
|
**/
|
|
p.getMeasuredLineHeight = function() {
|
|
return this._getMeasuredWidth("M")*1.2;
|
|
};
|
|
|
|
/**
|
|
* Returns the approximate height of multi-line text by multiplying the number of lines against either the
|
|
* <code>lineHeight</code> (if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that
|
|
* this operation requires the text flowing logic to run, which has an associated CPU cost.
|
|
* @method getMeasuredHeight
|
|
* @return {Number} The approximate height of the untransformed multi-line text.
|
|
**/
|
|
p.getMeasuredHeight = function() {
|
|
return this._drawText(null,{}).height;
|
|
};
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
*/
|
|
p.getBounds = function() {
|
|
var rect = this.DisplayObject_getBounds();
|
|
if (rect) { return rect; }
|
|
if (this.text == null || this.text == "") { return null; }
|
|
var o = this._drawText(null, {});
|
|
var w = (this.maxWidth && this.maxWidth < o.width) ? this.maxWidth : o.width;
|
|
var x = w * Text.H_OFFSETS[this.textAlign||"left"];
|
|
var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
|
|
var y = lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
|
|
return this._rectangle.setValues(x, y, w, o.height);
|
|
};
|
|
|
|
/**
|
|
* Returns an object with width, height, and lines properties. The width and height are the visual width and height
|
|
* of the drawn text. The lines property contains an array of strings, one for
|
|
* each line of text that will be drawn, accounting for line breaks and wrapping. These strings have trailing
|
|
* whitespace removed.
|
|
* @method getMetrics
|
|
* @return {Object} An object with width, height, and lines properties.
|
|
**/
|
|
p.getMetrics = function() {
|
|
var o = {lines:[]};
|
|
o.lineHeight = this.lineHeight || this.getMeasuredLineHeight();
|
|
o.vOffset = o.lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
|
|
return this._drawText(null, o, o.lines);
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of the Text instance.
|
|
* @method clone
|
|
* @return {Text} a clone of the Text instance.
|
|
**/
|
|
p.clone = function() {
|
|
return this._cloneProps(new Text(this.text, this.font, this.color));
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Text (text="+ (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _cloneProps
|
|
* @param {Text} o
|
|
* @protected
|
|
* @return {Text} o
|
|
**/
|
|
p._cloneProps = function(o) {
|
|
this.DisplayObject__cloneProps(o);
|
|
o.textAlign = this.textAlign;
|
|
o.textBaseline = this.textBaseline;
|
|
o.maxWidth = this.maxWidth;
|
|
o.outline = this.outline;
|
|
o.lineHeight = this.lineHeight;
|
|
o.lineWidth = this.lineWidth;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* @method _getWorkingContext
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @return {CanvasRenderingContext2D}
|
|
* @protected
|
|
**/
|
|
p._prepContext = function(ctx) {
|
|
ctx.font = this.font||"10px sans-serif";
|
|
ctx.textAlign = this.textAlign||"left";
|
|
ctx.textBaseline = this.textBaseline||"top";
|
|
return ctx;
|
|
};
|
|
|
|
/**
|
|
* Draws multiline text.
|
|
* @method _drawText
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @param {Object} o
|
|
* @param {Array} lines
|
|
* @return {Object}
|
|
* @protected
|
|
**/
|
|
p._drawText = function(ctx, o, lines) {
|
|
var paint = !!ctx;
|
|
if (!paint) {
|
|
ctx = Text._workingContext;
|
|
ctx.save();
|
|
this._prepContext(ctx);
|
|
}
|
|
var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
|
|
|
|
var maxW = 0, count = 0;
|
|
var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/);
|
|
for (var i=0, l=hardLines.length; i<l; i++) {
|
|
var str = hardLines[i];
|
|
var w = null;
|
|
|
|
if (this.lineWidth != null && (w = ctx.measureText(str).width) > this.lineWidth) {
|
|
// text wrapping:
|
|
var words = str.split(/(\s)/);
|
|
str = words[0];
|
|
w = ctx.measureText(str).width;
|
|
|
|
for (var j=1, jl=words.length; j<jl; j+=2) {
|
|
// Line needs to wrap:
|
|
var wordW = ctx.measureText(words[j] + words[j+1]).width;
|
|
if (w + wordW > this.lineWidth) {
|
|
if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
|
|
if (lines) { lines.push(str); }
|
|
if (w > maxW) { maxW = w; }
|
|
str = words[j+1];
|
|
w = ctx.measureText(str).width;
|
|
count++;
|
|
} else {
|
|
str += words[j] + words[j+1];
|
|
w += wordW;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
|
|
if (lines) { lines.push(str); }
|
|
if (o && w == null) { w = ctx.measureText(str).width; }
|
|
if (w > maxW) { maxW = w; }
|
|
count++;
|
|
}
|
|
|
|
if (o) {
|
|
o.width = maxW;
|
|
o.height = count*lineHeight;
|
|
}
|
|
if (!paint) { ctx.restore(); }
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* @method _drawTextLine
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* @param {String} text
|
|
* @param {Number} y
|
|
* @protected
|
|
**/
|
|
p._drawTextLine = function(ctx, text, y) {
|
|
// Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead:
|
|
if (this.outline) { ctx.strokeText(text, 0, y, this.maxWidth||0xFFFF); }
|
|
else { ctx.fillText(text, 0, y, this.maxWidth||0xFFFF); }
|
|
};
|
|
|
|
|
|
/**
|
|
* @method _getMeasuredWidth
|
|
* @param {String} text
|
|
* @protected
|
|
**/
|
|
p._getMeasuredWidth = function(text) {
|
|
var ctx = Text._workingContext;
|
|
ctx.save();
|
|
var w = this._prepContext(ctx).measureText(text).width;
|
|
ctx.restore();
|
|
return w;
|
|
};
|
|
|
|
|
|
createjs.Text = createjs.promote(Text, "DisplayObject");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// BitmapText.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Displays text using bitmap glyphs defined in a sprite sheet. Multi-line text is supported
|
|
* using new line characters, but automatic wrapping is not supported. See the
|
|
* {{#crossLink "BitmapText/spriteSheet:property"}}{{/crossLink}}
|
|
* property for more information on defining glyphs.
|
|
*
|
|
* <strong>Important:</strong> BitmapText extends Container, but is not designed to be used as one.
|
|
* As such, methods like addChild and removeChild are disabled.
|
|
* @class BitmapText
|
|
* @extends DisplayObject
|
|
* @param {String} [text=""] The text to display.
|
|
* @param {SpriteSheet} [spriteSheet=null] The spritesheet that defines the character glyphs.
|
|
* @constructor
|
|
**/
|
|
function BitmapText(text, spriteSheet) {
|
|
this.Container_constructor();
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The text to display.
|
|
* @property text
|
|
* @type String
|
|
* @default ""
|
|
**/
|
|
this.text = text||"";
|
|
|
|
/**
|
|
* A SpriteSheet instance that defines the glyphs for this bitmap text. Each glyph/character
|
|
* should have a single frame animation defined in the sprite sheet named the same as
|
|
* corresponding character. For example, the following animation definition:
|
|
*
|
|
* "A": {frames: [0]}
|
|
*
|
|
* would indicate that the frame at index 0 of the spritesheet should be drawn for the "A" character. The short form
|
|
* is also acceptable:
|
|
*
|
|
* "A": 0
|
|
*
|
|
* Note that if a character in the text is not found in the sprite sheet, it will also
|
|
* try to use the alternate case (upper or lower).
|
|
*
|
|
* See SpriteSheet for more information on defining sprite sheet data.
|
|
* @property spriteSheet
|
|
* @type String
|
|
* @default null
|
|
**/
|
|
this.spriteSheet = spriteSheet;
|
|
|
|
/**
|
|
* The height of each line of text. If 0, then it will use a line height calculated
|
|
* by checking for the height of the "1", "T", or "L" character (in that order). If
|
|
* those characters are not defined, it will use the height of the first frame of the
|
|
* sprite sheet.
|
|
* @property lineHeight
|
|
* @type Number
|
|
* @default 0
|
|
**/
|
|
this.lineHeight = 0;
|
|
|
|
/**
|
|
* This spacing (in pixels) will be added after each character in the output.
|
|
* @property letterSpacing
|
|
* @type Number
|
|
* @default 0
|
|
**/
|
|
this.letterSpacing = 0;
|
|
|
|
/**
|
|
* If a space character is not defined in the sprite sheet, then empty pixels equal to
|
|
* spaceWidth will be inserted instead. If 0, then it will use a value calculated
|
|
* by checking for the width of the "1", "l", "E", or "A" character (in that order). If
|
|
* those characters are not defined, it will use the width of the first frame of the
|
|
* sprite sheet.
|
|
* @property spaceWidth
|
|
* @type Number
|
|
* @default 0
|
|
**/
|
|
this.spaceWidth = 0;
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _oldProps
|
|
* @type Object
|
|
* @protected
|
|
**/
|
|
this._oldProps = {text:0,spriteSheet:0,lineHeight:0,letterSpacing:0,spaceWidth:0};
|
|
}
|
|
var p = createjs.extend(BitmapText, createjs.Container);
|
|
|
|
|
|
// static properties:
|
|
/**
|
|
* BitmapText uses Sprite instances to draw text. To reduce the creation and destruction of instances (and thus garbage collection), it maintains
|
|
* an internal object pool of sprite instances to reuse. Increasing this value can cause more sprites to be
|
|
* retained, slightly increasing memory use, but reducing instantiation.
|
|
* @property maxPoolSize
|
|
* @type Number
|
|
* @static
|
|
* @default 100
|
|
**/
|
|
BitmapText.maxPoolSize = 100;
|
|
|
|
/**
|
|
* Sprite object pool.
|
|
* @type {Array}
|
|
* @static
|
|
* @private
|
|
*/
|
|
BitmapText._spritePool = [];
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Docced in superclass.
|
|
**/
|
|
p.draw = function(ctx, ignoreCache) {
|
|
if (this.DisplayObject_draw(ctx, ignoreCache)) { return; }
|
|
this._updateText();
|
|
this.Container_draw(ctx, ignoreCache);
|
|
};
|
|
|
|
/**
|
|
* Docced in superclass.
|
|
**/
|
|
p.getBounds = function() {
|
|
this._updateText();
|
|
return this.Container_getBounds();
|
|
};
|
|
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
**/
|
|
p.isVisible = function() {
|
|
var hasContent = this.cacheCanvas || (this.spriteSheet && this.spriteSheet.complete && this.text);
|
|
return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
|
|
};
|
|
|
|
p.clone = function() {
|
|
return this._cloneProps(new BitmapText(this.text, this.spriteSheet));
|
|
};
|
|
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method addChild
|
|
**/
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method addChildAt
|
|
**/
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method removeChild
|
|
**/
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method removeChildAt
|
|
**/
|
|
/**
|
|
* <strong>Disabled in BitmapText.</strong>
|
|
* @method removeAllChildren
|
|
**/
|
|
p.addChild = p.addChildAt = p.removeChild = p.removeChildAt = p.removeAllChildren = function() {};
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _cloneProps
|
|
* @param {BitmapText} o
|
|
* @return {BitmapText} o
|
|
* @protected
|
|
**/
|
|
p._cloneProps = function(o) {
|
|
this.DisplayObject__cloneProps(o);
|
|
o.lineHeight = this.lineHeight;
|
|
o.letterSpacing = this.letterSpacing;
|
|
o.spaceWidth = this.spaceWidth;
|
|
return o;
|
|
};
|
|
|
|
/**
|
|
* @method _getFrameIndex
|
|
* @param {String} character
|
|
* @param {SpriteSheet} spriteSheet
|
|
* @return {Number}
|
|
* @protected
|
|
**/
|
|
p._getFrameIndex = function(character, spriteSheet) {
|
|
var c, o = spriteSheet.getAnimation(character);
|
|
if (!o) {
|
|
(character != (c = character.toUpperCase())) || (character != (c = character.toLowerCase())) || (c=null);
|
|
if (c) { o = spriteSheet.getAnimation(c); }
|
|
}
|
|
return o && o.frames[0];
|
|
};
|
|
|
|
/**
|
|
* @method _getFrame
|
|
* @param {String} character
|
|
* @param {SpriteSheet} spriteSheet
|
|
* @return {Object}
|
|
* @protected
|
|
**/
|
|
p._getFrame = function(character, spriteSheet) {
|
|
var index = this._getFrameIndex(character, spriteSheet);
|
|
return index == null ? index : spriteSheet.getFrame(index);
|
|
};
|
|
|
|
/**
|
|
* @method _getLineHeight
|
|
* @param {SpriteSheet} ss
|
|
* @return {Number}
|
|
* @protected
|
|
**/
|
|
p._getLineHeight = function(ss) {
|
|
var frame = this._getFrame("1",ss) || this._getFrame("T",ss) || this._getFrame("L",ss) || ss.getFrame(0);
|
|
return frame ? frame.rect.height : 1;
|
|
};
|
|
/**
|
|
* @method _getSpaceWidth
|
|
* @param {SpriteSheet} ss
|
|
* @return {Number}
|
|
* @protected
|
|
**/
|
|
p._getSpaceWidth = function(ss) {
|
|
var frame = this._getFrame("1",ss) || this._getFrame("l",ss) || this._getFrame("e",ss) || this._getFrame("a",ss) || ss.getFrame(0);
|
|
return frame ? frame.rect.width : 1;
|
|
};
|
|
|
|
/**
|
|
* @method _drawText
|
|
* @protected
|
|
**/
|
|
p._updateText = function() {
|
|
var x=0, y=0, o=this._oldProps, change=false, spaceW=this.spaceWidth, lineH=this.lineHeight, ss=this.spriteSheet;
|
|
var pool=BitmapText._spritePool, kids=this.children, childIndex=0, numKids=kids.length, sprite;
|
|
|
|
for (var n in o) {
|
|
if (o[n] != this[n]) {
|
|
o[n] = this[n];
|
|
change = true;
|
|
}
|
|
}
|
|
if (!change) { return; }
|
|
|
|
var hasSpace = !!this._getFrame(" ", ss);
|
|
if (!hasSpace && spaceW==0) { spaceW = this._getSpaceWidth(ss); }
|
|
if (lineH==0) { lineH = this._getLineHeight(ss); }
|
|
|
|
for(var i=0, l=this.text.length; i<l; i++) {
|
|
var character = this.text.charAt(i);
|
|
if (character == " " && !hasSpace) {
|
|
x += spaceW;
|
|
continue;
|
|
} else if (character=="\n" || character=="\r") {
|
|
if (character=="\r" && this.text.charAt(i+1) == "\n") { i++; } // crlf
|
|
x = 0;
|
|
y += lineH;
|
|
continue;
|
|
}
|
|
|
|
var index = this._getFrameIndex(character, ss);
|
|
if (index == null) { continue; }
|
|
|
|
if (childIndex < numKids) {
|
|
sprite = kids[childIndex];
|
|
} else {
|
|
kids.push(sprite = pool.length ? pool.pop() : new createjs.Sprite());
|
|
sprite.parent = this;
|
|
numKids++;
|
|
}
|
|
sprite.spriteSheet = ss;
|
|
sprite.gotoAndStop(index);
|
|
sprite.x = x;
|
|
sprite.y = y;
|
|
childIndex++;
|
|
|
|
x += sprite.getBounds().width + this.letterSpacing;
|
|
}
|
|
while (numKids > childIndex) { pool.push(sprite = kids.pop()); sprite.parent = null; numKids--; } // faster than removeChild.
|
|
if (pool.length > BitmapText.maxPoolSize) { pool.length = BitmapText.maxPoolSize; }
|
|
};
|
|
|
|
|
|
createjs.BitmapText = createjs.promote(BitmapText, "Container");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// SpriteSheetUtils.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* The SpriteSheetUtils class is a collection of static methods for working with {{#crossLink "SpriteSheet"}}{{/crossLink}}s.
|
|
* A sprite sheet is a series of images (usually animation frames) combined into a single image on a regular grid. For
|
|
* example, an animation consisting of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across
|
|
* by 2 high). The SpriteSheetUtils class uses a static interface and should not be instantiated.
|
|
* @class SpriteSheetUtils
|
|
* @static
|
|
**/
|
|
function SpriteSheetUtils() {
|
|
throw "SpriteSheetUtils cannot be instantiated";
|
|
}
|
|
|
|
|
|
// private static properties:
|
|
/**
|
|
* @property _workingCanvas
|
|
* @static
|
|
* @type HTMLCanvasElement | Object
|
|
* @protected
|
|
*/
|
|
/**
|
|
* @property _workingContext
|
|
* @static
|
|
* @type CanvasRenderingContext2D
|
|
* @protected
|
|
*/
|
|
var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
|
|
if (canvas.getContext) {
|
|
SpriteSheetUtils._workingCanvas = canvas;
|
|
SpriteSheetUtils._workingContext = canvas.getContext("2d");
|
|
canvas.width = canvas.height = 1;
|
|
}
|
|
|
|
|
|
// public static methods:
|
|
/**
|
|
* <b>This is an experimental method, and may be buggy. Please report issues.</b><br/><br/>
|
|
* Extends the existing sprite sheet by flipping the original frames horizontally, vertically, or both,
|
|
* and adding appropriate animation & frame data. The flipped animations will have a suffix added to their names
|
|
* (_h, _v, _hv as appropriate). Make sure the sprite sheet images are fully loaded before using this method.
|
|
* <br/><br/>
|
|
* For example:<br/>
|
|
* SpriteSheetUtils.addFlippedFrames(mySpriteSheet, true, true);
|
|
* The above would add frames that are flipped horizontally AND frames that are flipped vertically.
|
|
* <br/><br/>
|
|
* Note that you can also flip any display object by setting its scaleX or scaleY to a negative value. On some
|
|
* browsers (especially those without hardware accelerated canvas) this can result in slightly degraded performance,
|
|
* which is why addFlippedFrames is available.
|
|
* @method addFlippedFrames
|
|
* @static
|
|
* @param {SpriteSheet} spriteSheet
|
|
* @param {Boolean} horizontal If true, horizontally flipped frames will be added.
|
|
* @param {Boolean} vertical If true, vertically flipped frames will be added.
|
|
* @param {Boolean} both If true, frames that are flipped both horizontally and vertically will be added.
|
|
* @deprecated Modern browsers perform better when flipping via a transform (ex. scaleX=-1) rendering this obsolete.
|
|
**/
|
|
SpriteSheetUtils.addFlippedFrames = function(spriteSheet, horizontal, vertical, both) {
|
|
if (!horizontal && !vertical && !both) { return; }
|
|
|
|
var count = 0;
|
|
if (horizontal) { SpriteSheetUtils._flip(spriteSheet,++count,true,false); }
|
|
if (vertical) { SpriteSheetUtils._flip(spriteSheet,++count,false,true); }
|
|
if (both) { SpriteSheetUtils._flip(spriteSheet,++count,true,true); }
|
|
};
|
|
|
|
/**
|
|
* Returns a single frame of the specified sprite sheet as a new PNG image. An example of when this may be useful is
|
|
* to use a spritesheet frame as the source for a bitmap fill.
|
|
*
|
|
* <strong>WARNING:</strong> In almost all cases it is better to display a single frame using a {{#crossLink "Sprite"}}{{/crossLink}}
|
|
* with a {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} call than it is to slice out a frame using this
|
|
* method and display it with a Bitmap instance. You can also crop an image using the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}}
|
|
* property of {{#crossLink "Bitmap"}}{{/crossLink}}.
|
|
*
|
|
* The extractFrame method may cause cross-domain warnings since it accesses pixels directly on the canvas.
|
|
* @method extractFrame
|
|
* @static
|
|
* @param {Image} spriteSheet The SpriteSheet instance to extract a frame from.
|
|
* @param {Number|String} frameOrAnimation The frame number or animation name to extract. If an animation
|
|
* name is specified, only the first frame of the animation will be extracted.
|
|
* @return {Image} a single frame of the specified sprite sheet as a new PNG image.
|
|
*/
|
|
SpriteSheetUtils.extractFrame = function(spriteSheet, frameOrAnimation) {
|
|
if (isNaN(frameOrAnimation)) {
|
|
frameOrAnimation = spriteSheet.getAnimation(frameOrAnimation).frames[0];
|
|
}
|
|
var data = spriteSheet.getFrame(frameOrAnimation);
|
|
if (!data) { return null; }
|
|
var r = data.rect;
|
|
var canvas = SpriteSheetUtils._workingCanvas;
|
|
canvas.width = r.width;
|
|
canvas.height = r.height;
|
|
SpriteSheetUtils._workingContext.drawImage(data.image, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
|
|
var img = document.createElement("img");
|
|
img.src = canvas.toDataURL("image/png");
|
|
return img;
|
|
};
|
|
|
|
/**
|
|
* Merges the rgb channels of one image with the alpha channel of another. This can be used to combine a compressed
|
|
* JPEG image containing color data with a PNG32 monochromatic image containing alpha data. With certain types of
|
|
* images (those with detail that lend itself to JPEG compression) this can provide significant file size savings
|
|
* versus a single RGBA PNG32. This method is very fast (generally on the order of 1-2 ms to run).
|
|
* @method mergeAlpha
|
|
* @static
|
|
* @param {Image} rbgImage The image (or canvas) containing the RGB channels to use.
|
|
* @param {Image} alphaImage The image (or canvas) containing the alpha channel to use.
|
|
* @param {Canvas} canvas Optional. If specified, this canvas will be used and returned. If not, a new canvas will be created.
|
|
* @return {Canvas} A canvas with the combined image data. This can be used as a source for Bitmap or SpriteSheet.
|
|
* @deprecated Tools such as ImageAlpha generally provide better results. This will be moved to sandbox in the future.
|
|
*/
|
|
SpriteSheetUtils.mergeAlpha = function(rgbImage, alphaImage, canvas) {
|
|
if (!canvas) { canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); }
|
|
canvas.width = Math.max(alphaImage.width, rgbImage.width);
|
|
canvas.height = Math.max(alphaImage.height, rgbImage.height);
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.save();
|
|
ctx.drawImage(rgbImage,0,0);
|
|
ctx.globalCompositeOperation = "destination-in";
|
|
ctx.drawImage(alphaImage,0,0);
|
|
ctx.restore();
|
|
return canvas;
|
|
};
|
|
|
|
|
|
// private static methods:
|
|
SpriteSheetUtils._flip = function(spriteSheet, count, h, v) {
|
|
var imgs = spriteSheet._images;
|
|
var canvas = SpriteSheetUtils._workingCanvas;
|
|
var ctx = SpriteSheetUtils._workingContext;
|
|
var il = imgs.length/count;
|
|
for (var i=0;i<il;i++) {
|
|
var src = imgs[i];
|
|
src.__tmp = i; // a bit hacky, but faster than doing indexOf below.
|
|
ctx.setTransform(1,0,0,1,0,0);
|
|
ctx.clearRect(0,0,canvas.width+1,canvas.height+1);
|
|
canvas.width = src.width;
|
|
canvas.height = src.height;
|
|
ctx.setTransform(h?-1:1, 0, 0, v?-1:1, h?src.width:0, v?src.height:0);
|
|
ctx.drawImage(src,0,0);
|
|
var img = document.createElement("img");
|
|
img.src = canvas.toDataURL("image/png");
|
|
// work around a strange bug in Safari:
|
|
img.width = src.width;
|
|
img.height = src.height;
|
|
imgs.push(img);
|
|
}
|
|
|
|
var frames = spriteSheet._frames;
|
|
var fl = frames.length/count;
|
|
for (i=0;i<fl;i++) {
|
|
src = frames[i];
|
|
var rect = src.rect.clone();
|
|
img = imgs[src.image.__tmp+il*count];
|
|
|
|
var frame = {image:img,rect:rect,regX:src.regX,regY:src.regY};
|
|
if (h) {
|
|
rect.x = img.width-rect.x-rect.width; // update rect
|
|
frame.regX = rect.width-src.regX; // update registration point
|
|
}
|
|
if (v) {
|
|
rect.y = img.height-rect.y-rect.height; // update rect
|
|
frame.regY = rect.height-src.regY; // update registration point
|
|
}
|
|
frames.push(frame);
|
|
}
|
|
|
|
var sfx = "_"+(h?"h":"")+(v?"v":"");
|
|
var names = spriteSheet._animations;
|
|
var data = spriteSheet._data;
|
|
var al = names.length/count;
|
|
for (i=0;i<al;i++) {
|
|
var name = names[i];
|
|
src = data[name];
|
|
var anim = {name:name+sfx,speed:src.speed,next:src.next,frames:[]};
|
|
if (src.next) { anim.next += sfx; }
|
|
frames = src.frames;
|
|
for (var j=0,l=frames.length;j<l;j++) {
|
|
anim.frames.push(frames[j]+fl*count);
|
|
}
|
|
data[anim.name] = anim;
|
|
names.push(anim.name);
|
|
}
|
|
};
|
|
|
|
|
|
createjs.SpriteSheetUtils = SpriteSheetUtils;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// SpriteSheetBuilder.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* The SpriteSheetBuilder allows you to generate sprite sheets at run time from any display object. This can allow
|
|
* you to maintain your assets as vector graphics (for low file size), and render them at run time as sprite sheets
|
|
* for better performance.
|
|
*
|
|
* Sprite sheets can be built either synchronously, or asynchronously, so that large sprite sheets can be generated
|
|
* without locking the UI.
|
|
*
|
|
* Note that the "images" used in the generated sprite sheet are actually canvas elements, and that they will be sized
|
|
* to the nearest power of 2 up to the value of <code>maxWidth</code> or <code>maxHeight</code>.
|
|
* @class SpriteSheetBuilder
|
|
* @extends EventDispatcher
|
|
* @constructor
|
|
**/
|
|
function SpriteSheetBuilder() {
|
|
this.EventDispatcher_constructor();
|
|
|
|
// public properties:
|
|
/**
|
|
* The maximum width for the images (not individual frames) in the generated sprite sheet. It is recommended to use
|
|
* a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max dimensions, then
|
|
* additional images will be created as needed.
|
|
* @property maxWidth
|
|
* @type Number
|
|
* @default 2048
|
|
*/
|
|
this.maxWidth = 2048;
|
|
|
|
/**
|
|
* The maximum height for the images (not individual frames) in the generated sprite sheet. It is recommended to use
|
|
* a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max dimensions, then
|
|
* additional images will be created as needed.
|
|
* @property maxHeight
|
|
* @type Number
|
|
* @default 2048
|
|
**/
|
|
this.maxHeight = 2048;
|
|
|
|
/**
|
|
* The sprite sheet that was generated. This will be null before a build is completed successfully.
|
|
* @property spriteSheet
|
|
* @type SpriteSheet
|
|
**/
|
|
this.spriteSheet = null;
|
|
|
|
/**
|
|
* The scale to apply when drawing all frames to the sprite sheet. This is multiplied against any scale specified
|
|
* in the addFrame call. This can be used, for example, to generate a sprite sheet at run time that is tailored to
|
|
* the a specific device resolution (ex. tablet vs mobile).
|
|
* @property scale
|
|
* @type Number
|
|
* @default 1
|
|
**/
|
|
this.scale = 1;
|
|
|
|
/**
|
|
* The padding to use between frames. This is helpful to preserve antialiasing on drawn vector content.
|
|
* @property padding
|
|
* @type Number
|
|
* @default 1
|
|
**/
|
|
this.padding = 1;
|
|
|
|
/**
|
|
* A number from 0.01 to 0.99 that indicates what percentage of time the builder can use. This can be
|
|
* thought of as the number of seconds per second the builder will use. For example, with a timeSlice value of 0.3,
|
|
* the builder will run 20 times per second, using approximately 15ms per build (30% of available time, or 0.3s per second).
|
|
* Defaults to 0.3.
|
|
* @property timeSlice
|
|
* @type Number
|
|
* @default 0.3
|
|
**/
|
|
this.timeSlice = 0.3;
|
|
|
|
/**
|
|
* A value between 0 and 1 that indicates the progress of a build, or -1 if a build has not
|
|
* been initiated.
|
|
* @property progress
|
|
* @type Number
|
|
* @default -1
|
|
* @readonly
|
|
**/
|
|
this.progress = -1;
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _frames
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._frames = [];
|
|
|
|
/**
|
|
* @property _animations
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._animations = {};
|
|
|
|
/**
|
|
* @property _data
|
|
* @protected
|
|
* @type Array
|
|
**/
|
|
this._data = null;
|
|
|
|
/**
|
|
* @property _nextFrameIndex
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._nextFrameIndex = 0;
|
|
|
|
/**
|
|
* @property _index
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._index = 0;
|
|
|
|
/**
|
|
* @property _timerID
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._timerID = null;
|
|
|
|
/**
|
|
* @property _scale
|
|
* @protected
|
|
* @type Number
|
|
**/
|
|
this._scale = 1;
|
|
}
|
|
var p = createjs.extend(SpriteSheetBuilder, createjs.EventDispatcher);
|
|
|
|
// constants:
|
|
SpriteSheetBuilder.ERR_DIMENSIONS = "frame dimensions exceed max spritesheet dimensions";
|
|
SpriteSheetBuilder.ERR_RUNNING = "a build is already running";
|
|
|
|
// events:
|
|
/**
|
|
* Dispatched when a build completes.
|
|
* @event complete
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
/**
|
|
* Dispatched when an asynchronous build has progress.
|
|
* @event progress
|
|
* @param {Object} target The object that dispatched the event.
|
|
* @param {String} type The event type.
|
|
* @param {Number} progress The current progress value (0-1).
|
|
* @since 0.6.0
|
|
*/
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Adds a frame to the {{#crossLink "SpriteSheet"}}{{/crossLink}}. Note that the frame will not be drawn until you
|
|
* call {{#crossLink "SpriteSheetBuilder/build"}}{{/crossLink}} method. The optional setup params allow you to have
|
|
* a function run immediately before the draw occurs. For example, this allows you to add a single source multiple
|
|
* times, but manipulate it or its children to change it to generate different frames.
|
|
*
|
|
* Note that the source's transformations (x, y, scale, rotate, alpha) will be ignored, except for regX/Y. To apply
|
|
* transforms to a source object and have them captured in the sprite sheet, simply place it into a {{#crossLink "Container"}}{{/crossLink}}
|
|
* and pass in the Container as the source.
|
|
* @method addFrame
|
|
* @param {DisplayObject} source The source {{#crossLink "DisplayObject"}}{{/crossLink}} to draw as the frame.
|
|
* @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the
|
|
* source to draw to the frame. If not specified, it will look for a <code>getBounds</code> method, bounds property,
|
|
* or <code>nominalBounds</code> property on the source to use. If one is not found, the frame will be skipped.
|
|
* @param {Number} [scale=1] Optional. The scale to draw this frame at. Default is 1.
|
|
* @param {Function} [setupFunction] A function to call immediately before drawing this frame. It will be called with two parameters: the source, and setupData.
|
|
* @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter.
|
|
* @return {Number} The index of the frame that was just added, or null if a sourceRect could not be determined.
|
|
**/
|
|
p.addFrame = function(source, sourceRect, scale, setupFunction, setupData) {
|
|
if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
|
|
var rect = sourceRect||source.bounds||source.nominalBounds;
|
|
if (!rect&&source.getBounds) { rect = source.getBounds(); }
|
|
if (!rect) { return null; }
|
|
scale = scale||1;
|
|
return this._frames.push({source:source, sourceRect:rect, scale:scale, funct:setupFunction, data:setupData, index:this._frames.length, height:rect.height*scale})-1;
|
|
};
|
|
|
|
/**
|
|
* Adds an animation that will be included in the created sprite sheet.
|
|
* @method addAnimation
|
|
* @param {String} name The name for the animation.
|
|
* @param {Array} frames An array of frame indexes that comprise the animation. Ex. [3,6,5] would describe an animation
|
|
* that played frame indexes 3, 6, and 5 in that order.
|
|
* @param {String} [next] Specifies the name of the animation to continue to after this animation ends. You can
|
|
* also pass false to have the animation stop when it ends. By default it will loop to the start of the same animation.
|
|
* @param {Number} [frequency] Specifies a frame advance frequency for this animation. For example, a value
|
|
* of 2 would cause the animation to advance every second tick.
|
|
**/
|
|
p.addAnimation = function(name, frames, next, frequency) {
|
|
if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
|
|
this._animations[name] = {frames:frames, next:next, frequency:frequency};
|
|
};
|
|
|
|
/**
|
|
* This will take a MovieClip instance, and add its frames and labels to this builder. Labels will be added as an animation
|
|
* running from the label index to the next label. For example, if there is a label named "foo" at frame 0 and a label
|
|
* named "bar" at frame 10, in a MovieClip with 15 frames, it will add an animation named "foo" that runs from frame
|
|
* index 0 to 9, and an animation named "bar" that runs from frame index 10 to 14.
|
|
*
|
|
* Note that this will iterate through the full MovieClip with actionsEnabled set to false, ending on the last frame.
|
|
* @method addMovieClip
|
|
* @param {MovieClip} source The source MovieClip instance to add to the sprite sheet.
|
|
* @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the source to
|
|
* draw to the frame. If not specified, it will look for a <code>getBounds</code> method, <code>frameBounds</code>
|
|
* Array, <code>bounds</code> property, or <code>nominalBounds</code> property on the source to use. If one is not
|
|
* found, the MovieClip will be skipped.
|
|
* @param {Number} [scale=1] The scale to draw the movie clip at.
|
|
* @param {Function} [setupFunction] A function to call immediately before drawing each frame. It will be called with three parameters: the source, setupData, and the frame index.
|
|
* @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter.
|
|
* @param {Function} [labelFunction] This method will be called for each movieclip label that is added with four parameters: the label name, the source movieclip instance, the starting frame index (in the movieclip timeline) and the end index. It must return a new name for the label/animation, or false to exclude the label.
|
|
**/
|
|
p.addMovieClip = function(source, sourceRect, scale, setupFunction, setupData, labelFunction) {
|
|
if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
|
|
var rects = source.frameBounds;
|
|
var rect = sourceRect||source.bounds||source.nominalBounds;
|
|
if (!rect&&source.getBounds) { rect = source.getBounds(); }
|
|
if (!rect && !rects) { return; }
|
|
|
|
var i, l, baseFrameIndex = this._frames.length;
|
|
var duration = source.timeline.duration;
|
|
for (i=0; i<duration; i++) {
|
|
var r = (rects&&rects[i]) ? rects[i] : rect;
|
|
this.addFrame(source, r, scale, this._setupMovieClipFrame, {i:i, f:setupFunction, d:setupData});
|
|
}
|
|
var labels = source.timeline._labels;
|
|
var lbls = [];
|
|
for (var n in labels) {
|
|
lbls.push({index:labels[n], label:n});
|
|
}
|
|
if (lbls.length) {
|
|
lbls.sort(function(a,b){ return a.index-b.index; });
|
|
for (i=0,l=lbls.length; i<l; i++) {
|
|
var label = lbls[i].label;
|
|
var start = baseFrameIndex+lbls[i].index;
|
|
var end = baseFrameIndex+((i == l-1) ? duration : lbls[i+1].index);
|
|
var frames = [];
|
|
for (var j=start; j<end; j++) { frames.push(j); }
|
|
if (labelFunction) {
|
|
label = labelFunction(label, source, start, end);
|
|
if (!label) { continue; }
|
|
}
|
|
this.addAnimation(label, frames, true); // for now, this loops all animations.
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Builds a SpriteSheet instance based on the current frames.
|
|
* @method build
|
|
* @return {SpriteSheet} The created SpriteSheet instance, or null if a build is already running or an error occurred.
|
|
**/
|
|
p.build = function() {
|
|
if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
|
|
this._startBuild();
|
|
while (this._drawNext()) {}
|
|
this._endBuild();
|
|
return this.spriteSheet;
|
|
};
|
|
|
|
/**
|
|
* Asynchronously builds a {{#crossLink "SpriteSheet"}}{{/crossLink}} instance based on the current frames. It will
|
|
* run 20 times per second, using an amount of time defined by <code>timeSlice</code>. When it is complete it will
|
|
* call the specified callback.
|
|
* @method buildAsync
|
|
* @param {Number} [timeSlice] Sets the timeSlice property on this instance.
|
|
**/
|
|
p.buildAsync = function(timeSlice) {
|
|
if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
|
|
this.timeSlice = timeSlice;
|
|
this._startBuild();
|
|
var _this = this;
|
|
this._timerID = setTimeout(function() { _this._run(); }, 50-Math.max(0.01, Math.min(0.99, this.timeSlice||0.3))*50);
|
|
};
|
|
|
|
/**
|
|
* Stops the current asynchronous build.
|
|
* @method stopAsync
|
|
**/
|
|
p.stopAsync = function() {
|
|
clearTimeout(this._timerID);
|
|
this._data = null;
|
|
};
|
|
|
|
/**
|
|
* SpriteSheetBuilder instances cannot be cloned.
|
|
* @method clone
|
|
**/
|
|
p.clone = function() {
|
|
throw("SpriteSheetBuilder cannot be cloned.");
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[SpriteSheetBuilder]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _startBuild
|
|
* @protected
|
|
**/
|
|
p._startBuild = function() {
|
|
var pad = this.padding||0;
|
|
this.progress = 0;
|
|
this.spriteSheet = null;
|
|
this._index = 0;
|
|
this._scale = this.scale;
|
|
var dataFrames = [];
|
|
this._data = {
|
|
images: [],
|
|
frames: dataFrames,
|
|
animations: this._animations // TODO: should we "clone" _animations in case someone adds more animations after a build?
|
|
};
|
|
|
|
var frames = this._frames.slice();
|
|
frames.sort(function(a,b) { return (a.height<=b.height) ? -1 : 1; });
|
|
|
|
if (frames[frames.length-1].height+pad*2 > this.maxHeight) { throw SpriteSheetBuilder.ERR_DIMENSIONS; }
|
|
var y=0, x=0;
|
|
var img = 0;
|
|
while (frames.length) {
|
|
var o = this._fillRow(frames, y, img, dataFrames, pad);
|
|
if (o.w > x) { x = o.w; }
|
|
y += o.h;
|
|
if (!o.h || !frames.length) {
|
|
var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
|
|
canvas.width = this._getSize(x,this.maxWidth);
|
|
canvas.height = this._getSize(y,this.maxHeight);
|
|
this._data.images[img] = canvas;
|
|
if (!o.h) {
|
|
x=y=0;
|
|
img++;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _setupMovieClipFrame
|
|
* @protected
|
|
* @return {Number} The width & height of the row.
|
|
**/
|
|
p._setupMovieClipFrame = function(source, data) {
|
|
var ae = source.actionsEnabled;
|
|
source.actionsEnabled = false;
|
|
source.gotoAndStop(data.i);
|
|
source.actionsEnabled = ae;
|
|
data.f&&data.f(source, data.d, data.i);
|
|
};
|
|
|
|
/**
|
|
* @method _getSize
|
|
* @protected
|
|
* @return {Number} The width & height of the row.
|
|
**/
|
|
p._getSize = function(size,max) {
|
|
var pow = 4;
|
|
while (Math.pow(2,++pow) < size){}
|
|
return Math.min(max,Math.pow(2,pow));
|
|
};
|
|
|
|
/**
|
|
* @method _fillRow
|
|
* @param {Array} frames
|
|
* @param {Number} y
|
|
* @param {Image} img
|
|
* @param {Object} dataFrames
|
|
* @param {Number} pad
|
|
* @protected
|
|
* @return {Number} The width & height of the row.
|
|
**/
|
|
p._fillRow = function(frames, y, img, dataFrames, pad) {
|
|
var w = this.maxWidth;
|
|
var maxH = this.maxHeight;
|
|
y += pad;
|
|
var h = maxH-y;
|
|
var x = pad;
|
|
var height = 0;
|
|
for (var i=frames.length-1; i>=0; i--) {
|
|
var frame = frames[i];
|
|
var sc = this._scale*frame.scale;
|
|
var rect = frame.sourceRect;
|
|
var source = frame.source;
|
|
var rx = Math.floor(sc*rect.x-pad);
|
|
var ry = Math.floor(sc*rect.y-pad);
|
|
var rh = Math.ceil(sc*rect.height+pad*2);
|
|
var rw = Math.ceil(sc*rect.width+pad*2);
|
|
if (rw > w) { throw SpriteSheetBuilder.ERR_DIMENSIONS; }
|
|
if (rh > h || x+rw > w) { continue; }
|
|
frame.img = img;
|
|
frame.rect = new createjs.Rectangle(x,y,rw,rh);
|
|
height = height || rh;
|
|
frames.splice(i,1);
|
|
dataFrames[frame.index] = [x,y,rw,rh,img,Math.round(-rx+sc*source.regX-pad),Math.round(-ry+sc*source.regY-pad)];
|
|
x += rw;
|
|
}
|
|
return {w:x, h:height};
|
|
};
|
|
|
|
/**
|
|
* @method _endBuild
|
|
* @protected
|
|
**/
|
|
p._endBuild = function() {
|
|
this.spriteSheet = new createjs.SpriteSheet(this._data);
|
|
this._data = null;
|
|
this.progress = 1;
|
|
this.dispatchEvent("complete");
|
|
};
|
|
|
|
/**
|
|
* @method _run
|
|
* @protected
|
|
**/
|
|
p._run = function() {
|
|
var ts = Math.max(0.01, Math.min(0.99, this.timeSlice||0.3))*50;
|
|
var t = (new Date()).getTime()+ts;
|
|
var complete = false;
|
|
while (t > (new Date()).getTime()) {
|
|
if (!this._drawNext()) { complete = true; break; }
|
|
}
|
|
if (complete) {
|
|
this._endBuild();
|
|
} else {
|
|
var _this = this;
|
|
this._timerID = setTimeout(function() { _this._run(); }, 50-ts);
|
|
}
|
|
var p = this.progress = this._index/this._frames.length;
|
|
if (this.hasEventListener("progress")) {
|
|
var evt = new createjs.Event("progress");
|
|
evt.progress = p;
|
|
this.dispatchEvent(evt);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _drawNext
|
|
* @protected
|
|
* @return Boolean Returns false if this is the last draw.
|
|
**/
|
|
p._drawNext = function() {
|
|
var frame = this._frames[this._index];
|
|
var sc = frame.scale*this._scale;
|
|
var rect = frame.rect;
|
|
var sourceRect = frame.sourceRect;
|
|
var canvas = this._data.images[frame.img];
|
|
var ctx = canvas.getContext("2d");
|
|
frame.funct&&frame.funct(frame.source, frame.data);
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
ctx.clip();
|
|
ctx.translate(Math.ceil(rect.x-sourceRect.x*sc), Math.ceil(rect.y-sourceRect.y*sc));
|
|
ctx.scale(sc,sc);
|
|
frame.source.draw(ctx); // display object will draw itself.
|
|
ctx.restore();
|
|
return (++this._index) < this._frames.length;
|
|
};
|
|
|
|
|
|
createjs.SpriteSheetBuilder = createjs.promote(SpriteSheetBuilder, "EventDispatcher");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// DOMElement.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* <b>This class is still experimental, and more advanced use is likely to be buggy. Please report bugs.</b>
|
|
*
|
|
* A DOMElement allows you to associate a HTMLElement with the display list. It will be transformed
|
|
* within the DOM as though it is child of the {{#crossLink "Container"}}{{/crossLink}} it is added to. However, it is
|
|
* not rendered to canvas, and as such will retain whatever z-index it has relative to the canvas (ie. it will be
|
|
* drawn in front of or behind the canvas).
|
|
*
|
|
* The position of a DOMElement is relative to their parent node in the DOM. It is recommended that
|
|
* the DOM Object be added to a div that also contains the canvas so that they share the same position
|
|
* on the page.
|
|
*
|
|
* DOMElement is useful for positioning HTML elements over top of canvas content, and for elements
|
|
* that you want to display outside the bounds of the canvas. For example, a tooltip with rich HTML
|
|
* content.
|
|
*
|
|
* <h4>Mouse Interaction</h4>
|
|
*
|
|
* DOMElement instances are not full EaselJS display objects, and do not participate in EaselJS mouse
|
|
* events or support methods like hitTest. To get mouse events from a DOMElement, you must instead add handlers to
|
|
* the htmlElement (note, this does not support EventDispatcher)
|
|
*
|
|
* var domElement = new createjs.DOMElement(htmlElement);
|
|
* domElement.htmlElement.onclick = function() {
|
|
* console.log("clicked");
|
|
* }
|
|
*
|
|
* @class DOMElement
|
|
* @extends DisplayObject
|
|
* @constructor
|
|
* @param {HTMLElement} htmlElement A reference or id for the DOM element to manage.
|
|
*/
|
|
function DOMElement(htmlElement) {
|
|
this.DisplayObject_constructor();
|
|
|
|
if (typeof(htmlElement)=="string") { htmlElement = document.getElementById(htmlElement); }
|
|
this.mouseEnabled = false;
|
|
|
|
var style = htmlElement.style;
|
|
style.position = "absolute";
|
|
style.transformOrigin = style.WebkitTransformOrigin = style.msTransformOrigin = style.MozTransformOrigin = style.OTransformOrigin = "0% 0%";
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The DOM object to manage.
|
|
* @property htmlElement
|
|
* @type HTMLElement
|
|
*/
|
|
this.htmlElement = htmlElement;
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _oldMtx
|
|
* @type Matrix2D
|
|
* @protected
|
|
*/
|
|
this._oldProps = null;
|
|
}
|
|
var p = createjs.extend(DOMElement, createjs.DisplayObject);
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
|
|
* This does not account for whether it would be visible within the boundaries of the stage.
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method isVisible
|
|
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
|
|
*/
|
|
p.isVisible = function() {
|
|
return this.htmlElement != null;
|
|
};
|
|
|
|
/**
|
|
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
|
|
* Returns true if the draw was handled (useful for overriding functionality).
|
|
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
|
|
* @method draw
|
|
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
|
|
* @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
|
|
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
|
|
* into itself).
|
|
* @return {Boolean}
|
|
*/
|
|
p.draw = function(ctx, ignoreCache) {
|
|
// this relies on the _tick method because draw isn't called if the parent is not visible.
|
|
// the actual update happens in _handleDrawEnd
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method cache
|
|
*/
|
|
p.cache = function() {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method uncache
|
|
*/
|
|
p.uncache = function() {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method updateCache
|
|
*/
|
|
p.updateCache = function() {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method hitTest
|
|
*/
|
|
p.hitTest = function() {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method localToGlobal
|
|
*/
|
|
p.localToGlobal = function() {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method globalToLocal
|
|
*/
|
|
p.globalToLocal = function() {};
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @method localToLocal
|
|
*/
|
|
p.localToLocal = function() {};
|
|
|
|
/**
|
|
* DOMElement cannot be cloned. Throws an error.
|
|
* @method clone
|
|
*/
|
|
p.clone = function() {
|
|
throw("DOMElement cannot be cloned.")
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
*/
|
|
p.toString = function() {
|
|
return "[DOMElement (name="+ this.name +")]";
|
|
};
|
|
|
|
/**
|
|
* Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
|
|
* are not full EaselJS display objects and do not participate in EaselJS mouse events.
|
|
* @event click
|
|
*/
|
|
|
|
/**
|
|
* Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
|
|
* are not full EaselJS display objects and do not participate in EaselJS mouse events.
|
|
* @event dblClick
|
|
*/
|
|
|
|
/**
|
|
* Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
|
|
* are not full EaselJS display objects and do not participate in EaselJS mouse events.
|
|
* @event mousedown
|
|
*/
|
|
|
|
/**
|
|
* The HTMLElement can listen for the mouseover event, not the DOMElement instance.
|
|
* Since DOMElement instances are not full EaselJS display objects and do not participate in EaselJS mouse events.
|
|
* @event mouseover
|
|
*/
|
|
|
|
/**
|
|
* Not applicable to DOMElement.
|
|
* @event tick
|
|
*/
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _tick
|
|
* @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
|
|
* function.
|
|
* @protected
|
|
*/
|
|
p._tick = function(evtObj) {
|
|
var stage = this.getStage();
|
|
stage&&stage.on("drawend", this._handleDrawEnd, this, true);
|
|
this.DisplayObject__tick(evtObj);
|
|
};
|
|
|
|
/**
|
|
* @method _handleDrawEnd
|
|
* @param {Event} evt
|
|
* @protected
|
|
*/
|
|
p._handleDrawEnd = function(evt) {
|
|
var o = this.htmlElement;
|
|
if (!o) { return; }
|
|
var style = o.style;
|
|
|
|
var props = this.getConcatenatedDisplayProps(this._props), mtx = props.matrix;
|
|
|
|
var visibility = props.visible ? "visible" : "hidden";
|
|
if (visibility != style.visibility) { style.visibility = visibility; }
|
|
if (!props.visible) { return; }
|
|
|
|
var oldProps = this._oldProps, oldMtx = oldProps&&oldProps.matrix;
|
|
var n = 10000; // precision
|
|
|
|
if (!oldMtx || !oldMtx.equals(mtx)) {
|
|
var str = "matrix(" + (mtx.a*n|0)/n +","+ (mtx.b*n|0)/n +","+ (mtx.c*n|0)/n +","+ (mtx.d*n|0)/n +","+ (mtx.tx+0.5|0);
|
|
style.transform = style.WebkitTransform = style.OTransform = style.msTransform = str +","+ (mtx.ty+0.5|0) +")";
|
|
style.MozTransform = str +"px,"+ (mtx.ty+0.5|0) +"px)";
|
|
if (!oldProps) { oldProps = this._oldProps = new createjs.DisplayProps(true, NaN); }
|
|
oldProps.matrix.copy(mtx);
|
|
}
|
|
|
|
if (oldProps.alpha != props.alpha) {
|
|
style.opacity = ""+(props.alpha*n|0)/n;
|
|
oldProps.alpha = props.alpha;
|
|
}
|
|
};
|
|
|
|
|
|
createjs.DOMElement = createjs.promote(DOMElement, "DisplayObject");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Filter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Base class that all filters should inherit from. Filters need to be applied to objects that have been cached using
|
|
* the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method. If an object changes, please cache it again, or use
|
|
* {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}. Note that the filters must be applied before caching.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* myInstance.filters = [
|
|
* new createjs.ColorFilter(0, 0, 0, 1, 255, 0, 0),
|
|
* new createjs.BlurFilter(5, 5, 10)
|
|
* ];
|
|
* myInstance.cache(0,0, 100, 100);
|
|
*
|
|
* Note that each filter can implement a {{#crossLink "Filter/getBounds"}}{{/crossLink}} method, which returns the
|
|
* margins that need to be applied in order to fully display the filter. For example, the {{#crossLink "BlurFilter"}}{{/crossLink}}
|
|
* will cause an object to feather outwards, resulting in a margin around the shape.
|
|
*
|
|
* <h4>EaselJS Filters</h4>
|
|
* EaselJS comes with a number of pre-built filters. Note that individual filters are not compiled into the minified
|
|
* version of EaselJS. To use them, you must include them manually in the HTML.
|
|
* <ul><li>{{#crossLink "AlphaMapFilter"}}{{/crossLink}} : Map a greyscale image to the alpha channel of a display object</li>
|
|
* <li>{{#crossLink "AlphaMaskFilter"}}{{/crossLink}}: Map an image's alpha channel to the alpha channel of a display object</li>
|
|
* <li>{{#crossLink "BlurFilter"}}{{/crossLink}}: Apply vertical and horizontal blur to a display object</li>
|
|
* <li>{{#crossLink "ColorFilter"}}{{/crossLink}}: Color transform a display object</li>
|
|
* <li>{{#crossLink "ColorMatrixFilter"}}{{/crossLink}}: Transform an image using a {{#crossLink "ColorMatrix"}}{{/crossLink}}</li>
|
|
* </ul>
|
|
*
|
|
* @class Filter
|
|
* @constructor
|
|
**/
|
|
function Filter() {}
|
|
var p = Filter.prototype;
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Returns a rectangle with values indicating the margins required to draw the filter or null.
|
|
* For example, a filter that will extend the drawing area 4 pixels to the left, and 7 pixels to the right
|
|
* (but no pixels up or down) would return a rectangle with (x=-4, y=0, width=11, height=0).
|
|
* @method getBounds
|
|
* @return {Rectangle} a rectangle object indicating the margins required to draw the filter or null if the filter does not effect bounds.
|
|
**/
|
|
p.getBounds = function() {
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Applies the filter to the specified context.
|
|
* @method applyFilter
|
|
* @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
|
|
* @param {Number} x The x position to use for the source rect.
|
|
* @param {Number} y The y position to use for the source rect.
|
|
* @param {Number} width The width to use for the source rect.
|
|
* @param {Number} height The height to use for the source rect.
|
|
* @param {CanvasRenderingContext2D} [targetCtx] The 2D context to draw the result to. Defaults to the context passed to ctx.
|
|
* @param {Number} [targetX] The x position to draw the result to. Defaults to the value passed to x.
|
|
* @param {Number} [targetY] The y position to draw the result to. Defaults to the value passed to y.
|
|
* @return {Boolean} If the filter was applied successfully.
|
|
**/
|
|
p.applyFilter = function(ctx, x, y, width, height, targetCtx, targetX, targetY) {
|
|
// this is the default behaviour because most filters access pixel data. It is overridden when not needed.
|
|
targetCtx = targetCtx || ctx;
|
|
if (targetX == null) { targetX = x; }
|
|
if (targetY == null) { targetY = y; }
|
|
try {
|
|
var imageData = ctx.getImageData(x, y, width, height);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
if (this._applyFilter(imageData)) {
|
|
targetCtx.putImageData(imageData, targetX, targetY);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[Filter]";
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this Filter instance.
|
|
* @method clone
|
|
* @return {Filter} A clone of the current Filter instance.
|
|
**/
|
|
p.clone = function() {
|
|
return new Filter();
|
|
};
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _applyFilter
|
|
* @param {ImageData} imageData Target ImageData instance.
|
|
* @return {Boolean}
|
|
**/
|
|
p._applyFilter = function(imageData) { return true; };
|
|
|
|
|
|
createjs.Filter = Filter;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// BlurFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Applies a box blur to DisplayObjects. Note that this filter is fairly CPU intensive, particularly if the quality is
|
|
* set higher than 1.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example creates a red circle, and then applies a 5 pixel blur to it. It uses the {{#crossLink "Filter/getBounds"}}{{/crossLink}}
|
|
* method to account for the spread that the blur causes.
|
|
*
|
|
* var shape = new createjs.Shape().set({x:100,y:100});
|
|
* shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
|
|
*
|
|
* var blurFilter = new createjs.BlurFilter(5, 5, 1);
|
|
* shape.filters = [blurFilter];
|
|
* var bounds = blurFilter.getBounds();
|
|
*
|
|
* shape.cache(-50+bounds.x, -50+bounds.y, 100+bounds.width, 100+bounds.height);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
|
|
* @class BlurFilter
|
|
* @extends Filter
|
|
* @constructor
|
|
* @param {Number} [blurX=0] The horizontal blur radius in pixels.
|
|
* @param {Number} [blurY=0] The vertical blur radius in pixels.
|
|
* @param {Number} [quality=1] The number of blur iterations.
|
|
**/
|
|
function BlurFilter( blurX, blurY, quality) {
|
|
if ( isNaN(blurX) || blurX < 0 ) blurX = 0;
|
|
if ( isNaN(blurY) || blurY < 0 ) blurY = 0;
|
|
if ( isNaN(quality) || quality < 1 ) quality = 1;
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* Horizontal blur radius in pixels
|
|
* @property blurX
|
|
* @default 0
|
|
* @type Number
|
|
**/
|
|
this.blurX = blurX | 0;
|
|
|
|
/**
|
|
* Vertical blur radius in pixels
|
|
* @property blurY
|
|
* @default 0
|
|
* @type Number
|
|
**/
|
|
this.blurY = blurY | 0;
|
|
|
|
/**
|
|
* Number of blur iterations. For example, a value of 1 will produce a rough blur. A value of 2 will produce a
|
|
* smoother blur, but take twice as long to run.
|
|
* @property quality
|
|
* @default 1
|
|
* @type Number
|
|
**/
|
|
this.quality = quality | 0;
|
|
}
|
|
var p = createjs.extend(BlurFilter, createjs.Filter);
|
|
|
|
|
|
|
|
// constants:
|
|
/**
|
|
* Array of multiply values for blur calculations.
|
|
* @property MUL_TABLE
|
|
* @type Array
|
|
* @protected
|
|
* @static
|
|
**/
|
|
BlurFilter.MUL_TABLE = [1,171,205,293,57,373,79,137,241,27,391,357,41,19,283,265,497,469,443,421,25,191,365,349,335,161,155,149,9,278,269,261,505,245,475,231,449,437,213,415,405,395,193,377,369,361,353,345,169,331,325,319,313,307,301,37,145,285,281,69,271,267,263,259,509,501,493,243,479,118,465,459,113,446,55,435,429,423,209,413,51,403,199,393,97,3,379,375,371,367,363,359,355,351,347,43,85,337,333,165,327,323,5,317,157,311,77,305,303,75,297,294,73,289,287,71,141,279,277,275,68,135,67,133,33,262,260,129,511,507,503,499,495,491,61,121,481,477,237,235,467,232,115,457,227,451,7,445,221,439,218,433,215,427,425,211,419,417,207,411,409,203,202,401,399,396,197,49,389,387,385,383,95,189,47,187,93,185,23,183,91,181,45,179,89,177,11,175,87,173,345,343,341,339,337,21,167,83,331,329,327,163,81,323,321,319,159,79,315,313,39,155,309,307,153,305,303,151,75,299,149,37,295,147,73,291,145,289,287,143,285,71,141,281,35,279,139,69,275,137,273,17,271,135,269,267,133,265,33,263,131,261,130,259,129,257,1];
|
|
|
|
/**
|
|
* Array of shift values for blur calculations.
|
|
* @property SHG_TABLE
|
|
* @type Array
|
|
* @protected
|
|
* @static
|
|
**/
|
|
BlurFilter.SHG_TABLE = [0,9,10,11,9,12,10,11,12,9,13,13,10,9,13,13,14,14,14,14,10,13,14,14,14,13,13,13,9,14,14,14,15,14,15,14,15,15,14,15,15,15,14,15,15,15,15,15,14,15,15,15,15,15,15,12,14,15,15,13,15,15,15,15,16,16,16,15,16,14,16,16,14,16,13,16,16,16,15,16,13,16,15,16,14,9,16,16,16,16,16,16,16,16,16,13,14,16,16,15,16,16,10,16,15,16,14,16,16,14,16,16,14,16,16,14,15,16,16,16,14,15,14,15,13,16,16,15,17,17,17,17,17,17,14,15,17,17,16,16,17,16,15,17,16,17,11,17,16,17,16,17,16,17,17,16,17,17,16,17,17,16,16,17,17,17,16,14,17,17,17,17,15,16,14,16,15,16,13,16,15,16,14,16,15,16,12,16,15,16,17,17,17,17,17,13,16,15,17,17,17,16,15,17,17,17,16,15,17,17,14,16,17,17,16,17,17,16,15,17,16,14,17,16,15,17,16,17,17,16,17,15,16,17,14,17,16,15,17,16,17,13,17,16,17,17,16,17,14,17,16,17,16,17,16,17,9];
|
|
|
|
// public methods:
|
|
/** docced in super class **/
|
|
p.getBounds = function() {
|
|
var q = Math.pow(this.quality, 0.6)*0.5;
|
|
return new createjs.Rectangle(-this.blurX*q,-this.blurY*q,2*this.blurX*q,2*this.blurY*q);
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.clone = function() {
|
|
return new BlurFilter(this.blurX, this.blurY, this.quality);
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.toString = function() {
|
|
return "[BlurFilter]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
|
|
/** docced in super class **/
|
|
p._applyFilter = function(imageData) {
|
|
var radiusX = this.blurX/2;
|
|
if (isNaN(radiusX) || radiusX < 0 ) { return false; }
|
|
radiusX |= 0;
|
|
|
|
var radiusY = this.blurY/2;
|
|
if (isNaN(radiusY) || radiusY < 0 ) { return false; }
|
|
radiusY |= 0;
|
|
|
|
if ( radiusX == 0 && radiusY == 0 ) { return false; }
|
|
|
|
var iterations = this.quality;
|
|
if (isNaN(iterations) || iterations < 1) { iterations = 1; }
|
|
iterations |= 0;
|
|
if ( iterations > 3 ) { iterations = 3; }
|
|
if ( iterations < 1 ) { iterations = 1; }
|
|
|
|
var pixels = imageData.data, width = imageData.width, height=imageData.height;
|
|
|
|
// TODO: there are a lot of unused variables in this method:
|
|
var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, pr, pg, pb, pa;
|
|
|
|
var divx = radiusX + radiusX + 1;
|
|
var divy = radiusY + radiusY + 1;
|
|
var widthMinus1 = width - 1;
|
|
var heightMinus1 = height - 1;
|
|
var rxp1 = radiusX + 1;
|
|
var ryp1 = radiusY + 1;
|
|
var stackStartX = {r:0,b:0,g:0,a:0,next:null};
|
|
var stackx = stackStartX;
|
|
for ( i = 1; i < divx; i++ )
|
|
{
|
|
stackx = stackx.next = {r:0,b:0,g:0,a:0,next:null};
|
|
}
|
|
stackx.next = stackStartX;
|
|
|
|
var stackStartY = {r:0,b:0,g:0,a:0,next:null};
|
|
var stacky = stackStartY;
|
|
for ( i = 1; i < divy; i++ )
|
|
{
|
|
stacky = stacky.next = {r:0,b:0,g:0,a:0,next:null};
|
|
}
|
|
stacky.next = stackStartY;
|
|
|
|
var stackIn = null;
|
|
|
|
var mul_table = BlurFilter.MUL_TABLE;
|
|
var shg_table = BlurFilter.SHG_TABLE;
|
|
|
|
while ( iterations-- > 0 ) {
|
|
yw = yi = 0;
|
|
var mul_sum = mul_table[radiusX];
|
|
var shg_sum = shg_table[radiusX];
|
|
for ( y = height; --y > -1; )
|
|
{
|
|
r_sum = rxp1 * ( pr = pixels[yi] );
|
|
g_sum = rxp1 * ( pg = pixels[yi+1] );
|
|
b_sum = rxp1 * ( pb = pixels[yi+2] );
|
|
a_sum = rxp1 * ( pa = pixels[yi+3] );
|
|
|
|
stackx = stackStartX;
|
|
|
|
for( i = rxp1; --i > -1; )
|
|
{
|
|
stackx.r = pr;
|
|
stackx.g = pg;
|
|
stackx.b = pb;
|
|
stackx.a = pa;
|
|
stackx = stackx.next;
|
|
}
|
|
|
|
for( i = 1; i < rxp1; i++ )
|
|
{
|
|
p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
|
|
r_sum += ( stackx.r = pixels[p]);
|
|
g_sum += ( stackx.g = pixels[p+1]);
|
|
b_sum += ( stackx.b = pixels[p+2]);
|
|
a_sum += ( stackx.a = pixels[p+3]);
|
|
|
|
stackx = stackx.next;
|
|
}
|
|
|
|
stackIn = stackStartX;
|
|
for ( x = 0; x < width; x++ )
|
|
{
|
|
pixels[yi++] = (r_sum * mul_sum) >>> shg_sum;
|
|
pixels[yi++] = (g_sum * mul_sum) >>> shg_sum;
|
|
pixels[yi++] = (b_sum * mul_sum) >>> shg_sum;
|
|
pixels[yi++] = (a_sum * mul_sum) >>> shg_sum;
|
|
|
|
p = ( yw + ( ( p = x + radiusX + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
|
|
|
|
r_sum -= stackIn.r - ( stackIn.r = pixels[p]);
|
|
g_sum -= stackIn.g - ( stackIn.g = pixels[p+1]);
|
|
b_sum -= stackIn.b - ( stackIn.b = pixels[p+2]);
|
|
a_sum -= stackIn.a - ( stackIn.a = pixels[p+3]);
|
|
|
|
stackIn = stackIn.next;
|
|
|
|
}
|
|
yw += width;
|
|
}
|
|
|
|
mul_sum = mul_table[radiusY];
|
|
shg_sum = shg_table[radiusY];
|
|
for ( x = 0; x < width; x++ )
|
|
{
|
|
yi = x << 2;
|
|
|
|
r_sum = ryp1 * ( pr = pixels[yi]);
|
|
g_sum = ryp1 * ( pg = pixels[yi+1]);
|
|
b_sum = ryp1 * ( pb = pixels[yi+2]);
|
|
a_sum = ryp1 * ( pa = pixels[yi+3]);
|
|
|
|
stacky = stackStartY;
|
|
|
|
for( i = 0; i < ryp1; i++ )
|
|
{
|
|
stacky.r = pr;
|
|
stacky.g = pg;
|
|
stacky.b = pb;
|
|
stacky.a = pa;
|
|
stacky = stacky.next;
|
|
}
|
|
|
|
yp = width;
|
|
|
|
for( i = 1; i <= radiusY; i++ )
|
|
{
|
|
yi = ( yp + x ) << 2;
|
|
|
|
r_sum += ( stacky.r = pixels[yi]);
|
|
g_sum += ( stacky.g = pixels[yi+1]);
|
|
b_sum += ( stacky.b = pixels[yi+2]);
|
|
a_sum += ( stacky.a = pixels[yi+3]);
|
|
|
|
stacky = stacky.next;
|
|
|
|
if( i < heightMinus1 )
|
|
{
|
|
yp += width;
|
|
}
|
|
}
|
|
|
|
yi = x;
|
|
stackIn = stackStartY;
|
|
if ( iterations > 0 )
|
|
{
|
|
for ( y = 0; y < height; y++ )
|
|
{
|
|
p = yi << 2;
|
|
pixels[p+3] = pa =(a_sum * mul_sum) >>> shg_sum;
|
|
if ( pa > 0 )
|
|
{
|
|
pixels[p] = ((r_sum * mul_sum) >>> shg_sum );
|
|
pixels[p+1] = ((g_sum * mul_sum) >>> shg_sum );
|
|
pixels[p+2] = ((b_sum * mul_sum) >>> shg_sum );
|
|
} else {
|
|
pixels[p] = pixels[p+1] = pixels[p+2] = 0
|
|
}
|
|
|
|
p = ( x + (( ( p = y + ryp1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
|
|
|
|
r_sum -= stackIn.r - ( stackIn.r = pixels[p]);
|
|
g_sum -= stackIn.g - ( stackIn.g = pixels[p+1]);
|
|
b_sum -= stackIn.b - ( stackIn.b = pixels[p+2]);
|
|
a_sum -= stackIn.a - ( stackIn.a = pixels[p+3]);
|
|
|
|
stackIn = stackIn.next;
|
|
|
|
yi += width;
|
|
}
|
|
} else {
|
|
for ( y = 0; y < height; y++ )
|
|
{
|
|
p = yi << 2;
|
|
pixels[p+3] = pa =(a_sum * mul_sum) >>> shg_sum;
|
|
if ( pa > 0 )
|
|
{
|
|
pa = 255 / pa;
|
|
pixels[p] = ((r_sum * mul_sum) >>> shg_sum ) * pa;
|
|
pixels[p+1] = ((g_sum * mul_sum) >>> shg_sum ) * pa;
|
|
pixels[p+2] = ((b_sum * mul_sum) >>> shg_sum ) * pa;
|
|
} else {
|
|
pixels[p] = pixels[p+1] = pixels[p+2] = 0
|
|
}
|
|
|
|
p = ( x + (( ( p = y + ryp1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
|
|
|
|
r_sum -= stackIn.r - ( stackIn.r = pixels[p]);
|
|
g_sum -= stackIn.g - ( stackIn.g = pixels[p+1]);
|
|
b_sum -= stackIn.b - ( stackIn.b = pixels[p+2]);
|
|
a_sum -= stackIn.a - ( stackIn.a = pixels[p+3]);
|
|
|
|
stackIn = stackIn.next;
|
|
|
|
yi += width;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
createjs.BlurFilter = createjs.promote(BlurFilter, "Filter");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// AlphaMapFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Applies a greyscale alpha map image (or canvas) to the target, such that the alpha channel of the result will
|
|
* be copied from the red channel of the map, and the RGB channels will be copied from the target.
|
|
*
|
|
* Generally, it is recommended that you use {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}, because it has much
|
|
* better performance.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example draws a red->blue box, caches it, and then uses the cache canvas as an alpha map on a 100x100 image.
|
|
*
|
|
* var box = new createjs.Shape();
|
|
* box.graphics.beginLinearGradientFill(["#ff0000", "#0000ff"], [0, 1], 0, 0, 0, 100)
|
|
* box.graphics.drawRect(0, 0, 100, 100);
|
|
* box.cache(0, 0, 100, 100);
|
|
*
|
|
* var bmp = new createjs.Bitmap("path/to/image.jpg");
|
|
* bmp.filters = [
|
|
* new createjs.AlphaMapFilter(box.cacheCanvas)
|
|
* ];
|
|
* bmp.cache(0, 0, 100, 100);
|
|
* stage.addChild(bmp);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
|
|
* @class AlphaMapFilter
|
|
* @extends Filter
|
|
* @constructor
|
|
* @param {Image|HTMLCanvasElement} alphaMap The greyscale image (or canvas) to use as the alpha value for the
|
|
* result. This should be exactly the same dimensions as the target.
|
|
**/
|
|
function AlphaMapFilter(alphaMap) {
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The greyscale image (or canvas) to use as the alpha value for the result. This should be exactly the same
|
|
* dimensions as the target.
|
|
* @property alphaMap
|
|
* @type Image|HTMLCanvasElement
|
|
**/
|
|
this.alphaMap = alphaMap;
|
|
|
|
|
|
// private properties:
|
|
/**
|
|
* @property _alphaMap
|
|
* @protected
|
|
* @type Image|HTMLCanvasElement
|
|
**/
|
|
this._alphaMap = null;
|
|
|
|
/**
|
|
* @property _mapData
|
|
* @protected
|
|
* @type Uint8ClampedArray
|
|
**/
|
|
this._mapData = null;
|
|
}
|
|
var p = createjs.extend(AlphaMapFilter, createjs.Filter);
|
|
|
|
|
|
// public methods:
|
|
/** docced in super class **/
|
|
p.clone = function () {
|
|
var o = new AlphaMapFilter(this.alphaMap);
|
|
o._alphaMap = this._alphaMap;
|
|
o._mapData = this._mapData;
|
|
return o;
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.toString = function () {
|
|
return "[AlphaMapFilter]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
/** docced in super class **/
|
|
p._applyFilter = function (imageData) {
|
|
if (!this.alphaMap) { return true; }
|
|
if (!this._prepAlphaMap()) { return false; }
|
|
|
|
// TODO: update to support scenarios where the target has different dimensions.
|
|
var data = imageData.data;
|
|
var map = this._mapData;
|
|
for(var i=0, l=data.length; i<l; i += 4) { data[i + 3] = map[i] || 0; }
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* @method _prepAlphaMap
|
|
* @protected
|
|
**/
|
|
p._prepAlphaMap = function () {
|
|
if (!this.alphaMap) { return false; }
|
|
if (this.alphaMap == this._alphaMap && this._mapData) { return true; }
|
|
|
|
this._mapData = null;
|
|
var map = this._alphaMap = this.alphaMap;
|
|
var canvas = map;
|
|
var ctx;
|
|
if (map instanceof HTMLCanvasElement) {
|
|
ctx = canvas.getContext("2d");
|
|
} else {
|
|
canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
|
|
canvas.width = map.width;
|
|
canvas.height = map.height;
|
|
ctx = canvas.getContext("2d");
|
|
ctx.drawImage(map, 0, 0);
|
|
}
|
|
|
|
try {
|
|
var imgData = ctx.getImageData(0, 0, map.width, map.height);
|
|
} catch (e) {
|
|
//if (!this.suppressCrossDomainErrors) throw new Error("unable to access local image data: " + e);
|
|
return false;
|
|
}
|
|
|
|
this._mapData = imgData.data;
|
|
return true;
|
|
};
|
|
|
|
|
|
createjs.AlphaMapFilter = createjs.promote(AlphaMapFilter, "Filter");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// AlphaMaskFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Applies the alpha from the mask image (or canvas) to the target, such that the alpha channel of the result will
|
|
* be derived from the mask, and the RGB channels will be copied from the target. This can be used, for example, to
|
|
* apply an alpha mask to a display object. This can also be used to combine a JPG compressed RGB image with a PNG32
|
|
* alpha mask, which can result in a much smaller file size than a single PNG32 containing ARGB.
|
|
*
|
|
* <b>IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters correctly.</b>
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example draws a gradient box, then caches it and uses the "cacheCanvas" as the alpha mask on a 100x100 image.
|
|
*
|
|
* var box = new createjs.Shape();
|
|
* box.graphics.beginLinearGradientFill(["#000000", "rgba(0, 0, 0, 0)"], [0, 1], 0, 0, 100, 100)
|
|
* box.graphics.drawRect(0, 0, 100, 100);
|
|
* box.cache(0, 0, 100, 100);
|
|
*
|
|
* var bmp = new createjs.Bitmap("path/to/image.jpg");
|
|
* bmp.filters = [
|
|
* new createjs.AlphaMaskFilter(box.cacheCanvas)
|
|
* ];
|
|
* bmp.cache(0, 0, 100, 100);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
|
|
* @class AlphaMaskFilter
|
|
* @extends Filter
|
|
* @constructor
|
|
* @param {Image} mask
|
|
**/
|
|
function AlphaMaskFilter(mask) {
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* The image (or canvas) to use as the mask.
|
|
* @property mask
|
|
* @type Image
|
|
**/
|
|
this.mask = mask;
|
|
}
|
|
var p = createjs.extend(AlphaMaskFilter, createjs.Filter);
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Applies the filter to the specified context.
|
|
*
|
|
* <strong>IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters
|
|
* correctly.</strong>
|
|
* @method applyFilter
|
|
* @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
|
|
* @param {Number} x The x position to use for the source rect.
|
|
* @param {Number} y The y position to use for the source rect.
|
|
* @param {Number} width The width to use for the source rect.
|
|
* @param {Number} height The height to use for the source rect.
|
|
* @param {CanvasRenderingContext2D} [targetCtx] NOT SUPPORTED IN THIS FILTER. The 2D context to draw the result to. Defaults to the context passed to ctx.
|
|
* @param {Number} [targetX] NOT SUPPORTED IN THIS FILTER. The x position to draw the result to. Defaults to the value passed to x.
|
|
* @param {Number} [targetY] NOT SUPPORTED IN THIS FILTER. The y position to draw the result to. Defaults to the value passed to y.
|
|
* @return {Boolean} If the filter was applied successfully.
|
|
**/
|
|
p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) {
|
|
if (!this.mask) { return true; }
|
|
targetCtx = targetCtx || ctx;
|
|
if (targetX == null) { targetX = x; }
|
|
if (targetY == null) { targetY = y; }
|
|
|
|
targetCtx.save();
|
|
if (ctx != targetCtx) {
|
|
// TODO: support targetCtx and targetX/Y
|
|
// clearRect, then draw the ctx in?
|
|
return false;
|
|
}
|
|
|
|
targetCtx.globalCompositeOperation = "destination-in";
|
|
targetCtx.drawImage(this.mask, targetX, targetY);
|
|
targetCtx.restore();
|
|
return true;
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.clone = function () {
|
|
return new AlphaMaskFilter(this.mask);
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.toString = function () {
|
|
return "[AlphaMaskFilter]";
|
|
};
|
|
|
|
|
|
createjs.AlphaMaskFilter = createjs.promote(AlphaMaskFilter, "Filter");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// ColorFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Applies a color transform to DisplayObjects.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example draws a red circle, and then transforms it to Blue. This is accomplished by multiplying all the channels
|
|
* to 0 (except alpha, which is set to 1), and then adding 255 to the blue channel.
|
|
*
|
|
* var shape = new createjs.Shape().set({x:100,y:100});
|
|
* shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
|
|
*
|
|
* shape.filters = [
|
|
* new createjs.ColorFilter(0,0,0,1, 0,0,255,0)
|
|
* ];
|
|
* shape.cache(-50, -50, 100, 100);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
|
|
* @class ColorFilter
|
|
* @param {Number} [redMultiplier=1] The amount to multiply against the red channel. This is a range between 0 and 1.
|
|
* @param {Number} [greenMultiplier=1] The amount to multiply against the green channel. This is a range between 0 and 1.
|
|
* @param {Number} [blueMultiplier=1] The amount to multiply against the blue channel. This is a range between 0 and 1.
|
|
* @param {Number} [alphaMultiplier=1] The amount to multiply against the alpha channel. This is a range between 0 and 1.
|
|
* @param {Number} [redOffset=0] The amount to add to the red channel after it has been multiplied. This is a range
|
|
* between -255 and 255.
|
|
* @param {Number} [greenOffset=0] The amount to add to the green channel after it has been multiplied. This is a range
|
|
* between -255 and 255.
|
|
* @param {Number} [blueOffset=0] The amount to add to the blue channel after it has been multiplied. This is a range
|
|
* between -255 and 255.
|
|
* @param {Number} [alphaOffset=0] The amount to add to the alpha channel after it has been multiplied. This is a range
|
|
* between -255 and 255.
|
|
* @constructor
|
|
* @extends Filter
|
|
**/
|
|
function ColorFilter(redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier, redOffset, greenOffset, blueOffset, alphaOffset) {
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* Red channel multiplier.
|
|
* @property redMultiplier
|
|
* @type Number
|
|
**/
|
|
this.redMultiplier = redMultiplier != null ? redMultiplier : 1;
|
|
|
|
/**
|
|
* Green channel multiplier.
|
|
* @property greenMultiplier
|
|
* @type Number
|
|
**/
|
|
this.greenMultiplier = greenMultiplier != null ? greenMultiplier : 1;
|
|
|
|
/**
|
|
* Blue channel multiplier.
|
|
* @property blueMultiplier
|
|
* @type Number
|
|
**/
|
|
this.blueMultiplier = blueMultiplier != null ? blueMultiplier : 1;
|
|
|
|
/**
|
|
* Alpha channel multiplier.
|
|
* @property alphaMultiplier
|
|
* @type Number
|
|
**/
|
|
this.alphaMultiplier = alphaMultiplier != null ? alphaMultiplier : 1;
|
|
|
|
/**
|
|
* Red channel offset (added to value).
|
|
* @property redOffset
|
|
* @type Number
|
|
**/
|
|
this.redOffset = redOffset || 0;
|
|
|
|
/**
|
|
* Green channel offset (added to value).
|
|
* @property greenOffset
|
|
* @type Number
|
|
**/
|
|
this.greenOffset = greenOffset || 0;
|
|
|
|
/**
|
|
* Blue channel offset (added to value).
|
|
* @property blueOffset
|
|
* @type Number
|
|
**/
|
|
this.blueOffset = blueOffset || 0;
|
|
|
|
/**
|
|
* Alpha channel offset (added to value).
|
|
* @property alphaOffset
|
|
* @type Number
|
|
**/
|
|
this.alphaOffset = alphaOffset || 0;
|
|
}
|
|
var p = createjs.extend(ColorFilter, createjs.Filter);
|
|
|
|
|
|
// public methods:
|
|
/** docced in super class **/
|
|
p.toString = function() {
|
|
return "[ColorFilter]";
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.clone = function() {
|
|
return new ColorFilter(this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier, this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset);
|
|
};
|
|
|
|
|
|
// private methods:
|
|
/** docced in super class **/
|
|
p._applyFilter = function(imageData) {
|
|
var data = imageData.data;
|
|
var l = data.length;
|
|
for (var i=0; i<l; i+=4) {
|
|
data[i] = data[i]*this.redMultiplier+this.redOffset;
|
|
data[i+1] = data[i+1]*this.greenMultiplier+this.greenOffset;
|
|
data[i+2] = data[i+2]*this.blueMultiplier+this.blueOffset;
|
|
data[i+3] = data[i+3]*this.alphaMultiplier+this.alphaOffset;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
|
|
createjs.ColorFilter = createjs.promote(ColorFilter, "Filter");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// ColorMatrix.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Provides helper functions for assembling a matrix for use with the {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}.
|
|
* Most methods return the instance to facilitate chained calls.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* myColorMatrix.adjustHue(20).adjustBrightness(50);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for an example of how to apply filters, or {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}
|
|
* for an example of how to use ColorMatrix to change a DisplayObject's color.
|
|
* @class ColorMatrix
|
|
* @param {Number} brightness
|
|
* @param {Number} contrast
|
|
* @param {Number} saturation
|
|
* @param {Number} hue
|
|
* @constructor
|
|
**/
|
|
function ColorMatrix(brightness, contrast, saturation, hue) {
|
|
this.setColor(brightness, contrast, saturation, hue);
|
|
}
|
|
var p = ColorMatrix.prototype;
|
|
|
|
|
|
// constants:
|
|
/**
|
|
* Array of delta values for contrast calculations.
|
|
* @property DELTA_INDEX
|
|
* @type Array
|
|
* @protected
|
|
* @static
|
|
**/
|
|
ColorMatrix.DELTA_INDEX = [
|
|
0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
|
|
0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
|
|
0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
|
|
0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
|
|
0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
|
|
1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
|
|
1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
|
|
2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
|
|
4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
|
|
7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
|
|
10.0
|
|
];
|
|
|
|
/**
|
|
* Identity matrix values.
|
|
* @property IDENTITY_MATRIX
|
|
* @type Array
|
|
* @protected
|
|
* @static
|
|
**/
|
|
ColorMatrix.IDENTITY_MATRIX = [
|
|
1,0,0,0,0,
|
|
0,1,0,0,0,
|
|
0,0,1,0,0,
|
|
0,0,0,1,0,
|
|
0,0,0,0,1
|
|
];
|
|
|
|
/**
|
|
* The constant length of a color matrix.
|
|
* @property LENGTH
|
|
* @type Number
|
|
* @protected
|
|
* @static
|
|
**/
|
|
ColorMatrix.LENGTH = ColorMatrix.IDENTITY_MATRIX.length;
|
|
|
|
|
|
// public methods:
|
|
/**
|
|
* Resets the instance with the specified values.
|
|
* @method setColor
|
|
* @param {Number} brightness
|
|
* @param {Number} contrast
|
|
* @param {Number} saturation
|
|
* @param {Number} hue
|
|
* @protected
|
|
*/
|
|
p.setColor = function(brightness,contrast,saturation,hue) {
|
|
return this.reset().adjustColor(brightness,contrast,saturation,hue);;
|
|
};
|
|
|
|
/**
|
|
* Resets the matrix to identity values.
|
|
* @method reset
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
*/
|
|
p.reset = function() {
|
|
return this.copyMatrix(ColorMatrix.IDENTITY_MATRIX);
|
|
};
|
|
|
|
/**
|
|
* Shortcut method to adjust brightness, contrast, saturation and hue.
|
|
* Equivalent to calling adjustHue(hue), adjustContrast(contrast),
|
|
* adjustBrightness(brightness), adjustSaturation(saturation), in that order.
|
|
* @method adjustColor
|
|
* @param {Number} brightness
|
|
* @param {Number} contrast
|
|
* @param {Number} saturation
|
|
* @param {Number} hue
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.adjustColor = function(brightness,contrast,saturation,hue) {
|
|
this.adjustHue(hue);
|
|
this.adjustContrast(contrast);
|
|
this.adjustBrightness(brightness);
|
|
return this.adjustSaturation(saturation);
|
|
};
|
|
|
|
/**
|
|
* Adjusts the brightness of pixel color by adding the specified value to the red, green and blue channels.
|
|
* Positive values will make the image brighter, negative values will make it darker.
|
|
* @method adjustBrightness
|
|
* @param {Number} value A value between -255 & 255 that will be added to the RGB channels.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.adjustBrightness = function(value) {
|
|
if (value == 0 || isNaN(value)) { return this; }
|
|
value = this._cleanValue(value,255);
|
|
this._multiplyMatrix([
|
|
1,0,0,0,value,
|
|
0,1,0,0,value,
|
|
0,0,1,0,value,
|
|
0,0,0,1,0,
|
|
0,0,0,0,1
|
|
]);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adjusts the contrast of pixel color.
|
|
* Positive values will increase contrast, negative values will decrease contrast.
|
|
* @method adjustContrast
|
|
* @param {Number} value A value between -100 & 100.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.adjustContrast = function(value) {
|
|
if (value == 0 || isNaN(value)) { return this; }
|
|
value = this._cleanValue(value,100);
|
|
var x;
|
|
if (value<0) {
|
|
x = 127+value/100*127;
|
|
} else {
|
|
x = value%1;
|
|
if (x == 0) {
|
|
x = ColorMatrix.DELTA_INDEX[value];
|
|
} else {
|
|
x = ColorMatrix.DELTA_INDEX[(value<<0)]*(1-x)+ColorMatrix.DELTA_INDEX[(value<<0)+1]*x; // use linear interpolation for more granularity.
|
|
}
|
|
x = x*127+127;
|
|
}
|
|
this._multiplyMatrix([
|
|
x/127,0,0,0,0.5*(127-x),
|
|
0,x/127,0,0,0.5*(127-x),
|
|
0,0,x/127,0,0.5*(127-x),
|
|
0,0,0,1,0,
|
|
0,0,0,0,1
|
|
]);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Adjusts the color saturation of the pixel.
|
|
* Positive values will increase saturation, negative values will decrease saturation (trend towards greyscale).
|
|
* @method adjustSaturation
|
|
* @param {Number} value A value between -100 & 100.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.adjustSaturation = function(value) {
|
|
if (value == 0 || isNaN(value)) { return this; }
|
|
value = this._cleanValue(value,100);
|
|
var x = 1+((value > 0) ? 3*value/100 : value/100);
|
|
var lumR = 0.3086;
|
|
var lumG = 0.6094;
|
|
var lumB = 0.0820;
|
|
this._multiplyMatrix([
|
|
lumR*(1-x)+x,lumG*(1-x),lumB*(1-x),0,0,
|
|
lumR*(1-x),lumG*(1-x)+x,lumB*(1-x),0,0,
|
|
lumR*(1-x),lumG*(1-x),lumB*(1-x)+x,0,0,
|
|
0,0,0,1,0,
|
|
0,0,0,0,1
|
|
]);
|
|
return this;
|
|
};
|
|
|
|
|
|
/**
|
|
* Adjusts the hue of the pixel color.
|
|
* @method adjustHue
|
|
* @param {Number} value A value between -180 & 180.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.adjustHue = function(value) {
|
|
if (value == 0 || isNaN(value)) { return this; }
|
|
value = this._cleanValue(value,180)/180*Math.PI;
|
|
var cosVal = Math.cos(value);
|
|
var sinVal = Math.sin(value);
|
|
var lumR = 0.213;
|
|
var lumG = 0.715;
|
|
var lumB = 0.072;
|
|
this._multiplyMatrix([
|
|
lumR+cosVal*(1-lumR)+sinVal*(-lumR),lumG+cosVal*(-lumG)+sinVal*(-lumG),lumB+cosVal*(-lumB)+sinVal*(1-lumB),0,0,
|
|
lumR+cosVal*(-lumR)+sinVal*(0.143),lumG+cosVal*(1-lumG)+sinVal*(0.140),lumB+cosVal*(-lumB)+sinVal*(-0.283),0,0,
|
|
lumR+cosVal*(-lumR)+sinVal*(-(1-lumR)),lumG+cosVal*(-lumG)+sinVal*(lumG),lumB+cosVal*(1-lumB)+sinVal*(lumB),0,0,
|
|
0,0,0,1,0,
|
|
0,0,0,0,1
|
|
]);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Concatenates (multiplies) the specified matrix with this one.
|
|
* @method concat
|
|
* @param {Array} matrix An array or ColorMatrix instance.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.concat = function(matrix) {
|
|
matrix = this._fixMatrix(matrix);
|
|
if (matrix.length != ColorMatrix.LENGTH) { return this; }
|
|
this._multiplyMatrix(matrix);
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a clone of this ColorMatrix.
|
|
* @method clone
|
|
* @return {ColorMatrix} A clone of this ColorMatrix.
|
|
**/
|
|
p.clone = function() {
|
|
return (new ColorMatrix()).copyMatrix(this);
|
|
};
|
|
|
|
/**
|
|
* Return a length 25 (5x5) array instance containing this matrix's values.
|
|
* @method toArray
|
|
* @return {Array} An array holding this matrix's values.
|
|
**/
|
|
p.toArray = function() {
|
|
var arr = [];
|
|
for (var i= 0, l=ColorMatrix.LENGTH; i<l; i++) {
|
|
arr[i] = this[i];
|
|
}
|
|
return arr;
|
|
};
|
|
|
|
/**
|
|
* Copy the specified matrix's values to this matrix.
|
|
* @method copyMatrix
|
|
* @param {Array} matrix An array or ColorMatrix instance.
|
|
* @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
|
|
**/
|
|
p.copyMatrix = function(matrix) {
|
|
var l = ColorMatrix.LENGTH;
|
|
for (var i=0;i<l;i++) {
|
|
this[i] = matrix[i];
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Returns a string representation of this object.
|
|
* @method toString
|
|
* @return {String} a string representation of the instance.
|
|
**/
|
|
p.toString = function() {
|
|
return "[ColorMatrix]";
|
|
};
|
|
|
|
|
|
// private methods:
|
|
/**
|
|
* @method _multiplyMatrix
|
|
* @param {Array} matrix
|
|
* @protected
|
|
**/
|
|
p._multiplyMatrix = function(matrix) {
|
|
var i, j, k, col = [];
|
|
|
|
for (i=0;i<5;i++) {
|
|
for (j=0;j<5;j++) {
|
|
col[j] = this[j+i*5];
|
|
}
|
|
for (j=0;j<5;j++) {
|
|
var val=0;
|
|
for (k=0;k<5;k++) {
|
|
val += matrix[j+k*5]*col[k];
|
|
}
|
|
this[j+i*5] = val;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Make sure values are within the specified range, hue has a limit of 180, brightness is 255, others are 100.
|
|
* @method _cleanValue
|
|
* @param {Number} value The raw number
|
|
* @param {Number} limit The maximum that the number can be. The minimum is the limit * -1.
|
|
* @protected
|
|
**/
|
|
p._cleanValue = function(value, limit) {
|
|
return Math.min(limit,Math.max(-limit,value));
|
|
};
|
|
|
|
/**
|
|
* Makes sure matrixes are 5x5 (25 long).
|
|
* @method _fixMatrix
|
|
* @param {Array} matrix
|
|
* @protected
|
|
**/
|
|
p._fixMatrix = function(matrix) {
|
|
if (matrix instanceof ColorMatrix) { matrix = matrix.toArray(); }
|
|
if (matrix.length < ColorMatrix.LENGTH) {
|
|
matrix = matrix.slice(0,matrix.length).concat(ColorMatrix.IDENTITY_MATRIX.slice(matrix.length,ColorMatrix.LENGTH));
|
|
} else if (matrix.length > ColorMatrix.LENGTH) {
|
|
matrix = matrix.slice(0,ColorMatrix.LENGTH);
|
|
}
|
|
return matrix;
|
|
};
|
|
|
|
|
|
createjs.ColorMatrix = ColorMatrix;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// ColorMatrixFilter.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Allows you to carry out complex color operations such as modifying saturation, brightness, or inverting. See the
|
|
* {{#crossLink "ColorMatrix"}}{{/crossLink}} for more information on changing colors. For an easier color transform,
|
|
* consider the {{#crossLink "ColorFilter"}}{{/crossLink}}.
|
|
*
|
|
* <h4>Example</h4>
|
|
* This example creates a red circle, inverts its hue, and then saturates it to brighten it up.
|
|
*
|
|
* var shape = new createjs.Shape().set({x:100,y:100});
|
|
* shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
|
|
*
|
|
* var matrix = new createjs.ColorMatrix().adjustHue(180).adjustSaturation(100);
|
|
* shape.filters = [
|
|
* new createjs.ColorMatrixFilter(matrix)
|
|
* ];
|
|
*
|
|
* shape.cache(-50, -50, 100, 100);
|
|
*
|
|
* See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
|
|
* @class ColorMatrixFilter
|
|
* @constructor
|
|
* @extends Filter
|
|
* @param {Array | ColorMatrix} matrix A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
|
|
* class.
|
|
**/
|
|
function ColorMatrixFilter(matrix) {
|
|
|
|
|
|
// public properties:
|
|
/**
|
|
* A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
|
|
* @property matrix
|
|
* @type Array | ColorMatrix
|
|
**/
|
|
this.matrix = matrix;
|
|
}
|
|
var p = createjs.extend(ColorMatrixFilter, createjs.Filter);
|
|
|
|
|
|
// public methods:
|
|
/** docced in super class **/
|
|
p.toString = function() {
|
|
return "[ColorMatrixFilter]";
|
|
};
|
|
|
|
/** docced in super class **/
|
|
p.clone = function() {
|
|
return new ColorMatrixFilter(this.matrix);
|
|
};
|
|
|
|
// private methods:
|
|
/** docced in super class **/
|
|
p._applyFilter = function(imageData) {
|
|
var data = imageData.data;
|
|
var l = data.length;
|
|
var r,g,b,a;
|
|
var mtx = this.matrix;
|
|
var m0 = mtx[0], m1 = mtx[1], m2 = mtx[2], m3 = mtx[3], m4 = mtx[4];
|
|
var m5 = mtx[5], m6 = mtx[6], m7 = mtx[7], m8 = mtx[8], m9 = mtx[9];
|
|
var m10 = mtx[10], m11 = mtx[11], m12 = mtx[12], m13 = mtx[13], m14 = mtx[14];
|
|
var m15 = mtx[15], m16 = mtx[16], m17 = mtx[17], m18 = mtx[18], m19 = mtx[19];
|
|
|
|
for (var i=0; i<l; i+=4) {
|
|
r = data[i];
|
|
g = data[i+1];
|
|
b = data[i+2];
|
|
a = data[i+3];
|
|
data[i] = r*m0+g*m1+b*m2+a*m3+m4; // red
|
|
data[i+1] = r*m5+g*m6+b*m7+a*m8+m9; // green
|
|
data[i+2] = r*m10+g*m11+b*m12+a*m13+m14; // blue
|
|
data[i+3] = r*m15+g*m16+b*m17+a*m18+m19; // alpha
|
|
}
|
|
return true;
|
|
};
|
|
|
|
|
|
createjs.ColorMatrixFilter = createjs.promote(ColorMatrixFilter, "Filter");
|
|
}());
|
|
|
|
//##############################################################################
|
|
// Touch.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs||{};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
|
|
// constructor:
|
|
/**
|
|
* Global utility for working with multi-touch enabled devices in EaselJS. Currently supports W3C Touch API (iOS and
|
|
* modern Android browser) and the Pointer API (IE), including ms-prefixed events in IE10, and unprefixed in IE11.
|
|
*
|
|
* Ensure that you {{#crossLink "Touch/disable"}}{{/crossLink}} touch when cleaning up your application. You do not have
|
|
* to check if touch is supported to enable it, as it will fail gracefully if it is not supported.
|
|
*
|
|
* <h4>Example</h4>
|
|
*
|
|
* var stage = new createjs.Stage("canvasId");
|
|
* createjs.Touch.enable(stage);
|
|
*
|
|
* <strong>Note:</strong> It is important to disable Touch on a stage that you are no longer using:
|
|
*
|
|
* createjs.Touch.disable(stage);
|
|
*
|
|
* @class Touch
|
|
* @static
|
|
**/
|
|
function Touch() {
|
|
throw "Touch cannot be instantiated";
|
|
}
|
|
|
|
|
|
// public static methods:
|
|
/**
|
|
* Returns `true` if touch is supported in the current browser.
|
|
* @method isSupported
|
|
* @return {Boolean} Indicates whether touch is supported in the current browser.
|
|
* @static
|
|
**/
|
|
Touch.isSupported = function() {
|
|
return !!(('ontouchstart' in window) // iOS & Android
|
|
|| (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10
|
|
|| (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0)); // IE11+
|
|
};
|
|
|
|
/**
|
|
* Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS
|
|
* (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and
|
|
* multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for
|
|
* double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}}
|
|
* for more information.
|
|
* @method enable
|
|
* @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on.
|
|
* @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time.
|
|
* @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be
|
|
* allowed when the user is interacting with the target canvas.
|
|
* @return {Boolean} Returns `true` if touch was successfully enabled on the target stage.
|
|
* @static
|
|
**/
|
|
Touch.enable = function(stage, singleTouch, allowDefault) {
|
|
if (!stage || !stage.canvas || !Touch.isSupported()) { return false; }
|
|
if (stage.__touch) { return true; }
|
|
|
|
// inject required properties on stage:
|
|
stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0};
|
|
|
|
// note that in the future we may need to disable the standard mouse event model before adding
|
|
// these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though.
|
|
if ('ontouchstart' in window) { Touch._IOS_enable(stage); }
|
|
else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_enable(stage); }
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Removes all listeners that were set up when calling `Touch.enable()` on a stage.
|
|
* @method disable
|
|
* @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on.
|
|
* @static
|
|
**/
|
|
Touch.disable = function(stage) {
|
|
if (!stage) { return; }
|
|
if ('ontouchstart' in window) { Touch._IOS_disable(stage); }
|
|
else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_disable(stage); }
|
|
|
|
delete stage.__touch;
|
|
};
|
|
|
|
|
|
// Private static methods:
|
|
/**
|
|
* @method _IOS_enable
|
|
* @protected
|
|
* @param {Stage} stage
|
|
* @static
|
|
**/
|
|
Touch._IOS_enable = function(stage) {
|
|
var canvas = stage.canvas;
|
|
var f = stage.__touch.f = function(e) { Touch._IOS_handleEvent(stage,e); };
|
|
canvas.addEventListener("touchstart", f, false);
|
|
canvas.addEventListener("touchmove", f, false);
|
|
canvas.addEventListener("touchend", f, false);
|
|
canvas.addEventListener("touchcancel", f, false);
|
|
};
|
|
|
|
/**
|
|
* @method _IOS_disable
|
|
* @protected
|
|
* @param {Stage} stage
|
|
* @static
|
|
**/
|
|
Touch._IOS_disable = function(stage) {
|
|
var canvas = stage.canvas;
|
|
if (!canvas) { return; }
|
|
var f = stage.__touch.f;
|
|
canvas.removeEventListener("touchstart", f, false);
|
|
canvas.removeEventListener("touchmove", f, false);
|
|
canvas.removeEventListener("touchend", f, false);
|
|
canvas.removeEventListener("touchcancel", f, false);
|
|
};
|
|
|
|
/**
|
|
* @method _IOS_handleEvent
|
|
* @param {Stage} stage
|
|
* @param {Object} e The event to handle
|
|
* @protected
|
|
* @static
|
|
**/
|
|
Touch._IOS_handleEvent = function(stage, e) {
|
|
if (!stage) { return; }
|
|
if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); }
|
|
var touches = e.changedTouches;
|
|
var type = e.type;
|
|
for (var i= 0,l=touches.length; i<l; i++) {
|
|
var touch = touches[i];
|
|
var id = touch.identifier;
|
|
if (touch.target != stage.canvas) { continue; }
|
|
|
|
if (type == "touchstart") {
|
|
this._handleStart(stage, id, e, touch.pageX, touch.pageY);
|
|
} else if (type == "touchmove") {
|
|
this._handleMove(stage, id, e, touch.pageX, touch.pageY);
|
|
} else if (type == "touchend" || type == "touchcancel") {
|
|
this._handleEnd(stage, id, e);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _IE_enable
|
|
* @protected
|
|
* @param {Stage} stage
|
|
* @static
|
|
**/
|
|
Touch._IE_enable = function(stage) {
|
|
var canvas = stage.canvas;
|
|
var f = stage.__touch.f = function(e) { Touch._IE_handleEvent(stage,e); };
|
|
|
|
if (window.navigator["pointerEnabled"] === undefined) {
|
|
canvas.addEventListener("MSPointerDown", f, false);
|
|
window.addEventListener("MSPointerMove", f, false);
|
|
window.addEventListener("MSPointerUp", f, false);
|
|
window.addEventListener("MSPointerCancel", f, false);
|
|
if (stage.__touch.preventDefault) { canvas.style.msTouchAction = "none"; }
|
|
} else {
|
|
canvas.addEventListener("pointerdown", f, false);
|
|
window.addEventListener("pointermove", f, false);
|
|
window.addEventListener("pointerup", f, false);
|
|
window.addEventListener("pointercancel", f, false);
|
|
if (stage.__touch.preventDefault) { canvas.style.touchAction = "none"; }
|
|
|
|
}
|
|
stage.__touch.activeIDs = {};
|
|
};
|
|
|
|
/**
|
|
* @method _IE_disable
|
|
* @protected
|
|
* @param {Stage} stage
|
|
* @static
|
|
**/
|
|
Touch._IE_disable = function(stage) {
|
|
var f = stage.__touch.f;
|
|
|
|
if (window.navigator["pointerEnabled"] === undefined) {
|
|
window.removeEventListener("MSPointerMove", f, false);
|
|
window.removeEventListener("MSPointerUp", f, false);
|
|
window.removeEventListener("MSPointerCancel", f, false);
|
|
if (stage.canvas) {
|
|
stage.canvas.removeEventListener("MSPointerDown", f, false);
|
|
}
|
|
} else {
|
|
window.removeEventListener("pointermove", f, false);
|
|
window.removeEventListener("pointerup", f, false);
|
|
window.removeEventListener("pointercancel", f, false);
|
|
if (stage.canvas) {
|
|
stage.canvas.removeEventListener("pointerdown", f, false);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _IE_handleEvent
|
|
* @param {Stage} stage
|
|
* @param {Object} e The event to handle.
|
|
* @protected
|
|
* @static
|
|
**/
|
|
Touch._IE_handleEvent = function(stage, e) {
|
|
if (!stage) { return; }
|
|
if (stage.__touch.preventDefault) { e.preventDefault && e.preventDefault(); }
|
|
var type = e.type;
|
|
var id = e.pointerId;
|
|
var ids = stage.__touch.activeIDs;
|
|
|
|
if (type == "MSPointerDown" || type == "pointerdown") {
|
|
if (e.srcElement != stage.canvas) { return; }
|
|
ids[id] = true;
|
|
this._handleStart(stage, id, e, e.pageX, e.pageY);
|
|
} else if (ids[id]) { // it's an id we're watching
|
|
if (type == "MSPointerMove" || type == "pointermove") {
|
|
this._handleMove(stage, id, e, e.pageX, e.pageY);
|
|
} else if (type == "MSPointerUp" || type == "MSPointerCancel"
|
|
|| type == "pointerup" || type == "pointercancel") {
|
|
delete(ids[id]);
|
|
this._handleEnd(stage, id, e);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @method _handleStart
|
|
* @param {Stage} stage
|
|
* @param {String|Number} id
|
|
* @param {Object} e
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @protected
|
|
**/
|
|
Touch._handleStart = function(stage, id, e, x, y) {
|
|
var props = stage.__touch;
|
|
if (!props.multitouch && props.count) { return; }
|
|
var ids = props.pointers;
|
|
if (ids[id]) { return; }
|
|
ids[id] = true;
|
|
props.count++;
|
|
stage._handlePointerDown(id, e, x, y);
|
|
};
|
|
|
|
/**
|
|
* @method _handleMove
|
|
* @param {Stage} stage
|
|
* @param {String|Number} id
|
|
* @param {Object} e
|
|
* @param {Number} x
|
|
* @param {Number} y
|
|
* @protected
|
|
**/
|
|
Touch._handleMove = function(stage, id, e, x, y) {
|
|
if (!stage.__touch.pointers[id]) { return; }
|
|
stage._handlePointerMove(id, e, x, y);
|
|
};
|
|
|
|
/**
|
|
* @method _handleEnd
|
|
* @param {Stage} stage
|
|
* @param {String|Number} id
|
|
* @param {Object} e
|
|
* @protected
|
|
**/
|
|
Touch._handleEnd = function(stage, id, e) {
|
|
// TODO: cancel should be handled differently for proper UI (ex. an up would trigger a click, a cancel would more closely resemble an out).
|
|
var props = stage.__touch;
|
|
var ids = props.pointers;
|
|
if (!ids[id]) { return; }
|
|
props.count--;
|
|
stage._handlePointerUp(id, e, true);
|
|
delete(ids[id]);
|
|
};
|
|
|
|
|
|
createjs.Touch = Touch;
|
|
}());
|
|
|
|
//##############################################################################
|
|
// version.js
|
|
//##############################################################################
|
|
|
|
this.createjs = this.createjs || {};
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
/**
|
|
* Static class holding library specific information such as the version and buildDate of
|
|
* the library.
|
|
* @class EaselJS
|
|
**/
|
|
var s = createjs.EaselJS = createjs.EaselJS || {};
|
|
|
|
/**
|
|
* The version string for this release.
|
|
* @property version
|
|
* @type String
|
|
* @static
|
|
**/
|
|
s.version = /*=version*/""; // injected by build process
|
|
|
|
/**
|
|
* The build date for this release in UTC format.
|
|
* @property buildDate
|
|
* @type String
|
|
* @static
|
|
**/
|
|
s.buildDate = /*=date*/""; // injected by build process
|
|
|
|
})(); |