mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
Manage lifecycle for Input and SpineWebComponentOverlay. Missing SpineWebComponentWidget.
This commit is contained in:
parent
e26034426a
commit
5c42b6fa2d
@ -27,7 +27,8 @@
|
||||
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
export class Input {
|
||||
import { Disposable } from "./index.js"
|
||||
export class Input implements Disposable {
|
||||
element: HTMLElement;
|
||||
mouseX = 0;
|
||||
mouseY = 0;
|
||||
@ -37,28 +38,34 @@ export class Input {
|
||||
initialPinchDistance = 0;
|
||||
private listeners = new Array<InputListener>();
|
||||
private autoPreventDefault: boolean;
|
||||
private callbacks: {
|
||||
mouseDown: (ev: UIEvent) => void;
|
||||
mouseMove: (ev: UIEvent) => void;
|
||||
mouseUp: (ev: UIEvent) => void;
|
||||
mouseWheel: (ev: WheelEvent) => void;
|
||||
touchStart: (ev: TouchEvent) => void;
|
||||
touchMove: (ev: TouchEvent) => void;
|
||||
touchEnd: (ev: TouchEvent) => void;
|
||||
};
|
||||
|
||||
constructor (element: HTMLElement, autoPreventDefault = true) {
|
||||
this.element = element;
|
||||
this.autoPreventDefault = autoPreventDefault;
|
||||
this.setupCallbacks(element);
|
||||
this.callbacks = this.setupCallbacks(element);
|
||||
}
|
||||
|
||||
private setupCallbacks (element: HTMLElement) {
|
||||
let mouseDown = (ev: UIEvent) => {
|
||||
const mouseDown = (ev: UIEvent) => {
|
||||
if (ev instanceof MouseEvent) {
|
||||
let rect = element.getBoundingClientRect();
|
||||
this.mouseX = ev.clientX - rect.left;
|
||||
this.mouseY = ev.clientY - rect.top;
|
||||
this.buttonDown = true;
|
||||
this.listeners.map((listener) => { if (listener.down) listener.down(this.mouseX, this.mouseY, ev); });
|
||||
|
||||
document.addEventListener("mousemove", mouseMove);
|
||||
document.addEventListener("mouseup", mouseUp);
|
||||
}
|
||||
}
|
||||
|
||||
let mouseMove = (ev: UIEvent) => {
|
||||
const mouseMove = (ev: UIEvent) => {
|
||||
if (ev instanceof MouseEvent) {
|
||||
let rect = element.getBoundingClientRect();
|
||||
this.mouseX = ev.clientX - rect.left;
|
||||
@ -74,20 +81,17 @@ export class Input {
|
||||
}
|
||||
};
|
||||
|
||||
let mouseUp = (ev: UIEvent) => {
|
||||
const mouseUp = (ev: UIEvent) => {
|
||||
if (ev instanceof MouseEvent) {
|
||||
let rect = element.getBoundingClientRect();
|
||||
this.mouseX = ev.clientX - rect.left;;
|
||||
this.mouseY = ev.clientY - rect.top;
|
||||
this.buttonDown = false;
|
||||
this.listeners.map((listener) => { if (listener.up) listener.up(this.mouseX, this.mouseY, ev); });
|
||||
|
||||
document.removeEventListener("mousemove", mouseMove);
|
||||
document.removeEventListener("mouseup", mouseUp);
|
||||
}
|
||||
}
|
||||
|
||||
let mouseWheel = (ev: WheelEvent) => {
|
||||
const mouseWheel = (ev: WheelEvent) => {
|
||||
if (this.autoPreventDefault) ev.preventDefault();
|
||||
let deltaY = ev.deltaY;
|
||||
if (ev.deltaMode == WheelEvent.DOM_DELTA_LINE) deltaY *= 8;
|
||||
@ -95,13 +99,7 @@ export class Input {
|
||||
this.listeners.map((listener) => { if (listener.wheel) listener.wheel(ev.deltaY, ev); });
|
||||
};
|
||||
|
||||
element.addEventListener("mousedown", mouseDown, true);
|
||||
element.addEventListener("mousemove", mouseMove, true);
|
||||
element.addEventListener("mouseup", mouseUp, true);
|
||||
element.addEventListener("wheel", mouseWheel, true);
|
||||
|
||||
|
||||
element.addEventListener("touchstart", (ev: TouchEvent) => {
|
||||
const touchStart = (ev: TouchEvent) => {
|
||||
if (!this.touch0 || !this.touch1) {
|
||||
var touches = ev.changedTouches;
|
||||
let nativeTouch = touches.item(0);
|
||||
@ -126,9 +124,9 @@ export class Input {
|
||||
}
|
||||
}
|
||||
if (this.autoPreventDefault) ev.preventDefault();
|
||||
}, { passive: false, capture: false });
|
||||
}
|
||||
|
||||
element.addEventListener("touchmove", (ev: TouchEvent) => {
|
||||
const touchMove = (ev: TouchEvent) => {
|
||||
if (this.touch0) {
|
||||
var touches = ev.changedTouches;
|
||||
let rect = element.getBoundingClientRect();
|
||||
@ -155,9 +153,9 @@ export class Input {
|
||||
}
|
||||
}
|
||||
if (this.autoPreventDefault) ev.preventDefault();
|
||||
}, { passive: false, capture: false });
|
||||
}
|
||||
|
||||
let touchEnd = (ev: TouchEvent) => {
|
||||
const touchEnd = (ev: TouchEvent) => {
|
||||
if (this.touch0) {
|
||||
var touches = ev.changedTouches;
|
||||
let rect = element.getBoundingClientRect();
|
||||
@ -193,8 +191,38 @@ export class Input {
|
||||
}
|
||||
if (this.autoPreventDefault) ev.preventDefault();
|
||||
};
|
||||
|
||||
element.addEventListener("mousedown", mouseDown, true);
|
||||
element.addEventListener("mousemove", mouseMove, true);
|
||||
element.addEventListener("mouseup", mouseUp, true);
|
||||
element.addEventListener("wheel", mouseWheel, true);
|
||||
element.addEventListener("touchstart", touchStart, { passive: false, capture: false });
|
||||
element.addEventListener("touchmove", touchMove, { passive: false, capture: false });
|
||||
element.addEventListener("touchend", touchEnd, { passive: false, capture: false });
|
||||
element.addEventListener("touchcancel", touchEnd);
|
||||
|
||||
return {
|
||||
mouseDown,
|
||||
mouseMove,
|
||||
mouseUp,
|
||||
mouseWheel,
|
||||
touchStart,
|
||||
touchMove,
|
||||
touchEnd,
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
const element = this.element;
|
||||
element.addEventListener("mousedown", this.callbacks.mouseDown, true);
|
||||
element.addEventListener("mousemove", this.callbacks.mouseMove, true);
|
||||
element.addEventListener("mouseup", this.callbacks.mouseUp, true);
|
||||
element.addEventListener("wheel", this.callbacks.mouseWheel, true);
|
||||
element.addEventListener("touchstart", this.callbacks.touchStart, { passive: false, capture: false });
|
||||
element.addEventListener("touchmove", this.callbacks.touchMove, { passive: false, capture: false });
|
||||
element.addEventListener("touchend", this.callbacks.touchEnd, { passive: false, capture: false });
|
||||
element.addEventListener("touchcancel", this.callbacks.touchEnd);
|
||||
this.listeners.length = 0;
|
||||
}
|
||||
|
||||
addListener (listener: InputListener) {
|
||||
|
||||
@ -34,6 +34,7 @@ import {
|
||||
AtlasAttachmentLoader,
|
||||
AssetManager,
|
||||
Color,
|
||||
Disposable,
|
||||
Input,
|
||||
LoadingScreen,
|
||||
ManagedWebGLRenderingContext,
|
||||
@ -810,7 +811,12 @@ export class SpineWebComponentWidget extends HTMLElement implements WidgetAttrib
|
||||
}
|
||||
}
|
||||
|
||||
class SpineWebComponentOverlay extends HTMLElement {
|
||||
class SpineWebComponentOverlay extends HTMLElement implements Disposable {
|
||||
|
||||
public skeletonList = new Array<SpineWebComponentWidget>();
|
||||
public renderer: SceneRenderer;
|
||||
public assetManager: AssetManager;
|
||||
|
||||
private root: ShadowRoot;
|
||||
|
||||
private div: HTMLDivElement;
|
||||
@ -818,10 +824,8 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
private fps: HTMLSpanElement;
|
||||
private fpsAppended = false;
|
||||
|
||||
public skeletonList = new Array<SpineWebComponentWidget>();
|
||||
|
||||
private intersectionObserver? : IntersectionObserver;
|
||||
private input: Input;
|
||||
private input?: Input;
|
||||
|
||||
// how many pixels to add to the edges to prevent "edge cuttin" on fast scrolling
|
||||
// be aware that the canvas is already big as the display size
|
||||
@ -836,9 +840,8 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
private currentCanvasBaseWidth = 0;
|
||||
private currentCanvasBaseHeight = 0;
|
||||
|
||||
public renderer: SceneRenderer;
|
||||
public assetManager: AssetManager;
|
||||
private disposed = false;
|
||||
private detached = true;
|
||||
readonly time = new TimeKeeper();
|
||||
|
||||
constructor() {
|
||||
@ -873,40 +876,40 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
const context = new ManagedWebGLRenderingContext(this.canvas, { alpha: true });
|
||||
this.renderer = new SceneRenderer(this.canvas, context);
|
||||
this.assetManager = new AssetManager(context);
|
||||
this.input = new Input(this.canvas, false);
|
||||
this.setupRenderingElements();
|
||||
|
||||
this.overflowLeftSize = this.overflowLeft * document.documentElement.clientWidth;
|
||||
this.overflowTopSize = this.overflowTop * document.documentElement.clientHeight;
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
this.updateCanvasSize();
|
||||
this.zoomHandler();
|
||||
});
|
||||
|
||||
window.screen.orientation.onchange = () => {
|
||||
this.updateCanvasSize();
|
||||
// after an orientation change the scrolling changes, but the scroll event does not fire
|
||||
this.scrollHandler();
|
||||
}
|
||||
|
||||
window.addEventListener("scroll", this.scrollHandler);
|
||||
|
||||
window.onload = () => {
|
||||
this.updateCanvasSize();
|
||||
this.zoomHandler();
|
||||
|
||||
// translateCanvas starts a requestAnimationFrame loop
|
||||
this.translateCanvas();
|
||||
|
||||
this.scrollHandler();
|
||||
};
|
||||
|
||||
this.input = new Input(document.body, false);
|
||||
this.setupDragUtility();
|
||||
}
|
||||
|
||||
private resizeCallback = () => {
|
||||
this.updateCanvasSize();
|
||||
this.zoomHandler();
|
||||
}
|
||||
private orientationChangeCallback = () => {
|
||||
this.updateCanvasSize();
|
||||
// after an orientation change the scrolling changes, but the scroll event does not fire
|
||||
this.scrollHandler();
|
||||
}
|
||||
// right now, we scroll the canvas each frame, that makes scrolling on mobile waaay more smoother
|
||||
// this is way scroll handler do nothing
|
||||
private scrollHandler = () => {
|
||||
// this.translateCanvas();
|
||||
}
|
||||
private onLoadCallback = () => {
|
||||
this.updateCanvasSize();
|
||||
this.zoomHandler();
|
||||
|
||||
// translateCanvas starts a requestAnimationFrame loop
|
||||
this.translateCanvas();
|
||||
|
||||
this.scrollHandler();
|
||||
}
|
||||
connectedCallback(): void {
|
||||
window.addEventListener("resize", this.resizeCallback);
|
||||
window.addEventListener("scroll", this.scrollHandler);
|
||||
window.addEventListener("load", this.onLoadCallback);
|
||||
window.screen.orientation.addEventListener('change', this.orientationChangeCallback);
|
||||
|
||||
this.intersectionObserver = new IntersectionObserver((widgets) => {
|
||||
widgets.forEach(({ isIntersecting, target, intersectionRatio }) => {
|
||||
const widget = this.skeletonList.find(w => w.getHTMLElementReference() == target);
|
||||
@ -923,17 +926,40 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
}
|
||||
})
|
||||
}, { rootMargin: "30px 20px 30px 20px" });
|
||||
this.skeletonList.forEach((widget) => {
|
||||
this.intersectionObserver?.observe(widget.getHTMLElementReference());
|
||||
})
|
||||
this.input = this.setupDragUtility();
|
||||
|
||||
this.detached = false;
|
||||
|
||||
this.startRenderingLoop();
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
window.removeEventListener("resize", this.resizeCallback);
|
||||
window.removeEventListener("scroll", this.scrollHandler);
|
||||
window.removeEventListener("load", this.onLoadCallback);
|
||||
window.screen.orientation.removeEventListener('change', this.orientationChangeCallback);
|
||||
this.intersectionObserver?.disconnect();
|
||||
this.input?.dispose();
|
||||
this.detached = true;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
document.body.removeChild(this);
|
||||
this.skeletonList.length = 0;
|
||||
this.renderer.dispose();
|
||||
this.disposed = true;
|
||||
this.detached = true;
|
||||
}
|
||||
|
||||
addWidget(widget: SpineWebComponentWidget) {
|
||||
this.skeletonList.push(widget);
|
||||
this.intersectionObserver!.observe(widget.getHTMLElementReference());
|
||||
this.intersectionObserver?.observe(widget.getHTMLElementReference());
|
||||
}
|
||||
|
||||
private setupRenderingElements() {
|
||||
private startRenderingLoop() {
|
||||
const updateWidgets = () => {
|
||||
const delta = this.time.delta;
|
||||
this.skeletonList.forEach(({ skeleton, state, update, onScreen, offScreenUpdateBehaviour, beforeUpdateWorldTransforms, afterUpdateWorldTransforms }) => {
|
||||
@ -1195,7 +1221,7 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
}
|
||||
|
||||
const loop = () => {
|
||||
if (this.disposed) return;
|
||||
if (this.disposed || this.detached) return;
|
||||
requestAnimationFrame(loop);
|
||||
this.time.update();
|
||||
updateWidgets();
|
||||
@ -1210,8 +1236,9 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
const transparentWhite = new Color(1, 1, 1, .3);
|
||||
}
|
||||
|
||||
private setupDragUtility() {
|
||||
// TODO: we should use document - body might have some margin that offset the click events - Meanwhile I take event pageX/Y
|
||||
private setupDragUtility(): Input {
|
||||
// TODO: we should use document - body might have some margin that offset the click events - Meanwhile I take event pageX/Y
|
||||
const inputManager = new Input(document.body, false)
|
||||
const point: Point = { x: 0, y: 0 };
|
||||
|
||||
const getInput = (ev?: MouseEvent | TouchEvent): Point => {
|
||||
@ -1223,7 +1250,7 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
|
||||
let prevX = 0;
|
||||
let prevY = 0;
|
||||
this.input.addListener({
|
||||
inputManager.addListener({
|
||||
down: (x, y, ev) => {
|
||||
const input = getInput(ev);
|
||||
this.skeletonList.forEach(widget => {
|
||||
@ -1257,7 +1284,9 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
widget.dragging = false;
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return inputManager;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1306,12 +1335,6 @@ class SpineWebComponentOverlay extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
// right now, we scroll the canvas each frame, that makes scrolling on mobile waaay more smoother
|
||||
// this is way scroll handler do nothing
|
||||
private scrollHandler = () => {
|
||||
// this.translateCanvas();
|
||||
}
|
||||
|
||||
private translateCanvas() {
|
||||
const scrollPositionX = window.scrollX - this.overflowLeftSize;
|
||||
const scrollPositionY = window.scrollY - this.overflowTopSize;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user