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
This commit is contained in:
Mario Zechner 2022-01-24 22:23:03 +01:00
commit fb0a658ecf
8 changed files with 125 additions and 19 deletions

View File

@ -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

View File

@ -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
spine-ts/publish.sh Executable file
View File

@ -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

View File

@ -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>

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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 {