UNPKG

playable.js

Version:

A lightweight HTML5 game engine.

1,740 lines (1,720 loc) 107 kB
class Event { constructor(type, data = null) { this.type = null; this.data = null; this.target = null; this.currentTarget = null; this.$init(type, data); } $init(type, data = null) { this.type = type; this.data = data; return this; } release() { this.type = null; this.data = null; Event.recycle(this); } static create(type, data = null) { let pool = this.$pool; if (pool.length > 0) { return pool.pop().$init(type, data); } else { return new Event(type, data); } } static recycle(e) { this.$pool.push(e); } } /** @event added */ Event.ADDED = 'added'; /** @event removed */ Event.REMOVED = 'removed'; /** @event addedToStage */ Event.ADDED_TO_STAGE = 'addedToStage'; /** @event removeFromStage */ Event.REMOVED_FROM_STAGE = 'removeFromStage'; /** @event activate */ Event.ACTIVATE = 'activate'; /** @event deactivate */ Event.DEACTIVATE = 'deactivate'; /** @event enterFrame */ Event.ENTER_FRAME = 'enterFrame'; /** @event tick */ Event.TICK = 'tick'; /** @event tickerPause */ Event.TICKER_PAUSE = 'tickerPause'; /** @event tickerResume */ Event.TICKER_RESUME = 'tickerResume'; /** @event viewportResize */ Event.VIEWPORT_RESIZE = 'viewportResize'; /** @event load */ Event.LOAD = 'load'; /** @event abort */ Event.ABORT = 'abort'; /** @event error */ Event.ERROR = 'error'; /** @event progress */ Event.PROGRESS = 'progress'; /** @event complete */ Event.COMPLETE = 'complete'; /** @event ended */ Event.ENDED = 'ended'; /** @event soundComplete */ Event.SOUND_COMPLETE = 'soundComplete'; Event.$pool = []; var Event$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Event: Event }); class EventEmitter { constructor() { this.$events = {}; this.$emittingType = null; this.$removedListeners = []; } on(type, listener) { let listeners = this.$events[type] || []; listeners.push(listener); this.$events[type] = listeners; return this; } off(type, listener) { if (this.$emittingType === type && listener) { this.$removedListeners.push(listener); } else { let listeners = this.$events[type] || []; if (listener) { let index = listeners.indexOf(listener); if (index >= 0) { listeners.splice(index, 1); } } else { listeners.length = 0; } } return this; } once(type, listener) { let that = this; let wrapper = function () { listener.apply(this, arguments); that.off(type, wrapper); }; return this.on(type, wrapper); } emit(type, ...args) { let event; if (type instanceof Event) { args = [type]; type.target = type.target || this; type.currentTarget = this; type = type.type; } let listeners = this.$events[type]; let hasListeners = listeners && listeners.length > 0; let removedListeners = this.$removedListeners; if (!event && hasListeners && args.length === 0) { event = Event.create(type); event.target = this; event.currentTarget = this; args.push(event); } if (hasListeners) { this.$emittingType = type; for (let listener of listeners) { if (removedListeners.indexOf(listener) < 0) { listener.apply(this, args); } } this.$emittingType = null; } if (event) { event.release(); } for (let listener of removedListeners) { this.off(type, listener); } removedListeners.length = 0; return hasListeners; } hasEventListener(type) { let listeners = this.$events[type]; return !!listeners && listeners.length > 0; } removeAllListeners() { this.$events = {}; return this; } } var EventEmitter$1 = /*#__PURE__*/Object.freeze({ __proto__: null, EventEmitter: EventEmitter }); class Ticker extends EventEmitter { constructor(stage) { super(); this.$stage = null; this.$fps = 0; this.$deltaTime = 0; this.$paused = true; this.$shouldResume = false; this.$timerIndex = 0; this.$lastTimestamp = 0; this.$tickHandle = null; this.$stage = stage; this.$timers = []; this.$boundTick = this.$tick.bind(this); this.$enterFrameCallbackList = [stage]; this.$start(); } get fps() { return this.$fps; } get deltaTime() { return this.$deltaTime; } get paused() { return this.$paused; } $start() { let stage = this.$stage; let prefixes = ['', 'o', 'ms', 'moz', 'webkit']; for (let prefix of prefixes) { let requestKey = prefix ? prefix + 'RequestAnimationFrame' : 'requestAnimationFrame'; let cancelKey = prefix ? prefix + 'CancelAnimationFrame' : 'cancelAnimationFrame'; let cancelRequestKey = prefix ? prefix + 'CancelRequestAnimationFrame' : 'cancelRequestAnimationFrame'; window.requestAnimationFrame = window.requestAnimationFrame || window[requestKey]; window.cancelAnimationFrame = window.cancelAnimationFrame || window[cancelKey] || window[cancelRequestKey]; } if (!window.requestAnimationFrame) { window.requestAnimationFrame = function (callback) { return window.setTimeout(callback, 1000 / 60); }; window.cancelAnimationFrame = function (handle) { window.clearTimeout(handle); }; } stage.on(Event.ACTIVATE, () => { if (this.$shouldResume) { this.resume(); this.$shouldResume = false; } }); stage.on(Event.DEACTIVATE, () => { if (!this.$paused) { this.pause(); this.$shouldResume = true; } }); if (stage.activated) { this.$paused = false; this.$tick(); } else { this.$shouldResume = true; } return this; } pause() { if (!this.$paused) { this.$paused = true; this.$lastTimestamp = 0; this.emit(Event.TICKER_PAUSE); cancelAnimationFrame(this.$tickHandle); } return this; } resume() { if (this.$paused) { this.$paused = false; this.$tick(); this.emit(Event.TICKER_RESUME); } return this; } setTimeout(handler, timeout = 0) { let handle = ++this.$timerIndex; this.$timers[handle] = { handler, timeout, resetTime: timeout }; return handle; } clearTimeout(handle) { delete this.$timers[handle]; } setInterval(handler, timeout) { let handle = ++this.$timerIndex; this.$timers[handle] = { handler, timeout, resetTime: timeout, interval: true }; return handle; } clearInterval(handle) { delete this.$timers[handle]; } registerEnterFrameCallback(layer) { let list = this.$enterFrameCallbackList; if (list.indexOf(layer) < 0) { list.push(layer); } return this; } unregisterEnterFrameCallback(layer) { let list = this.$enterFrameCallbackList; let index = list.indexOf(layer); if (index >= 0) { list.splice(index, 1); } return this; } $tick() { let now = Date.now(); let lastTimestamp = this.$lastTimestamp; let deltaTime = lastTimestamp ? now - this.$lastTimestamp : 1000 / 60; let enterFrameCallbackList = this.$enterFrameCallbackList; this.$fps = Math.round(1000 / deltaTime); this.$deltaTime = deltaTime; this.$lastTimestamp = now; this.$tickHandle = window.requestAnimationFrame(this.$boundTick); this.$checkTimers(deltaTime); let event = Event.create(Event.ENTER_FRAME, deltaTime); for (let layer of enterFrameCallbackList) { layer.emit(event); } event.release(); let tickEvent = Event.create(Event.TICK, deltaTime); this.emit(tickEvent); tickEvent.release(); } $checkTimers(dt) { let timers = this.$timers; for (let key in timers) { let timer = timers[key]; let restTime = timer.resetTime = timer.resetTime - dt; if (restTime <= 0) { timer.handler(); if (timer.interval) { timer.resetTime += timer.timeout; } else { this.clearTimeout(+key); } } } } } var Ticker$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Ticker: Ticker }); class Vector { constructor(x, y) { this.set(x, y); } get length() { return Math.sqrt(this.x * this.x + this.y * this.y); } get angle() { return Math.atan2(this.y, this.x); } set(x, y) { this.x = x || 0; this.y = y || 0; return this; } add(x, y) { if (x instanceof Vector) { this.x += x.x; this.y += x.y; } else { this.x += x; this.y += y; } return this; } subtract(x, y) { if (x instanceof Vector) { this.x -= x.x; this.y -= x.y; } else { this.x -= x; this.y -= y; } return this; } dotProduct(x, y) { if (x instanceof Vector) { return this.x * x.x + this.y * x.y; } else { return this.x * x + this.y * y; } } normalize() { let length = this.length; this.x = this.x / length; this.y = this.y / length; return this; } negate() { this.x *= -1; this.y *= -1; return this; } scale(x, y) { this.x *= x; this.y *= y === undefined ? x : y; return this; } rotate(angle) { let x = this.x; let y = this.y; this.x = x * Math.cos(angle) - y * Math.sin(angle); this.y = x * Math.sin(angle) + y * Math.cos(angle); return this; } transform(m) { let x = this.x; let y = this.y; this.x = m.a * x + m.c * y + m.tx; this.y = m.b * x + m.d * y + m.ty; return this; } distance(v) { return Math.sqrt((this.x - v.x) * (this.x - v.x) + (this.y - v.y) * (this.y - v.y)); } equal(v) { return this.x === v.x && this.y === v.y; } release() { Vector.recycle(this); return this; } static create(x, y) { let pool = this.$pool; if (pool.length > 0) { return pool.pop().set(x, y); } else { return new Vector(x, y); } } static recycle(v) { this.$pool.push(v); } } Vector.$pool = []; var Vector$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Vector: Vector }); /** * ``` * {a b 0} * (x, y, 1) * {c d 0} = (ax + cy + tx, bx + dy + ty, 1) * {tx ty 1} * ``` */ class Matrix { constructor(a, b, c, d, tx, ty) { if (arguments.length > 0) { this.set(a, b, c, d, tx, ty); } else { this.identity(); } } set(a, b, c, d, tx, ty) { this.a = a; this.b = b; this.c = c; this.d = d; this.tx = tx; this.ty = ty; return this; } identity() { return this.set(1, 0, 0, 1, 0, 0); } invert() { let a = this.a; let b = this.b; let c = this.c; let d = this.d; let tx = this.tx; let ty = this.ty; let n = a * d - c * b; this.a = d / n; this.b = -b / n; this.c = -c / n; this.d = a / n; this.tx = (c * ty - d * tx) / n; this.ty = (b * tx - a * ty) / n; return this; } prepend(a, b, c, d, tx, ty) { if (a instanceof Matrix) { return this.prepend(a.a, a.b, a.c, a.d, a.tx, a.ty); } let a1 = this.a; let b1 = this.b; let c1 = this.c; let d1 = this.d; let tx1 = this.tx; let ty1 = this.ty; 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 + tx1; this.ty = tx * b1 + ty * d1 + ty1; return this; } append(a, b, c, d, tx, ty) { if (a instanceof Matrix) { return this.append(a.a, a.b, a.c, a.d, a.tx, a.ty); } let a1 = this.a; let b1 = this.b; let c1 = this.c; let d1 = this.d; let tx1 = this.tx; let ty1 = this.ty; this.a = a * a1 + c * b1; this.b = b * a1 + d * b1; this.c = a * c1 + c * d1; this.d = b * c1 + d * d1; this.tx = a * tx1 + c * ty1 + tx; this.ty = b * tx1 + d * ty1 + ty; return this; } scale(x, y) { return this.append(x, 0, 0, y === undefined ? x : y, 0, 0); } rotate(angle) { let sin = Math.sin(angle); let cos = Math.cos(angle); return this.append(cos, sin, -sin, cos, 0, 0); } skew(skewX, skewY) { return this.append(1, Math.tan(skewY), Math.tan(skewX), 1, 0, 0); } translate(x, y) { if (x instanceof Vector) { return this.append(1, 0, 0, 1, x.x, x.y); } return this.append(1, 0, 0, 1, x, y); } equal(m) { return m instanceof Matrix && this.a === m.a && this.b === m.b && this.c === m.c && this.d === m.d && this.tx === m.tx && this.ty === m.ty; } release() { Matrix.recycle(this); } static create(a, b, c, d, tx, ty) { let m; let pool = this.$pool; if (pool.length > 0) { m = pool.pop(); } else { m = new Matrix(); } if (arguments.length) { m.set(a, b, c, d, tx, ty); } else { m.identity(); } return m; } static recycle(m) { this.$pool.push(m); } } Matrix.$pool = []; var Matrix$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Matrix: Matrix }); class Rectangle { constructor(x, y, width, height) { this.set(x, y, width, height); } get top() { return this.y; } set top(top) { this.height += this.y - top; this.y = top; } get bottom() { return this.y + this.height; } set bottom(bottom) { this.height = bottom - this.y; } get left() { return this.x; } set left(left) { this.width += this.x - left; this.x = left; } get right() { return this.x + this.width; } set right(right) { this.width = right - this.x; } get topLeft() { return Vector.create(this.left, this.top); } set topLeft(v) { this.top = v.y; this.left = v.x; } get bottomRight() { return Vector.create(this.right, this.bottom); } set bottomRight(v) { this.bottom = v.y; this.right = v.x; } set(x, y, width, height) { this.x = x || 0; this.y = y || 0; this.width = width || 0; this.height = height || 0; return this; } contains(x, y) { if (x instanceof Vector) { return this.contains(x.x, x.y); } return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height; } equal(r) { return r instanceof Rectangle && r.x === this.x && r.y === this.y && r.width === this.width && r.height === this.height; } release() { Rectangle.recycle(this); } static create(x, y, width, height) { let pool = this.$pool; if (pool.length > 0) { return pool.pop().set(x, y, width, height); } else { return new Rectangle(x, y, width, height); } } static recycle(r) { this.$pool.push(r); } } Rectangle.$pool = []; var Rectangle$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Rectangle: Rectangle }); class TouchEvent extends Event { constructor(type) { super(type); this.$init(type); } $init(type) { this.type = type; this.targetX = 0; this.targetY = 0; this.localX = 0; this.localY = 0; this.stageX = 0; this.stageY = 0; this.identifier = 0; this.target = null; this.currentTarget = null; this.cancelBubble = false; return this; } stopPropagation() { this.cancelBubble = true; } release() { TouchEvent.recycle(this); } static create(type) { let pool = this.$pool; if (pool.length > 0) { return pool.pop().$init(type); } else { return new TouchEvent(type); } } static recycle(e) { this.$pool.push(e); } } /** @event touchStart */ TouchEvent.TOUCH_START = 'touchStart'; /** @event touchMove */ TouchEvent.TOUCH_MOVE = 'touchMove'; /** @event touchEnd */ TouchEvent.TOUCH_END = 'touchEnd'; /** @event touchCancel */ TouchEvent.TOUCH_CANCEL = 'touchCancel'; /** @event touchTap */ TouchEvent.TOUCH_TAP = 'touchTap'; TouchEvent.$pool = []; var TouchEvent$1 = /*#__PURE__*/Object.freeze({ __proto__: null, TouchEvent: TouchEvent }); class Layer extends EventEmitter { constructor() { super(); this.name = ''; this.tag = ''; this.touchable = true; this.$x = 0; this.$y = 0; this.$width = 0; this.$height = 0; this.$anchorX = 0; this.$anchorY = 0; this.$skewX = 0; this.$skewY = 0; this.$scaleX = 1; this.$scaleY = 1; this.$rotation = 0; this.$alpha = 1; this.$visible = true; this.$smoothing = true; this.$background = null; this.$stage = null; this.$parent = null; this.$children = []; this.$dirty = true; this.$shouldEmitTap = true; this.$touches = []; this.$canvas = document.createElement('canvas'); this.$context = this.$canvas.getContext('2d'); } get x() { return this.$x; } set x(x) { if (this.$x !== x) { this.$x = x; this.$markParentDirty(); } } get y() { return this.$y; } set y(y) { if (this.$y !== y) { this.$y = y; this.$markParentDirty(); } } get width() { return this.$width ? this.$width : this.$canvas.width / Layer.pixelRatio; } set width(width) { if (this.$width !== width) { this.$width = width; this.$resizeCanvas(); } } get height() { return this.$height ? this.$height : this.$canvas.height / Layer.pixelRatio; } set height(height) { if (this.$height !== height) { this.$height = height; this.$resizeCanvas(); } } get anchorX() { return this.$anchorX; } set anchorX(anchorX) { if (this.$anchorX !== anchorX) { this.$anchorX = anchorX; this.$resizeCanvas(); } } get anchorY() { return this.$anchorY; } set anchorY(anchorY) { if (this.$anchorY !== anchorY) { this.$anchorY = anchorY; this.$resizeCanvas(); } } get skewX() { return this.$skewX; } set skewX(skewX) { if (this.$skewX !== skewX) { this.$skewX = skewX; this.$markParentDirty(); } } get skewY() { return this.$skewY; } set skewY(skewY) { if (this.$skewY !== skewY) { this.$skewY = skewY; this.$markParentDirty(); } } get scaleX() { return this.$scaleX; } set scaleX(scaleX) { if (this.$scaleX !== scaleX) { this.$scaleX = scaleX; this.$markParentDirty(); } } get scaleY() { return this.$scaleY; } set scaleY(scaleY) { if (this.$scaleY !== scaleY) { this.$scaleY = scaleY; this.$markParentDirty(); } } get rotation() { return this.$rotation; } set rotation(rotation) { if (this.$rotation !== rotation) { this.$rotation = rotation; this.$markParentDirty(); } } get alpha() { return this.$alpha; } set alpha(alpha) { if (this.$alpha !== alpha) { this.$alpha = alpha; this.$markParentDirty(); } } get visible() { return this.$visible; } set visible(visible) { if (this.$visible !== visible) { this.$visible = visible; this.$markParentDirty(); } } get smoothing() { return this.$smoothing; } set smoothing(smoothing) { this.$smoothing = smoothing; this.$resizeCanvas(); } get background() { return this.$background; } set background(background) { if (this.$background !== background) { this.$background = background; this.$markDirty(); } } get stage() { return this.$stage; } get parent() { return this.$parent; } get numChildren() { return this.$children.length; } get ticker() { return this.$stage ? this.$stage.ticker : null; } get canvas() { return this.$canvas; } addChild(child) { return this.addChildAt(child, this.$children.length); } addChildAt(child, index) { let children = this.$children; if (child.$parent) { child.$parent.removeChild(child); } if (index < 0 || index > children.length) { index = children.length; } child.$emitAdded(this); children.splice(index, 0, child); this.$resizeCanvas(); return this; } replaceChild(oldChild, newChild) { let index = this.getChildIndex(oldChild); this.removeChildAt(index); this.addChildAt(newChild, index); return this; } getChildByName(name) { let children = this.$children; for (let child of children) { if (child.name === name) { return child; } } return null; } getChildrenByTag(tag) { let result = []; let children = this.$children; for (let child of children) { if (child.tag === tag) { result.push(child); } } return result; } getChildAt(index) { return this.$children[index] || null; } getChildIndex(child) { return this.$children.indexOf(child); } hasChild(child) { return this.getChildIndex(child) >= 0; } swapChildren(child1, child2) { let index1 = this.getChildIndex(child1); let index2 = this.getChildIndex(child2); if (index1 >= 0 && index2 >= 0) { this.swapChildrenAt(index1, index2); } return this; } swapChildrenAt(index1, index2) { let child1 = this.$children[index1]; let child2 = this.$children[index2]; if (index1 !== index2 && child1 && child2) { this.$children[index1] = child2; this.$children[index2] = child1; this.$markDirty(); } return this; } setChildIndex(child, index) { let children = this.$children; let oldIndex = this.getChildIndex(child); if (index < 0) { index = 0; } else if (index > children.length) { index = children.length; } if (oldIndex >= 0 && index > oldIndex) { for (let i = oldIndex + 1; i <= index; ++i) { children[i - 1] = children[i]; } children[index] = child; this.$markDirty(); } else if (oldIndex >= 0 && index < oldIndex) { for (let i = oldIndex - 1; i >= index; --i) { children[i + 1] = children[i]; } children[index] = child; this.$markDirty(); } return this; } removeChild(child) { let index = this.getChildIndex(child); return this.removeChildAt(index); } removeChildAt(index) { let children = this.$children; let child = children[index]; if (child) { children.splice(index, 1); child.$emitRemoved(); this.$resizeCanvas(); } return this; } removeChildByName(name) { let children = this.$children; for (let i = 0, l = children.length; i < l; ++i) { let child = children[i]; if (child.name === name) { this.removeChildAt(i); break; } } return this; } removeChildrenByTag(tag) { let children = this.$children; for (let i = children.length - 1; i >= 0; --i) { let child = children[i]; if (child.tag === tag) { this.removeChildAt(i); } } return this; } removeAllChildren() { let children = this.$children; for (let child of children) { child.$emitRemoved(); } this.$children.length = 0; this.$resizeCanvas(); return this; } removeSelf() { if (this.$parent) { this.$parent.removeChild(this); } return this; } $markDirty(sizeDirty) { if (sizeDirty) { this.$resizeParentCanvas(); } else if (!this.$dirty) { this.$markParentDirty(); } this.$dirty = true; } $markParentDirty() { if (this.$parent) { this.$parent.$markDirty(); } } $resizeCanvas() { let width = this.$width; let height = this.$height; let canvas = this.$canvas; let anchorX = this.$anchorX; let anchorY = this.$anchorY; let context = this.$context; let smoothing = this.$smoothing; let pixelRatio = Layer.pixelRatio; if (width && height) { canvas.width = width * pixelRatio; canvas.height = height * pixelRatio; } else { let bounds = this.$getContentBounds(); canvas.width = (width || bounds.right + anchorX) * pixelRatio; canvas.height = (height || bounds.bottom + anchorY) * pixelRatio; bounds.release(); } if (context.imageSmoothingEnabled !== smoothing) { context.imageSmoothingEnabled = smoothing; } this.$markDirty(true); } $resizeParentCanvas() { if (this.$parent) { this.$parent.$resizeCanvas(); } } $getTransform() { let degToRad = Math.PI / 180; let matrix = Matrix.create(); matrix.translate(-this.$anchorX, -this.$anchorY); matrix.skew(this.skewX * degToRad, this.skewY * degToRad); matrix.rotate(this.rotation * degToRad); matrix.scale(this.scaleX, this.scaleY); matrix.translate(this.x, this.y); return matrix; } $getChildTransform(child) { return child.$getTransform(); } $getChildBounds(child) { let width = child.width; let height = child.height; let bounds = Rectangle.create(); let matrix = this.$getChildTransform(child); let topLeft = Vector.create(0, 0).transform(matrix); let topRight = Vector.create(width, 0).transform(matrix); let bottomLeft = Vector.create(0, height).transform(matrix); let bottomRight = Vector.create(width, height).transform(matrix); let minX = Math.min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); let maxX = Math.max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); let minY = Math.min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); let maxY = Math.max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); bounds.top = minY; bounds.bottom = maxY; bounds.left = minX; bounds.right = maxX; matrix.release(); topLeft.release(); topRight.release(); bottomLeft.release(); bottomRight.release(); return bounds; } $getContentBounds() { let bounds; let children = this.$children; for (let child of children) { if (child.$visible) { let childBounds = this.$getChildBounds(child); if (bounds) { bounds.top = Math.min(bounds.top, childBounds.top); bounds.bottom = Math.max(bounds.bottom, childBounds.bottom); bounds.left = Math.min(bounds.left, childBounds.left); bounds.right = Math.max(bounds.right, childBounds.right); childBounds.release(); } else { bounds = childBounds; } } } bounds = bounds || Rectangle.create(); return bounds; } $emitTouchEvent(event, inside) { let type = event.type; let localX = event.localX; let localY = event.localY; let touches = this.$touches; let identifier = event.identifier; if (type === TouchEvent.TOUCH_START) { this.$shouldEmitTap = true; touches[identifier] = true; } else if (!touches[identifier]) { return false; } else if (type === TouchEvent.TOUCH_TAP || type === TouchEvent.TOUCH_CANCEL) { touches[identifier] = false; } if (type === TouchEvent.TOUCH_MOVE) { this.$shouldEmitTap = false; } let children = this.$children; for (let i = children.length - 1; i >= 0; --i) { let child = children[i]; if (!child.$visible || !child.touchable) { continue; } let matrix = this.$getChildTransform(child); let localPos = Vector.create(localX, localY).transform(matrix.invert()).subtract(child.$anchorX, child.$anchorY); let inside = child.$localHitTest(localPos); localPos.release(); matrix.release(); if (inside || type !== TouchEvent.TOUCH_START) { event.target = child; event.localX = event.targetX = localPos.x; event.localY = event.targetY = localPos.y; if (child.$emitTouchEvent(event, inside)) { break; } } } if (type === TouchEvent.TOUCH_TAP && (!inside || !this.$shouldEmitTap)) { return true; } if (!event.cancelBubble) { event.localX = localX; event.localY = localY; this.emit(event); } return true; } $emitAdded(parent) { let stage = parent.$stage; this.$parent = parent; this.emit(Event.ADDED); if (stage) { this.$emitAddedToStage(stage); } } $emitRemoved() { let stage = this.$stage; this.$parent = null; this.emit(Event.REMOVED); if (stage) { this.$emitRemovedFromStage(); } } $emitAddedToStage(stage) { let children = this.$children; this.$stage = stage; this.emit(Event.ADDED_TO_STAGE); if (this.hasEventListener(Event.ENTER_FRAME)) { stage.ticker.registerEnterFrameCallback(this); } for (let child of children) { child.$emitAddedToStage(stage); } } $emitRemovedFromStage() { let stage = this.$stage; let children = this.$children; this.$stage = null; this.emit(Event.REMOVED_FROM_STAGE); if (this.hasEventListener(Event.ENTER_FRAME)) { stage.ticker.unregisterEnterFrameCallback(this); } for (let child of children) { child.$emitRemovedFromStage(); } } $localHitTest(vector) { return vector.x >= -this.anchorX && vector.x <= this.width - this.anchorX && vector.y >= -this.anchorY && vector.y <= this.height - this.anchorY; } $isChildVisible(child) { if (!child.visible || !child.alpha || !child.width || !child.height) { return false; } let minX = -this.$anchorX; let maxX = this.width + minX; let minY = -this.$anchorY; let maxY = this.height + minY; let bounds = this.$getChildBounds(child); let inside = bounds.left <= maxX && bounds.right >= minX && bounds.top <= maxY && bounds.bottom >= minY; bounds.release(); return inside; } $drawChild(child) { let ctx = this.$context; let canvas = child.$canvas; let width = child.width; let height = child.height; let pixelRatio = Layer.pixelRatio; let matrix = this.$getChildTransform(child).scale(pixelRatio); let drawCalls = child.$render(); let globalAlpha = ctx.globalAlpha; if (globalAlpha !== child.alpha) { ctx.globalAlpha = child.alpha; } if (matrix.b === 0 && matrix.c === 0) { let tx = (matrix.tx + 0.5) | 0; let ty = (matrix.ty + 0.5) | 0; width = (width * matrix.a) + 0.5 | 0; height = (height * matrix.d) + 0.5 | 0; ctx.drawImage(canvas, tx, ty, width, height); } else { ctx.save(); ctx.transform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); ctx.drawImage(canvas, 0, 0, width, height); ctx.restore(); } if (globalAlpha !== child.alpha) { ctx.globalAlpha = globalAlpha; } matrix.release(); return drawCalls + 1; } $render() { if (!this.$dirty) { return 0; } let drawCalls = 0; let ctx = this.$context; let canvas = this.$canvas; let children = this.$children; let canvasWidth = canvas.width; let canvasHeight = canvas.height; let anchorX = (this.$anchorX + 0.5) | 0; let anchorY = (this.$anchorY + 0.5) | 0; let background = this.$background; let pixelRatio = Layer.pixelRatio; ctx.globalAlpha = 1; ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, canvasWidth, canvasHeight); if (background) { ctx.fillStyle = background; ctx.fillRect(0, 0, canvasWidth, canvasHeight); } ctx.translate(anchorX * pixelRatio, anchorY * pixelRatio); for (let child of children) { if (this.$isChildVisible(child)) { drawCalls += this.$drawChild(child); } } this.$dirty = false; return drawCalls; } on(type, listener) { super.on(type, listener); if (type === Event.ENTER_FRAME && this.ticker) { this.ticker.registerEnterFrameCallback(this); } else if (type === Event.ADDED && this.$parent) { let event = Event.create(type); listener.call(this, event); event.release(); } else if (type === Event.ADDED_TO_STAGE && this.$stage) { let event = Event.create(type); listener.call(this, event); event.release(); } return this; } off(type, listener) { super.off(type, listener); if (type === Event.ENTER_FRAME && !this.hasEventListener(Event.ENTER_FRAME) && this.ticker) { this.ticker.unregisterEnterFrameCallback(this); } return this; } } Layer.pixelRatio = typeof window === 'undefined' ? 1 : window.devicePixelRatio || 1; var Layer$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Layer: Layer }); class Ease { static linear(t, b, c, d) { return c * t / d + b; } static easeInQuad(t, b, c, d) { return c * (t /= d) * t + b; } static easeOutQuad(t, b, c, d) { return -c * (t /= d) * (t - 2) + b; } static easeInOutQuad(t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t + b; return -c / 2 * ((--t) * (t - 2) - 1) + b; } static easeInCubic(t, b, c, d) { return c * (t /= d) * t * t + b; } static easeOutCubic(t, b, c, d) { return c * ((t = t / d - 1) * t * t + 1) + b; } static easeInOutCubic(t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; return c / 2 * ((t -= 2) * t * t + 2) + b; } static easeInQuart(t, b, c, d) { return c * (t /= d) * t * t * t + b; } static easeOutQuart(t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; } static easeInOutQuart(t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b; return -c / 2 * ((t -= 2) * t * t * t - 2) + b; } static easeInQuint(t, b, c, d) { return c * (t /= d) * t * t * t * t + b; } static easeOutQuint(t, b, c, d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b; } static easeInOutQuint(t, b, c, d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b; return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; } static easeInSine(t, b, c, d) { return (t === d) ? b + c : -c * Math.cos(t / d * (Math.PI / 2)) + c + b; } static easeOutSine(t, b, c, d) { return c * Math.sin(t / d * (Math.PI / 2)) + b; } static easeInOutSine(t, b, c, d) { return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; } static easeInExpo(t, b, c, d) { return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; } static easeOutExpo(t, b, c, d) { return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; } static easeInOutExpo(t, b, c, d) { if (t === 0) return b; if (t === d) return b + c; if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b; return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; } static easeInCirc(t, b, c, d) { return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; } static easeOutCirc(t, b, c, d) { return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; } static easeInOutCirc(t, b, c, d) { if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; } static easeInElastic(t, b, c, d) { let s = 1.70158; let p = 0; let a = c; if (t === 0) return b; if ((t /= d) === 1) return b + c; if (!p) p = d * 0.3; if (a < Math.abs(c)) { a = c; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(c / a); } return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; } static easeOutElastic(t, b, c, d) { let s = 1.70158; let p = 0; let a = c; if (t === 0) return b; if ((t /= d) === 1) return b + c; if (!p) p = d * 0.3; if (a < Math.abs(c)) { a = c; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(c / a); } return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b; } static easeInOutElastic(t, b, c, d) { let s = 1.70158; let p = 0; let a = c; if (t === 0) return b; if ((t /= d / 2) === 2) return b + c; if (!p) p = d * (0.3 * 1.5); if (a < Math.abs(c)) { a = c; s = p / 4; } else { s = p / (2 * Math.PI) * Math.asin(c / a); } if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b; } static easeInBack(t, b, c, d, s = 1.70158) { return (t === d) ? b + c : c * (t /= d) * t * ((s + 1) * t - s) + b; } static easeOutBack(t, b, c, d, s = 1.70158) { return (t === 0) ? b : c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; } static easeInOutBack(t, b, c, d, s = 1.70158) { if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; } static easeInBounce(t, b, c, d) { return c - Ease.easeOutBounce(d - t, 0, c, d) + b; } static easeOutBounce(t, b, c, d) { if ((t /= d) < (1 / 2.75)) { return c * (7.5625 * t * t) + b; } else if (t < (2 / 2.75)) { return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b; } else if (t < (2.5 / 2.75)) { return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b; } else { return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b; } } static easeInOutBounce(t, b, c, d) { if (t < d / 2) return Ease.easeInBounce(t * 2, 0, c, d) * 0.5 + b; return Ease.easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; } } var Ease$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Ease: Ease }); class Tween extends EventEmitter { constructor(target, option) { super(); this.loop = false; this.$target = null; this.$paused = true; this.$stopped = true; this.$stepIndex = 0; this.$stepPosition = 0; this.$steps = []; this.$stepProps = []; this.$shouldSaveProps = true; this.$target = target; this.loop = option ? option.loop : false; this.$boundOnEnterFrame = this.$onEnterFrame.bind(this); } get paused() { return this.$paused; } get stopped() { return this.$stopped; } set(props) { this.$steps.push({ type: 'set', props }); return this; } to(props, duration, ease) { this.$steps.push({ type: 'to', duration, props, ease }); return this; } wait(duration) { this.$steps.push({ type: 'wait', duration }); return this; } call(callback) { this.$steps.push({ type: 'call', callback }); return this; } play() { if (this.$stopped) { this.resume(); Tween.$tweens.push(this); } return this; } pause() { this.$paused = true; this.$target.off(Event.ENTER_FRAME, this.$boundOnEnterFrame); return this; } resume() { if (this.$paused) { this.$paused = false; this.$stopped = false; this.$target.on(Event.ENTER_FRAME, this.$boundOnEnterFrame); } return this; } stop() { this.pause(); this.$stopped = true; let index = Tween.$tweens.indexOf(this); if (index >= 0) { Tween.$tweens.splice(index, 1); } return this; } $onEnterFrame(e) { this.$nextFrame(e.data); } $nextFrame(dt) { let loop = this.loop; let steps = this.$steps; let stepLength = this.$steps.length; let stepIndex = this.$stepIndex; let stepPosition = this.$stepPosition + dt; let step = steps[stepIndex]; let type = step.type; let duration = step.duration || 0; let props = step.props; let ease = step.ease || Ease.linear; let callback = step.callback; if (type === 'set') { this.$setProps(props); } else if (type === 'to') { this.$easeProps(stepIndex, props, stepPosition, duration, ease); } else if (type === 'call') { callback.call(this.$target); } if (stepPosition < duration) { this.$stepPosition = stepPosition; } else if (stepIndex + 1 < stepLength) { this.$stepPosition = 0; this.$stepIndex = stepIndex + 1; this.$shouldSaveProps = true; this.$setProps(props); this.$nextFrame(stepPosition - duration); } else if (loop) { this.$stepIndex = 0; this.$stepPosition = 0; this.$shouldSaveProps = true; this.$setProps(props); this.$nextFrame(stepPosition - duration); } else { this.$stepIndex = 0; this.$stepPosition = 0; this.$shouldSaveProps = true; this.$setProps(props); this.pause(); } } $saveOriginalProps(stepIndex, props) { let target = this.$target; let stepProps = this.$stepProps; let originalProps = stepProps[stepIndex] = stepProps[stepIndex] || {}; for (let key in props) { originalProps[key] = target[key]; } this.$shouldSaveProps = false; } $easeProps(stepIndex, props, position, duration, ease) { if (this.$shouldSaveProps) { this.$saveOriginalProps(stepIndex, props); } let target = this.$target; let originalProps = this.$stepProps[stepIndex] || {}; if (position > duration) { position = duration; } for (let key in props) { let originalValue = originalProps[key]; let offsetValue = props[key] - originalValue; target[key] = ease(position, originalValue, offsetValue, duration); } } $setProps(props) { let target = this.$target; for (let key in props) { target[key] = props[key]; } } static get(target, option) { return new Tween(target, option); } static pauseTweens(target) { let tweens = this.$tweens; for (let tween of tweens) { if (tween.$target === target) { tween.pause(); } } } static resumeTweens(target) { let tweens = this.$tweens; for (let tween of tweens) { if (tween.$target === target) { tween.resume(); } } } static removeTweens(target) { let tweens = this.$tweens; for (let i = tweens.length - 1; i >= 0; --i) { let tween = tweens[i]; if (tween.$target === target) { tween.stop(); } } } static removeAllTweens() { let tweens = this.$tweens; for (let i = tweens.length - 1; i >= 0; --i) { let tween = tweens[i]; tween.stop(); } } } Tween.$tweens = []; var Tween$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Tween: Tween }); class Scroller extends Layer { constructor() { super(); this.$scrollTop = 0; this.$scrollLeft = 0; this.$scrollWidth = 0; this.$scrollHeight = 0; this.$touchingX = null; this.$touchingY = null; this.$touchingId = null; this.$touchingTime = null; this.$velocitiesX = []; this.$velocitiesY = []; this.$inertiaTween = null; this.width = 200; this.height = 200; this.on(TouchEvent.TOUCH_START, this.$onTouchStart); this.on(TouchEvent.TOUCH_MOVE, this.$onTouchMove); this.on(TouchEvent.TOUCH_END, this.$onTouchEnd); this.on(TouchEvent.TOUCH_CANCEL, this.$onTouchCancel); } get scrollTop() { return this.$scrollTop; } set scrollTop(scrollTop) { let bounds = this.$getContentBounds(); let maxScrollTop = this.$scrollHeight - this.$height; scrollTop = Math.max(0, Math.min(scrollTop, maxScrollTop)); if (scrollTop !== this.$scrollTop) { this.$scrollTop = scrollTop; this.$markDirty(); } bounds.release(); } get scrollLeft() { return this.$scrollLeft; } set scrollLeft(scrollLeft) { let bounds = this.$getContentBounds(); let maxScrollLeft = this.$scrollWidth - this.width; scrollLeft = Math.max(0, Math.min(scrollLeft,