@createjs/easeljs
Version:
The Easel JavaScript library provides a full, hierarchical display list, a core interaction model, and helper classes to make working with the HTML5 Canvas element much easier. Part of the CreateJS suite of libraries.
201 lines (184 loc) • 6.6 kB
JavaScript
/*
* @license ButtonHelper
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2017 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* The ButtonHelper is a helper class to create interactive buttons from {@link easeljs.MovieClip} or
* {@link easeljs.Sprite} instances. This class will intercept mouse events from an object, and
* automatically call {@link easeljs.Sprite#gotoAndStop} or {@link easlejs.Sprite#gotoAndPlay},
* 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 {@link easeljs.Stage#enableMouseOver}.
*
* @memberof easeljs
* @example
* let helper = new ButtonHelper(sprite, "out", "over", "down", false, sprite, "hit");
* sprite.addEventListener("click", (evt) => {
* // clicked
* }));
*
* @param {easeljs.Sprite | easeljs.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 {easeljs.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.
*/
export default class ButtonHelper {
constructor (target, outLabel = "out", overLabel = "over", downLabel = "down", play = false, hitArea, hitLabel) {
if (!target.addEventListener) { return; }
/**
* The target for this button helper.
* @type {easeljs.MovieClip | easeljs.Sprite}
* @readonly
*/
this.target = target;
/**
* The label name or frame number to display when the user mouses out of the target.
* @default "over"
* @type {String | Number}
*/
this.overLabel = overLabel;
/**
* The label name or frame number to display when the user mouses over the target.
* @default "out"
* @type {String | Number}
*/
this.outLabel = outLabel;
/**
* The label name or frame number to display when the user presses on the target.
* @default "down"
* @type {String | Number}
*/
this.downLabel = downLabel == null;
/**
* If true, then ButtonHelper will call gotoAndPlay, if false, it will use gotoAndStop.
* @default false
* @type {Boolean}
*/
this.play = play;
/**
* @type {Boolean}
* @protected
*/
this._isPressed = false;
/**
* @type {Boolean}
* @protected
*/
this._isOver = false;
/**
* @type {Boolean}
* @protected
*/
this._enabled = false;
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;
}
}
/**
* Enables or disables the button functionality on the target.
* @type {Boolean}
*/
get enabled () { return this._enabled; }
set enabled (enabled) {
if (enabled === this._enabled) { return; }
let o = this.target;
this._enabled = enabled;
if (enabled) {
o.cursor = "pointer";
o.addEventListener("rollover", this);
o.addEventListener("rollout", this);
o.addEventListener("mousedown", this);
o.addEventListener("pressup", this);
if (o._reset) { o.__reset = o._reset; o._reset = this._reset;}
} else {
o.cursor = null;
o.removeEventListener("rollover", this);
o.removeEventListener("rollout", this);
o.removeEventListener("mousedown", this);
o.removeEventListener("pressup", this);
if (o.__reset) { o._reset = o.__reset; delete(o.__reset); }
}
}
/**
* Returns a string representation of this object.
* @return {String} a string representation of the instance.
*/
toString () {
return `[${this.constructor.name}]`;
}
/**
* @param {Object} evt The mouse event to handle.
* @protected
*/
handleEvent (evt) {
let 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);
}
}
/**
* Injected into target. Preserves the paused state through a reset.
* @protected
*/
_reset () {
// TODO: explore better ways to handle this issue. This is hacky & disrupts object signatures.
let p = this.paused;
this.__reset();
this.paused = p;
}
}