Эх сурвалжийг харах

Merge branch '4.0' into 4.1-beta

# Conflicts:
#	spine-ts/package-lock.json
#	spine-ts/package.json
#	spine-ts/spine-canvas/package.json
#	spine-ts/spine-core/package.json
#	spine-ts/spine-player/package.json
#	spine-ts/spine-threejs/package.json
#	spine-ts/spine-webgl/package.json
Mario Zechner 3 жил өмнө
parent
commit
fb0a658ecf

+ 1 - 0
CHANGELOG.md

@@ -336,6 +336,7 @@
 * Added `SpinePlayerConfig.draw`. If set, the callback is called each frame, just after the skeleton is drawn.
 * Added `SpinePlayerConfig.downloader`. The `spine.Downloader` instance can be shared between players so assets are only downloaded once.
 * If `SpinePlayerConfig.jsonURL` ends with an anchor, the anchor text is used to find the skeleton in the specified JSON file.
+* Added `SpinePlayer.dispose()`, disposes all CPU and GPU side resources, removes all listeners, and removes the player DOM from the parent.
 
 # 3.8
 

+ 1 - 0
spine-ts/index.html

@@ -21,6 +21,7 @@
 			<li><a href="/spine-player/example/embedding-binary-example.html">Embedding binary</a></li>
 			<li><a href="/spine-player/example/embedding-json-example.html">Embedding JSON</a></li>
 			<li><a href="/spine-player/example/editor.html">Editor</a></li>
+			<li><a href="/spine-player/example/dispose.html">Disposing a player</a></li>
 		</ul>
 		<li>WebGL</li>
 		<ul>

+ 24 - 0
spine-ts/publish.sh

@@ -0,0 +1,24 @@
+#!/bin/sh
+set -e
+
+if [ ! "$#" -eq 2 ]; then
+	echo "Usage: ./publish.sh <last-version> <new-version>"
+	exit	
+else
+	lastVersion=${1%/}
+	newVersion=${2%/}
+	echo "last version: $lastVersion"
+	echo "new version: $newVersion"
+fi
+
+sed -i '' "s/$lastVersion/$newVersion/" package.json
+sed -i '' "s/$lastVersion/$newVersion/" spine-canvas/package.json
+sed -i '' "s/$lastVersion/$newVersion/" spine-core/package.json
+sed -i '' "s/$lastVersion/$newVersion/" spine-player/package.json
+sed -i '' "s/$lastVersion/$newVersion/" spine-threejs/package.json
+sed -i '' "s/$lastVersion/$newVersion/" spine-webgl/package.json
+
+rm -rf node_modules
+rm package-lock.json
+npm install
+npm publish --access public --workspaces

+ 49 - 0
spine-ts/spine-player/example/dispose.html

@@ -0,0 +1,49 @@
+<!doctype html>
+<html>
+
+<head>
+	<meta charset="utf-8">
+	<script src="../dist/iife/spine-player.js"></script>
+	<link rel="stylesheet" href="../css/spine-player.css">
+	<meta name="viewport" content="width=device-width, initial-scale=1.0">
+</head>
+
+<style>
+	body {
+		background: gray;
+		margin: 0px;
+	}
+</style>
+
+<body>
+	<div id="container" style="width:640px; height:380px"></div>
+	<div>
+		<button id="dispose">Dispose</button>
+	</div>
+</body>
+<script>
+	var player = createPlayer();
+
+	document.getElementById("dispose").addEventListener("click", event => {
+		console.log("Disposing player.");
+		player.dispose();
+		player = createPlayer();
+	});
+
+	function createPlayer() {
+		return new spine.SpinePlayer("container", {
+			skelUrl: "assets/spineboy-pro.skel",
+			atlasUrl: "assets/spineboy-pma.atlas",
+			animation: "run",
+			premultipliedAlpha: true,
+			backgroundColor: "#cccccc",
+			viewport: {
+				debugRender: true,
+			},
+			showControls: true
+		});
+	}	
+</script>
+</body>
+
+</html>

+ 35 - 7
spine-ts/spine-player/src/Player.ts

@@ -27,7 +27,7 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-import { Animation, AnimationState, AnimationStateData, AtlasAttachmentLoader, Bone, Color, Downloader, MathUtils, MixBlend, MixDirection, Skeleton, SkeletonBinary, SkeletonData, SkeletonJson, StringMap, TextureAtlas, TextureFilter, TimeKeeper, TrackEntry, Vector2 } from "@esotericsoftware/spine-core"
+import { Animation, AnimationState, AnimationStateData, AtlasAttachmentLoader, Bone, Color, Disposable, Downloader, MathUtils, MixBlend, MixDirection, Skeleton, SkeletonBinary, SkeletonData, SkeletonJson, StringMap, TextureAtlas, TextureFilter, TimeKeeper, TrackEntry, Vector2 } from "@esotericsoftware/spine-core"
 import { AssetManager, GLTexture, Input, LoadingScreen, ManagedWebGLRenderingContext, ResizeMode, SceneRenderer, Vector3 } from "@esotericsoftware/spine-webgl"
 
 export interface SpinePlayerConfig {
@@ -178,7 +178,7 @@ export interface Viewport {
 	padBottom: string | number
 }
 
-export class SpinePlayer {
+export class SpinePlayer implements Disposable {
 	public parent: HTMLElement;
 	public dom: HTMLElement;
 	public canvas: HTMLCanvasElement;
@@ -211,11 +211,13 @@ export class SpinePlayer {
 	public speed = 1;
 	public time = new TimeKeeper();
 	private stopRequestAnimationFrame = false;
+	private disposed = false;
 
 	private viewport: Viewport = {} as Viewport;
 	private currentViewport: Viewport;
 	private previousViewport: Viewport;
 	private viewportTransitionStart = 0;
+	private eventListeners: Array<{ target: any, event: any, func: any }> = [];
 
 	constructor (parent: HTMLElement | string, private config: SpinePlayerConfig) {
 		this.parent = typeof parent === "string" ? document.getElementById(parent) : parent;
@@ -248,12 +250,29 @@ export class SpinePlayer {
 		this.initialize();
 
 		// Register a global resize handler to redraw, avoiding flicker.
-		window.addEventListener("resize", () => this.drawFrame(false));
+		this.addEventListener(window, "resize", () => this.drawFrame(false));
 
 		// Start the rendering loop.
 		requestAnimationFrame(() => this.drawFrame());
 	}
 
+	dispose (): void {
+		this.sceneRenderer.dispose();
+		this.loadingScreen.dispose();
+		this.assetManager.dispose();
+		for (var i = 0; i < this.eventListeners.length; i++) {
+			var eventListener = this.eventListeners[i];
+			eventListener.target.removeEventListener(eventListener.event, eventListener.func);
+		}
+		this.parent.removeChild(this.dom);
+		this.disposed = true;
+	}
+
+	addEventListener (target: any, event: any, func: any) {
+		this.eventListeners.push({ target: target, event: event, func: func });
+		target.addEventListener(event, func);
+	}
+
 	private validateConfig (config: SpinePlayerConfig) {
 		if (!config) throw new Error("A configuration object must be passed to to new SpinePlayer().");
 		if ((config as any).skelUrl) config.binaryUrl = (config as any).skelUrl;
@@ -583,16 +602,17 @@ export class SpinePlayer {
 		if (config.showControls) {
 			// For manual hover to work, we need to disable hidding controls if the mouse/touch entered the clickable area of a child of the controls.
 			// For this we need to register a mouse handler on the document and see if we are within the canvas area.
-			document.addEventListener("mousemove", (ev: UIEvent) => {
+			this.addEventListener(document, "mousemove", (ev: UIEvent) => {
 				if (ev instanceof MouseEvent) handleHover(ev.clientX, ev.clientY);
 			});
-			document.addEventListener("touchmove", (ev: UIEvent) => {
+			this.addEventListener(document, "touchmove", (ev: UIEvent) => {
 				if (ev instanceof TouchEvent) {
 					let touches = ev.changedTouches;
 					if (touches.length) {
 						let touch = touches[0];
 						handleHover(touch.clientX, touch.clientY);
 					}
+
 				}
 			});
 
@@ -747,6 +767,7 @@ export class SpinePlayer {
 	private drawFrame (requestNextFrame = true) {
 		try {
 			if (this.error) return;
+			if (this.disposed) return;
 			if (requestNextFrame && !this.stopRequestAnimationFrame) requestAnimationFrame(() => this.drawFrame());
 
 			let doc = document as any;
@@ -1015,6 +1036,7 @@ export class SpinePlayer {
 class Popup {
 	public dom: HTMLElement;
 	private className: string;
+	private windowClickListener: any;
 
 	constructor (private id: string, private button: HTMLElement, private player: SpinePlayer, parent: HTMLElement, htmlContent: string) {
 		this.dom = createElement(/*html*/`<div class="spine-player-popup spine-player-hidden"></div>`);
@@ -1023,6 +1045,10 @@ class Popup {
 		this.className = "spine-player-button-icon-" + id + "-selected";
 	}
 
+	dispose () {
+
+	}
+
 	hide (id: string): boolean {
 		this.dom.remove();
 		this.button.classList.remove(this.className);
@@ -1063,7 +1089,7 @@ class Popup {
 				dismissed = true;
 			}
 		};
-		window.addEventListener("click", windowClickListener);
+		this.player.addEventListener(window, "click", windowClickListener);
 	}
 }
 
@@ -1074,6 +1100,7 @@ class Switch {
 
 	constructor (private text: string) { }
 
+
 	create (): HTMLElement {
 		this.switch = createElement(/*html*/`
 <div class="spine-player-switch">
@@ -1092,7 +1119,8 @@ class Switch {
 	setEnabled (enabled: boolean) {
 		if (enabled) this.switch.classList.add("active");
 		else this.switch.classList.remove("active");
-		this.enabled = enabled;
+		this.enabled = enabled
+			;
 	}
 
 	isEnabled (): boolean {

+ 1 - 2
spine-ts/spine-webgl/src/Input.ts

@@ -27,8 +27,6 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-import { Pool } from "@esotericsoftware/spine-core";
-
 export class Input {
 	element: HTMLElement;
 	mouseX = 0;
@@ -38,6 +36,7 @@ export class Input {
 	touch1: Touch = null;
 	initialPinchDistance = 0;
 	private listeners = new Array<InputListener>();
+	private eventListeners: Array<{ target: any, event: any, func: any }> = [];
 
 	constructor (element: HTMLElement) {
 		this.element = element;

+ 6 - 2
spine-ts/spine-webgl/src/LoadingScreen.ts

@@ -27,7 +27,7 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-import { Color, TimeKeeper } from "@esotericsoftware/spine-core";
+import { Color, Disposable, TimeKeeper } from "@esotericsoftware/spine-core";
 import { GLTexture } from "./GLTexture";
 import { ResizeMode, SceneRenderer } from "./SceneRenderer";
 
@@ -38,7 +38,7 @@ let loaded = 0;
 const FADE_IN = 1, FADE_OUT = 1;
 const logoWidth = 165, logoHeight = 108, spinnerSize = 163;
 
-export class LoadingScreen {
+export class LoadingScreen implements Disposable {
 	private renderer: SceneRenderer;
 	private logo: GLTexture = null;
 	private spinner: GLTexture = null;
@@ -69,6 +69,10 @@ export class LoadingScreen {
 			spinnerImage.onload = onload;
 		}
 	}
+	dispose (): void {
+		this.logo.dispose();
+		this.spinner.dispose();
+	}
 
 	draw (complete = false) {
 		if (loaded < 2 || (complete && this.fadeOut > FADE_OUT)) return;

+ 8 - 8
spine-ts/spine-webgl/src/SceneRenderer.ts

@@ -73,6 +73,14 @@ export class SceneRenderer implements Disposable {
 		this.skeletonDebugRenderer = new SkeletonDebugRenderer(this.context);
 	}
 
+	dispose () {
+		this.batcher.dispose();
+		this.batcherShader.dispose();
+		this.shapes.dispose();
+		this.shapesShader.dispose();
+		this.skeletonDebugRenderer.dispose();
+	}
+
 	begin () {
 		this.camera.update();
 		this.enableRenderer(this.batcher);
@@ -498,14 +506,6 @@ export class SceneRenderer implements Disposable {
 		} else
 			this.activeRenderer = this.skeletonDebugRenderer;
 	}
-
-	dispose () {
-		this.batcher.dispose();
-		this.batcherShader.dispose();
-		this.shapes.dispose();
-		this.shapesShader.dispose();
-		this.skeletonDebugRenderer.dispose();
-	}
 }
 
 export enum ResizeMode {