[ts] Modularize player, add package.json files, fix up module paths.

This commit is contained in:
Mario Zechner 2021-08-25 21:00:17 +02:00
parent 79f0abf765
commit 17c342ac61
33 changed files with 1251 additions and 10010 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -61,6 +61,14 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@esotericsoftware/spine-canvas": {
"resolved": "spine-canvas",
"link": true
},
"node_modules/@esotericsoftware/spine-core": {
"resolved": "spine-core",
"link": true
},
"node_modules/@esotericsoftware/spine-webgl": { "node_modules/@esotericsoftware/spine-webgl": {
"resolved": "spine-webgl", "resolved": "spine-webgl",
"link": true "link": true
@ -5398,13 +5406,17 @@
"spine-canvas": { "spine-canvas": {
"name": "@esotericsoftware/spine-canvas", "name": "@esotericsoftware/spine-canvas",
"version": "4.0.1", "version": "4.0.1",
"extraneous": true, "license": "LicenseRef-LICENSE",
"license": "LicenseRef-LICENSE" "devDependencies": {
"npx": "^10.2.2",
"rollup": "^2.56.2",
"rollup-plugin-dts": "^3.0.2",
"typescript": "^4.3.5"
}
}, },
"spine-core": { "spine-core": {
"name": "@esotericsoftware/spine-core", "name": "@esotericsoftware/spine-core",
"version": "4.0.1", "version": "4.0.1",
"extraneous": true,
"license": "LicenseRef-LICENSE" "license": "LicenseRef-LICENSE"
}, },
"spine-webgl": { "spine-webgl": {
@ -5443,6 +5455,18 @@
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
} }
}, },
"@esotericsoftware/spine-canvas": {
"version": "file:spine-canvas",
"requires": {
"npx": "^10.2.2",
"rollup": "^2.56.2",
"rollup-plugin-dts": "^3.0.2",
"typescript": "^4.3.5"
}
},
"@esotericsoftware/spine-core": {
"version": "file:spine-core"
},
"@esotericsoftware/spine-webgl": { "@esotericsoftware/spine-webgl": {
"version": "file:spine-webgl" "version": "file:spine-webgl"
}, },

View File

@ -3,7 +3,11 @@
"version": "4.0.1", "version": "4.0.1",
"description": "The official Spine Runtimes for the web.", "description": "The official Spine Runtimes for the web.",
"scripts": { "scripts": {
"build": "npx tsc -b --clean; tsc -b; npx rollup -c rollup.config.js --sourcemap;" "build": "rm -rf build && npm run build:core && npm run build:canvas && npm run build:webgl && npm run build:player",
"build:core": "npx esbuild --bundle spine-core/src/index.ts --tsconfig=spine-core/tsconfig.json --minify --sourcemap --outfile=build/spine-core.js --format=iife --global-name=\"spine\"",
"build:canvas": "npx esbuild --bundle spine-canvas/src/index.ts --tsconfig=spine-canvas/tsconfig.json --minify --sourcemap --outfile=build/spine-canvas.js --format=iife --global-name=\"spine\"",
"build:webgl": "npx esbuild --bundle spine-webgl/src/index.ts --tsconfig=spine-webgl/tsconfig.json --minify --sourcemap --outfile=build/spine-webgl.js --format=iife --global-name=\"spine\"",
"build:player": "npx esbuild --bundle spine-player/src/index.ts --tsconfig=spine-player/tsconfig.json --sourcemap --outfile=build/spine-player.js --format=iife --global-name=\"spine\""
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -3,6 +3,10 @@
"version": "4.0.1", "version": "4.0.1",
"description": "The official Spine Runtimes for the web.", "description": "The official Spine Runtimes for the web.",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*"
],
"scripts": {}, "scripts": {},
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { AssetManagerBase, Downloader } from "spine-core"; import { AssetManagerBase, Downloader } from "@esotericsoftware/spine-core"
import { CanvasTexture } from "./CanvasTexture"; import { CanvasTexture } from "./CanvasTexture";
export class AssetManager extends AssetManagerBase { export class AssetManager extends AssetManagerBase {

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Texture, TextureFilter, TextureWrap } from "spine-core"; import { Texture, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
export class CanvasTexture extends Texture { export class CanvasTexture extends Texture {
constructor(image: HTMLImageElement) { constructor(image: HTMLImageElement) {

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Utils, Color, Skeleton, RegionAttachment, TextureAtlasRegion, BlendMode, MeshAttachment, Slot } from "spine-core"; import { Utils, Color, Skeleton, RegionAttachment, TextureAtlasRegion, BlendMode, MeshAttachment, Slot } from "@esotericsoftware/spine-core";
import { CanvasTexture } from "./CanvasTexture"; import { CanvasTexture } from "./CanvasTexture";
export class SkeletonRenderer { export class SkeletonRenderer {

View File

@ -2,7 +2,7 @@ export * from "./AssetManager";
export * from "./CanvasTexture"; export * from "./CanvasTexture";
export * from "./SkeletonRenderer"; export * from "./SkeletonRenderer";
export * from "spine-core"; export * from "@esotericsoftware/spine-core"
// Before modularization, we would expose spine-core on the global // Before modularization, we would expose spine-core on the global
// `spine` object, and spine-canvas on the global `spine.canvas` object. // `spine` object, and spine-canvas on the global `spine.canvas` object.
@ -23,9 +23,5 @@ export * from "spine-core";
// //
// This will break if esbuild renames the variable `src_exports` pointing to // This will break if esbuild renames the variable `src_exports` pointing to
// the exports object. // the exports object.
declare global {
var spine: any;
}
let exports = eval("src_exports"); let exports = eval("src_exports");
exports.canvas = exports; if (exports) exports.canvas = exports;

View File

@ -5,7 +5,7 @@
"rootDir": "./src", "rootDir": "./src",
"outDir": "./dist", "outDir": "./dist",
"paths": { "paths": {
"spine-core": [ "@esotericsoftware/spine-core": [
"../spine-core/src" "../spine-core/src"
] ]
} }

View File

@ -3,6 +3,10 @@
"version": "4.0.1", "version": "4.0.1",
"description": "The official Spine Runtimes for the web.", "description": "The official Spine Runtimes for the web.",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*"
],
"scripts": {}, "scripts": {},
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -0,0 +1,30 @@
{
"name": "@esotericsoftware/spine-player",
"version": "4.0.1",
"description": "The official Spine Runtimes for the web.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*"
],
"scripts": {},
"repository": {
"type": "git",
"url": "git+https://github.com/esotericsoftware/spine-runtimes.git"
},
"keywords": [
"gamedev",
"animations",
"2d",
"spine",
"game-dev",
"runtimes",
"skeletal"
],
"author": "Esoteric Software LLC",
"license": "LicenseRef-LICENSE",
"bugs": {
"url": "https://github.com/esotericsoftware/spine-runtimes/issues"
},
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme"
}

View File

@ -27,8 +27,10 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
module spine { 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"
export interface SpinePlayerConfig { import { AssetManager, GLTexture, Input, LoadingScreen, ManagedWebGLRenderingContext, ResizeMode, SceneRenderer, Vector3 } from "@esotericsoftware/spine-webgl"
export interface SpinePlayerConfig {
/* The URL of the skeleton JSON file (.json). */ /* The URL of the skeleton JSON file (.json). */
jsonUrl: string jsonUrl: string
@ -44,7 +46,7 @@ module spine {
/* Raw data URIs, mapping a path to base64 encoded raw data. When player's asset manager resolves the jsonUrl, binaryUrl, /* Raw data URIs, mapping a path to base64 encoded raw data. When player's asset manager resolves the jsonUrl, binaryUrl,
atlasUrl, or the image paths referenced in the atlas, it will first look for that path in the raw data URIs. This atlasUrl, or the image paths referenced in the atlas, it will first look for that path in the raw data URIs. This
allows embedding assets directly in HTML/JS. Default: none */ allows embedding assets directly in HTML/JS. Default: none */
rawDataURIs: Map<string> rawDataURIs: StringMap<string>
/* Optional: The name of the animation to be played. Default: empty animation */ /* Optional: The name of the animation to be played. Default: empty animation */
animation: string animation: string
@ -105,7 +107,7 @@ module spine {
transitionTime: number transitionTime: number
/* Optional: Viewports for specific animations. Default: none */ /* Optional: Viewports for specific animations. Default: none */
animations: Map<Viewport> animations: StringMap<Viewport>
} }
/* Optional: Whether the canvas is transparent, allowing the web page behind the canvas to show through when /* Optional: Whether the canvas is transparent, allowing the web page behind the canvas to show through when
@ -158,10 +160,10 @@ module spine {
/* Optional: The downloader used by the player's asset manager. Passing the same downloader to multiple players using the /* Optional: The downloader used by the player's asset manager. Passing the same downloader to multiple players using the
same assets ensures the assets are only downloaded once. Default: new instance */ same assets ensures the assets are only downloaded once. Default: new instance */
downloader: spine.Downloader downloader: Downloader
} }
export interface Viewport { export interface Viewport {
/* Optional: The position and size of the viewport in the skeleton's world coordinates. Default: the bounding box that fits /* Optional: The position and size of the viewport in the skeleton's world coordinates. Default: the bounding box that fits
the current animation */ the current animation */
x: number, x: number,
@ -174,16 +176,16 @@ module spine {
padRight: string | number padRight: string | number
padTop: string | number padTop: string | number
padBottom: string | number padBottom: string | number
} }
export class SpinePlayer { export class SpinePlayer {
public parent: HTMLElement; public parent: HTMLElement;
public dom: HTMLElement; public dom: HTMLElement;
public canvas: HTMLCanvasElement; public canvas: HTMLCanvasElement;
public context: spine.webgl.ManagedWebGLRenderingContext; public context: ManagedWebGLRenderingContext;
public sceneRenderer: spine.webgl.SceneRenderer; public sceneRenderer: SceneRenderer;
public loadingScreen: spine.webgl.LoadingScreen; public loadingScreen: LoadingScreen;
public assetManager: spine.webgl.AssetManager; public assetManager: AssetManager;
public bg = new Color(); public bg = new Color();
public bgFullscreen = new Color(); public bgFullscreen = new Color();
@ -215,7 +217,7 @@ module spine {
private previousViewport: Viewport; private previousViewport: Viewport;
private viewportTransitionStart = 0; private viewportTransitionStart = 0;
constructor (parent: HTMLElement | string, private config: SpinePlayerConfig) { constructor(parent: HTMLElement | string, private config: SpinePlayerConfig) {
this.parent = typeof parent === "string" ? document.getElementById(parent) : parent; this.parent = typeof parent === "string" ? document.getElementById(parent) : parent;
if (!this.parent) throw new Error("SpinePlayer parent not found: " + parent); if (!this.parent) throw new Error("SpinePlayer parent not found: " + parent);
@ -252,8 +254,8 @@ module spine {
requestAnimationFrame(() => this.drawFrame()); requestAnimationFrame(() => this.drawFrame());
} }
private validateConfig (config: SpinePlayerConfig) { private validateConfig(config: SpinePlayerConfig) {
if (!config) throw new Error("A configuration object must be passed to to new spine.SpinePlayer()."); 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; if ((config as any).skelUrl) config.binaryUrl = (config as any).skelUrl;
if (!config.jsonUrl && !config.binaryUrl) throw new Error("A URL must be specified for the skeleton JSON or binary file."); if (!config.jsonUrl && !config.binaryUrl) throw new Error("A URL must be specified for the skeleton JSON or binary file.");
if (!config.atlasUrl) throw new Error("A URL must be specified for the atlas file."); if (!config.atlasUrl) throw new Error("A URL must be specified for the atlas file.");
@ -276,7 +278,7 @@ module spine {
if (config.defaultMix === void 0) config.defaultMix = 0.25; if (config.defaultMix === void 0) config.defaultMix = 0.25;
} }
private initialize (): HTMLElement { private initialize(): HTMLElement {
let config = this.config; let config = this.config;
let dom = this.dom; let dom = this.dom;
@ -288,17 +290,17 @@ module spine {
try { try {
// Setup the OpenGL context. // Setup the OpenGL context.
this.canvas = findWithClass(dom, "spine-player-canvas") as HTMLCanvasElement; this.canvas = findWithClass(dom, "spine-player-canvas") as HTMLCanvasElement;
this.context = new spine.webgl.ManagedWebGLRenderingContext(this.canvas, { alpha: config.alpha }); this.context = new ManagedWebGLRenderingContext(this.canvas, { alpha: config.alpha });
// Setup the scene renderer and loading screen. // Setup the scene renderer and loading screen.
this.sceneRenderer = new spine.webgl.SceneRenderer(this.canvas, this.context, true); this.sceneRenderer = new SceneRenderer(this.canvas, this.context, true);
if (config.showLoading) this.loadingScreen = new spine.webgl.LoadingScreen(this.sceneRenderer); if (config.showLoading) this.loadingScreen = new LoadingScreen(this.sceneRenderer);
} catch (e) { } catch (e) {
this.showError("Sorry, your browser does not support WebGL.\nPlease use the latest version of Firefox, Chrome, Edge, or Safari.", e); this.showError("Sorry, your browser does not support \nPlease use the latest version of Firefox, Chrome, Edge, or Safari.", e);
} }
// Load the assets. // Load the assets.
this.assetManager = new spine.webgl.AssetManager(this.context, "", config.downloader); this.assetManager = new AssetManager(this.context, "", config.downloader);
if (config.rawDataURIs) { if (config.rawDataURIs) {
for (let path in config.rawDataURIs) for (let path in config.rawDataURIs)
this.assetManager.setRawDataURI(path, config.rawDataURIs[path]); this.assetManager.setRawDataURI(path, config.rawDataURIs[path]);
@ -389,7 +391,7 @@ module spine {
return dom; return dom;
} }
private loadSkeleton () { private loadSkeleton() {
if (this.error) return; if (this.error) return;
if (this.assetManager.hasErrors()) if (this.assetManager.hasErrors())
@ -415,7 +417,7 @@ module spine {
minFilter = TextureFilter.Linear; // Don't use mipmaps without anisotropic. minFilter = TextureFilter.Linear; // Don't use mipmaps without anisotropic.
page.texture.setFilters(minFilter, TextureFilter.Nearest); page.texture.setFilters(minFilter, TextureFilter.Nearest);
} }
if (minFilter != TextureFilter.Nearest && minFilter != TextureFilter.Linear) (page.texture as spine.webgl.GLTexture).update(true); if (minFilter != TextureFilter.Nearest && minFilter != TextureFilter.Linear) (page.texture as GLTexture).update(true);
} }
// Load skeleton data. // Load skeleton data.
@ -513,17 +515,17 @@ module spine {
} }
} }
private setupInput () { private setupInput() {
let config = this.config; let config = this.config;
let controlBones = config.controlBones; let controlBones = config.controlBones;
if (!controlBones.length && !config.showControls) return; if (!controlBones.length && !config.showControls) return;
let selectedBones = this.selectedBones = new Array<Bone>(controlBones.length); let selectedBones = this.selectedBones = new Array<Bone>(controlBones.length);
let canvas = this.canvas; let canvas = this.canvas;
let target: Bone = null; let target: Bone = null;
let offset = new spine.Vector2(); let offset = new Vector2();
let coords = new spine.webgl.Vector3(); let coords = new Vector3();
let mouse = new spine.webgl.Vector3(); let mouse = new Vector3();
let position = new spine.Vector2(); let position = new Vector2();
let skeleton = this.skeleton; let skeleton = this.skeleton;
let renderer = this.sceneRenderer; let renderer = this.sceneRenderer;
@ -550,7 +552,7 @@ module spine {
return best; return best;
}; };
new spine.webgl.Input(canvas).addListener({ new Input(canvas).addListener({
down: (x, y) => { down: (x, y) => {
target = closest(x, y); target = closest(x, y);
}, },
@ -619,7 +621,7 @@ module spine {
} }
} }
play () { play() {
this.paused = false; this.paused = false;
let config = this.config; let config = this.config;
if (config.showControls) { if (config.showControls) {
@ -640,7 +642,7 @@ module spine {
} }
} }
pause () { pause() {
this.paused = true; this.paused = true;
if (this.config.showControls) { if (this.config.showControls) {
this.playerControls.classList.remove("spine-player-controls-hidden"); this.playerControls.classList.remove("spine-player-controls-hidden");
@ -651,19 +653,19 @@ module spine {
} }
/* Sets a new animation and viewport on track 0. */ /* Sets a new animation and viewport on track 0. */
setAnimation (animation: string | Animation, loop: boolean = true): TrackEntry { setAnimation(animation: string | Animation, loop: boolean = true): TrackEntry {
animation = this.setViewport(animation); animation = this.setViewport(animation);
return this.animationState.setAnimationWith(0, animation, loop); return this.animationState.setAnimationWith(0, animation, loop);
} }
/* Adds a new animation and viewport on track 0. */ /* Adds a new animation and viewport on track 0. */
addAnimation (animation: string | Animation, loop: boolean = true, delay: number = 0): TrackEntry { addAnimation(animation: string | Animation, loop: boolean = true, delay: number = 0): TrackEntry {
animation = this.setViewport(animation); animation = this.setViewport(animation);
return this.animationState.addAnimationWith(0, animation, loop, delay); return this.animationState.addAnimationWith(0, animation, loop, delay);
} }
/* Sets the viewport for the specified animation. */ /* Sets the viewport for the specified animation. */
setViewport (animation: string | Animation): Animation { setViewport(animation: string | Animation): Animation {
if (typeof animation == "string") animation = this.skeleton.data.findAnimation(animation); if (typeof animation == "string") animation = this.skeleton.data.findAnimation(animation);
this.previousViewport = this.currentViewport; this.previousViewport = this.currentViewport;
@ -709,18 +711,18 @@ module spine {
return animation; return animation;
} }
private percentageToWorldUnit (size: number, percentageOrAbsolute: string | number): number { private percentageToWorldUnit(size: number, percentageOrAbsolute: string | number): number {
if (typeof percentageOrAbsolute === "string") if (typeof percentageOrAbsolute === "string")
return size * parseFloat(percentageOrAbsolute.substr(0, percentageOrAbsolute.length - 1)) / 100; return size * parseFloat(percentageOrAbsolute.substr(0, percentageOrAbsolute.length - 1)) / 100;
return percentageOrAbsolute; return percentageOrAbsolute;
} }
private calculateAnimationViewport (animation: Animation, viewport: Viewport) { private calculateAnimationViewport(animation: Animation, viewport: Viewport) {
this.skeleton.setToSetupPose(); this.skeleton.setToSetupPose();
let steps = 100, stepTime = animation.duration ? animation.duration / steps : 0, time = 0; let steps = 100, stepTime = animation.duration ? animation.duration / steps : 0, time = 0;
let minX = 100000000, maxX = -100000000, minY = 100000000, maxY = -100000000; let minX = 100000000, maxX = -100000000, minY = 100000000, maxY = -100000000;
let offset = new spine.Vector2(), size = new spine.Vector2(); let offset = new Vector2(), size = new Vector2();
for (let i = 0; i < steps; i++, time += stepTime) { for (let i = 0; i < steps; i++, time += stepTime) {
animation.apply(this.skeleton, time, time, false, null, 1, MixBlend.setup, MixDirection.mixIn); animation.apply(this.skeleton, time, time, false, null, 1, MixBlend.setup, MixDirection.mixIn);
@ -742,7 +744,7 @@ module spine {
viewport.height = maxY - minY; viewport.height = maxY - minY;
} }
private drawFrame (requestNextFrame = true) { private drawFrame(requestNextFrame = true) {
try { try {
if (this.error) return; if (this.error) return;
if (requestNextFrame && !this.stopRequestAnimationFrame) requestAnimationFrame(() => this.drawFrame()); if (requestNextFrame && !this.stopRequestAnimationFrame) requestAnimationFrame(() => this.drawFrame());
@ -762,7 +764,7 @@ module spine {
if (skeleton) { if (skeleton) {
// Resize the canvas. // Resize the canvas.
let renderer = this.sceneRenderer; let renderer = this.sceneRenderer;
renderer.resize(webgl.ResizeMode.Expand); renderer.resize(ResizeMode.Expand);
let playDelta = this.paused ? 0 : delta * this.speed; let playDelta = this.paused ? 0 : delta * this.speed;
if (config.frame) config.frame(this, playDelta); if (config.frame) config.frame(this, playDelta);
@ -882,15 +884,15 @@ module spine {
} }
} }
stopRendering () { stopRendering() {
this.stopRequestAnimationFrame = true; this.stopRequestAnimationFrame = true;
} }
private hidePopup (id: string): boolean { private hidePopup(id: string): boolean {
return this.popup && this.popup.hide(id); return this.popup && this.popup.hide(id);
} }
private showSpeedDialog (speedButton: HTMLElement) { private showSpeedDialog(speedButton: HTMLElement) {
let id = "speed"; let id = "speed";
if (this.hidePopup(id)) return; if (this.hidePopup(id)) return;
@ -910,7 +912,7 @@ module spine {
popup.show(); popup.show();
} }
private showAnimationsDialog (animationsButton: HTMLElement) { private showAnimationsDialog(animationsButton: HTMLElement) {
let id = "animations"; let id = "animations";
if (this.hidePopup(id)) return; if (this.hidePopup(id)) return;
if (!this.skeleton || !this.skeleton.data.animations.length) return; if (!this.skeleton || !this.skeleton.data.animations.length) return;
@ -940,7 +942,7 @@ module spine {
popup.show(); popup.show();
} }
private showSkinsDialog (skinButton: HTMLElement) { private showSkinsDialog(skinButton: HTMLElement) {
let id = "skins"; let id = "skins";
if (this.hidePopup(id)) return; if (this.hidePopup(id)) return;
if (!this.skeleton || !this.skeleton.data.animations.length) return; if (!this.skeleton || !this.skeleton.data.animations.length) return;
@ -968,7 +970,7 @@ module spine {
popup.show(); popup.show();
} }
private showSettingsDialog (settingsButton: HTMLElement) { private showSettingsDialog(settingsButton: HTMLElement) {
let id = "settings"; let id = "settings";
if (this.hidePopup(id)) return; if (this.hidePopup(id)) return;
if (!this.skeleton || !this.skeleton.data.animations.length) return; if (!this.skeleton || !this.skeleton.data.animations.length) return;
@ -996,7 +998,7 @@ module spine {
popup.show(); popup.show();
} }
private showError (message: string, error: Error = null) { private showError(message: string, error: Error = null) {
if (this.error) { if (this.error) {
if (error) throw error; // Don't lose error if showError throws, is caught, and showError is called again. if (error) throw error; // Don't lose error if showError throws, is caught, and showError is called again.
} else { } else {
@ -1008,20 +1010,20 @@ module spine {
throw (error ? error : new Error(message)); throw (error ? error : new Error(message));
} }
} }
} }
class Popup { class Popup {
public dom: HTMLElement; public dom: HTMLElement;
private className: string; private className: string;
constructor (private id: string, private button: HTMLElement, private player: SpinePlayer, parent: HTMLElement, htmlContent: string) { 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>`); this.dom = createElement(/*html*/`<div class="spine-player-popup spine-player-hidden"></div>`);
this.dom.innerHTML = htmlContent; this.dom.innerHTML = htmlContent;
parent.appendChild(this.dom); parent.appendChild(this.dom);
this.className = "spine-player-button-icon-" + id + "-selected"; this.className = "spine-player-button-icon-" + id + "-selected";
} }
hide (id: string): boolean { hide(id: string): boolean {
this.dom.remove(); this.dom.remove();
this.button.classList.remove(this.className); this.button.classList.remove(this.className);
if (this.id == id) { if (this.id == id) {
@ -1030,7 +1032,7 @@ module spine {
} }
} }
show () { show() {
this.player.popup = this; this.player.popup = this;
this.button.classList.add(this.className); this.button.classList.add(this.className);
this.dom.classList.remove("spine-player-hidden"); this.dom.classList.remove("spine-player-hidden");
@ -1063,16 +1065,16 @@ module spine {
}; };
window.addEventListener("click", windowClickListener); window.addEventListener("click", windowClickListener);
} }
} }
class Switch { class Switch {
private switch: HTMLElement; private switch: HTMLElement;
private enabled = false; private enabled = false;
public change: (value: boolean) => void; public change: (value: boolean) => void;
constructor (private text: string) { } constructor(private text: string) { }
create (): HTMLElement { create(): HTMLElement {
this.switch = createElement(/*html*/` this.switch = createElement(/*html*/`
<div class="spine-player-switch"> <div class="spine-player-switch">
<span class="spine-player-switch-text">${this.text}</span> <span class="spine-player-switch-text">${this.text}</span>
@ -1087,26 +1089,26 @@ module spine {
return this.switch; return this.switch;
} }
setEnabled (enabled: boolean) { setEnabled(enabled: boolean) {
if (enabled) this.switch.classList.add("active"); if (enabled) this.switch.classList.add("active");
else this.switch.classList.remove("active"); else this.switch.classList.remove("active");
this.enabled = enabled; this.enabled = enabled;
} }
isEnabled (): boolean { isEnabled(): boolean {
return this.enabled; return this.enabled;
} }
} }
class Slider { class Slider {
private slider: HTMLElement; private slider: HTMLElement;
private value: HTMLElement; private value: HTMLElement;
private knob: HTMLElement; private knob: HTMLElement;
public change: (percentage: number) => void; public change: (percentage: number) => void;
constructor (public snaps = 0, public snapPercentage = 0.1, public big = false) { } constructor(public snaps = 0, public snapPercentage = 0.1, public big = false) { }
create (): HTMLElement { create(): HTMLElement {
this.slider = createElement(/*html*/` this.slider = createElement(/*html*/`
<div class="spine-player-slider ${this.big ? "big" : ""}"> <div class="spine-player-slider ${this.big ? "big" : ""}">
<div class="spine-player-slider-value"></div> <div class="spine-player-slider-value"></div>
@ -1117,7 +1119,7 @@ module spine {
this.setValue(0); this.setValue(0);
let dragging = false; let dragging = false;
new spine.webgl.Input(this.slider).addListener({ new Input(this.slider).addListener({
down: (x, y) => { down: (x, y) => {
dragging = true; dragging = true;
this.value.classList.add("hovering"); this.value.classList.add("hovering");
@ -1138,7 +1140,7 @@ module spine {
return this.slider; return this.slider;
} }
setValue (percentage: number): number { setValue(percentage: number): number {
percentage = Math.max(0, Math.min(1, percentage)); percentage = Math.max(0, Math.min(1, percentage));
if (this.snaps) { if (this.snaps) {
let snap = 1 / this.snaps; let snap = 1 / this.snaps;
@ -1154,34 +1156,33 @@ module spine {
// this.knob.style.left = "" + (-8 + percentage * this.slider.clientWidth) + "px"; // this.knob.style.left = "" + (-8 + percentage * this.slider.clientWidth) + "px";
return percentage; return percentage;
} }
} }
function findWithClass (element: HTMLElement, className: string): HTMLElement { function findWithClass(element: HTMLElement, className: string): HTMLElement {
return element.getElementsByClassName(className)[0] as HTMLElement; return element.getElementsByClassName(className)[0] as HTMLElement;
} }
function createElement (html: string): HTMLElement { function createElement(html: string): HTMLElement {
let div = document.createElement("div"); let div = document.createElement("div");
div.innerHTML = html; div.innerHTML = html;
return div.children[0] as HTMLElement; return div.children[0] as HTMLElement;
} }
function removeClass (elements: HTMLCollection, clazz: string) { function removeClass(elements: HTMLCollection, clazz: string) {
for (let i = 0; i < elements.length; i++) for (let i = 0; i < elements.length; i++)
elements[i].classList.remove(clazz); elements[i].classList.remove(clazz);
} }
function toString (object: any) { function toString(object: any) {
return JSON.stringify(object) return JSON.stringify(object)
.replace(/&/g, "&amp;") .replace(/&/g, "&amp;")
.replace(/</g, "&lt;") .replace(/</g, "&lt;")
.replace(/>/g, "&gt;") .replace(/>/g, "&gt;")
.replace(/"/g, "&#34;") .replace(/"/g, "&#34;")
.replace(/'/g, "&#39;"); .replace(/'/g, "&#39;");
}
const BONE_INNER_OVER = new spine.Color(0.478, 0, 0, 0.25);
const BONE_OUTER_OVER = new spine.Color(1, 1, 1, 1);
const BONE_INNER = new spine.Color(0.478, 0, 0, 0.5);
const BONE_OUTER = new spine.Color(1, 0, 0, 0.8);
} }
const BONE_INNER_OVER = new Color(0.478, 0, 0, 0.25);
const BONE_OUTER_OVER = new Color(1, 1, 1, 1);
const BONE_INNER = new Color(0.478, 0, 0, 0.5);
const BONE_OUTER = new Color(1, 0, 0, 0.8);

View File

@ -27,10 +27,9 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
declare function CodeMirror (el: Element, config: any): void; declare function CodeMirror(el: Element, config: any): void;
module spine { export class SpinePlayerEditor {
export class SpinePlayerEditor {
private static DEFAULT_CODE = private static DEFAULT_CODE =
` `
<script src="https://esotericsoftware.com/files/spine-player/4.0/spine-player.js"></script> <script src="https://esotericsoftware.com/files/spine-player/4.0/spine-player.js"></script>
@ -58,11 +57,11 @@ body { margin: 0px; }
private code: any; private code: any;
private player: HTMLIFrameElement; private player: HTMLIFrameElement;
constructor (parent: HTMLElement) { constructor(parent: HTMLElement) {
this.render(parent); this.render(parent);
} }
private render (parent: HTMLElement) { private render(parent: HTMLElement) {
let dom = /*html*/` let dom = /*html*/`
<div class="spine-player-editor-container"> <div class="spine-player-editor-container">
<div class="spine-player-editor-code"></div> <div class="spine-player-editor-code"></div>
@ -91,19 +90,19 @@ body { margin: 0px; }
}) })
} }
setPreAndPostfix (prefix: string, postfix: string) { setPreAndPostfix(prefix: string, postfix: string) {
this.prefix = prefix; this.prefix = prefix;
this.postfix = postfix; this.postfix = postfix;
this.startPlayer() this.startPlayer()
} }
setCode (code: string) { setCode(code: string) {
this.code.setValue(code); this.code.setValue(code);
this.startPlayer(); this.startPlayer();
} }
private timerId = 0; private timerId = 0;
startPlayer () { startPlayer() {
clearTimeout(this.timerId); clearTimeout(this.timerId);
this.timerId = setTimeout(() => { this.timerId = setTimeout(() => {
let code = this.code.getDoc().getValue(); let code = this.code.getDoc().getValue();
@ -113,5 +112,4 @@ body { margin: 0px; }
this.player.src = "data:text/html;base64," + code; this.player.src = "data:text/html;base64," + code;
}, 500); }, 500);
} }
}
} }

View File

@ -0,0 +1,27 @@
export * from './Player';
export * from './PlayerEditor';
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";
// Before modularization, we would expose spine-core on the global
// `spine` object, and spine-webgl on the global `spine.webgl` object.
// This was used by clients when including spine-webgl via <script src="spine-webgl.js">
//
// Now with modularization and using esbuild for bundling, we need to emulate this old
// behaviour as to not break old clients.
//
// We pass `--global-name=spine` to esbuild. This will create an object containing
// all exports and assign it to the global variable called `spine`.
//
// That solves half the issue. We also need to assign the exports object to
// `spine.webgl`. esbuild creates a local variable called `scr_exports` pointing
// to the exports object. We get to it via eval, then assign it to itself, on a new
// property called `webgl`. The client can then access the APIs through `spine` and
// `spine.webgl` as before (with the caveat that both spine-core and spine-webgl are
// now in `spine` and `spine.webgl`).
//
// This will break if esbuild renames the variable `src_exports` pointing to
// the exports object.
let exports = eval("src_exports");
if (exports) exports.webgl = exports;

View File

@ -0,0 +1,30 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "./src",
"outDir": "./dist",
"paths": {
"@esotericsoftware/spine-core": [
"../spine-core/src"
],
"@esotericsoftware/spine-webgl": [
"../spine-webgl/src"
]
}
},
"include": [
"**/*.ts"
],
"exclude": [
"dist/**/*.d.ts"
],
"references": [
{
"path": "../spine-core"
},
{
"path": "../spine-webgl"
}
]
}

View File

@ -3,6 +3,10 @@
"version": "4.0.1", "version": "4.0.1",
"description": "The official Spine Runtimes for the web.", "description": "The official Spine Runtimes for the web.",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*"
],
"scripts": {}, "scripts": {},
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -27,14 +27,15 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { AssetManagerBase, Downloader } from "spine-core"; import { AssetManagerBase, Downloader } from "@esotericsoftware/spine-core"
import { ManagedWebGLRenderingContext } from "./WebGL"; import { ManagedWebGLRenderingContext } from "./WebGL";
import { GLTexture } from "./GLTexture";
export class AssetManager extends AssetManagerBase { export class AssetManager extends AssetManagerBase {
constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "", downloader: Downloader = null) { constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "", downloader: Downloader = null) {
super((image: HTMLImageElement | ImageBitmap) => { super((image: HTMLImageElement | ImageBitmap) => {
return new spine.webgl.GLTexture(context, image); return new GLTexture(context, image);
}, pathPrefix, downloader); }, pathPrefix, downloader);
} }
} }

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Texture, Disposable, Restorable, TextureFilter, TextureWrap } from "spine-core"; import { Texture, Disposable, Restorable, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
import { ManagedWebGLRenderingContext } from "./WebGL"; import { ManagedWebGLRenderingContext } from "./WebGL";
export class GLTexture extends Texture implements Disposable, Restorable { export class GLTexture extends Texture implements Disposable, Restorable {

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Pool } from "spine-core"; import { Pool } from "@esotericsoftware/spine-core";
export class Input { export class Input {
element: HTMLElement; element: HTMLElement;
@ -38,7 +38,7 @@ export class Input {
private listeners = new Array<InputListener>(); private listeners = new Array<InputListener>();
touchesPool = new Pool<Touch>(() => { touchesPool = new Pool<Touch>(() => {
return new spine.webgl.Touch(0, 0, 0); return new Touch(0, 0, 0);
}); });
constructor(element: HTMLElement) { constructor(element: HTMLElement) {

View File

@ -27,6 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Color, TimeKeeper } from "@esotericsoftware/spine-core";
import { GLTexture } from "./GLTexture"; import { GLTexture } from "./GLTexture";
import { ResizeMode, SceneRenderer } from "./SceneRenderer"; import { ResizeMode, SceneRenderer } from "./SceneRenderer";
@ -44,9 +45,9 @@ export class LoadingScreen {
private angle = 0; private angle = 0;
private fadeOut = 0; private fadeOut = 0;
private fadeIn = 0; private fadeIn = 0;
private timeKeeper = new spine.TimeKeeper(); private timeKeeper = new TimeKeeper();
backgroundColor = new spine.Color(0.135, 0.135, 0.135, 1); backgroundColor = new Color(0.135, 0.135, 0.135, 1);
private tempColor = new spine.Color(); private tempColor = new Color();
constructor(renderer: SceneRenderer) { constructor(renderer: SceneRenderer) {
this.renderer = renderer; this.renderer = renderer;

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Disposable, Restorable } from "spine-core"; import { Disposable, Restorable } from "@esotericsoftware/spine-core";
import { Shader } from "./Shader"; import { Shader } from "./Shader";
import { ManagedWebGLRenderingContext } from "./WebGL"; import { ManagedWebGLRenderingContext } from "./WebGL";

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Disposable } from "spine-core"; import { Disposable } from "@esotericsoftware/spine-core";
import { GLTexture } from "./GLTexture"; import { GLTexture } from "./GLTexture";
import { Mesh, Position2Attribute, ColorAttribute, TexCoordAttribute, Color2Attribute } from "./Mesh"; import { Mesh, Position2Attribute, ColorAttribute, TexCoordAttribute, Color2Attribute } from "./Mesh";
import { Shader } from "./Shader"; import { Shader } from "./Shader";

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Color, Disposable, Skeleton, MathUtils, TextureAtlasRegion } from "spine-core"; import { Color, Disposable, Skeleton, MathUtils, TextureAtlasRegion } from "@esotericsoftware/spine-core";
import { OrthoCamera } from "./Camera"; import { OrthoCamera } from "./Camera";
import { GLTexture } from "./GLTexture"; import { GLTexture } from "./GLTexture";
import { PolygonBatcher } from "./PolygonBatcher"; import { PolygonBatcher } from "./PolygonBatcher";

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Disposable, Restorable } from "spine-core"; import { Disposable, Restorable } from "@esotericsoftware/spine-core";
import { ManagedWebGLRenderingContext } from "./WebGL"; import { ManagedWebGLRenderingContext } from "./WebGL";
export class Shader implements Disposable, Restorable { export class Shader implements Disposable, Restorable {

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Disposable, Color, Vector2, MathUtils } from "spine-core"; import { Disposable, Color, Vector2, MathUtils } from "@esotericsoftware/spine-core";
import { Mesh, Position2Attribute, ColorAttribute } from "./Mesh"; import { Mesh, Position2Attribute, ColorAttribute } from "./Mesh";
import { Shader } from "./Shader"; import { Shader } from "./Shader";
import { ManagedWebGLRenderingContext } from "./WebGL"; import { ManagedWebGLRenderingContext } from "./WebGL";

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Disposable, Color, SkeletonBounds, Utils, Skeleton, RegionAttachment, MeshAttachment, PathAttachment, ClippingAttachment } from "spine-core"; import { Disposable, Color, SkeletonBounds, Utils, Skeleton, RegionAttachment, MeshAttachment, PathAttachment, ClippingAttachment } from "@esotericsoftware/spine-core";
import { ShapeRenderer } from "./ShapeRenderer"; import { ShapeRenderer } from "./ShapeRenderer";
import { ManagedWebGLRenderingContext } from "./WebGL"; import { ManagedWebGLRenderingContext } from "./WebGL";

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { NumberArrayLike, VertexEffect, Color, SkeletonClipping, Vector2, Utils, Skeleton, BlendMode, RegionAttachment, TextureAtlasRegion, MeshAttachment, ClippingAttachment } from "spine-core"; import { NumberArrayLike, VertexEffect, Color, SkeletonClipping, Vector2, Utils, Skeleton, BlendMode, RegionAttachment, TextureAtlasRegion, MeshAttachment, ClippingAttachment } from "@esotericsoftware/spine-core";
import { GLTexture } from "./GLTexture"; import { GLTexture } from "./GLTexture";
import { PolygonBatcher } from "./PolygonBatcher"; import { PolygonBatcher } from "./PolygonBatcher";
import { ManagedWebGLRenderingContext, WebGLBlendModeConverter } from "./WebGL"; import { ManagedWebGLRenderingContext, WebGLBlendModeConverter } from "./WebGL";
@ -120,7 +120,7 @@ export class SkeletonRenderer {
renderable.numVertices = (mesh.worldVerticesLength >> 1); renderable.numVertices = (mesh.worldVerticesLength >> 1);
renderable.numFloats = renderable.numVertices * clippedVertexSize; renderable.numFloats = renderable.numVertices * clippedVertexSize;
if (renderable.numFloats > renderable.vertices.length) { if (renderable.numFloats > renderable.vertices.length) {
renderable.vertices = this.vertices = spine.Utils.newFloatArray(renderable.numFloats); renderable.vertices = this.vertices = Utils.newFloatArray(renderable.numFloats);
} }
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, renderable.vertices, 0, clippedVertexSize); mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, renderable.vertices, 0, clippedVertexSize);
triangles = mesh.triangles; triangles = mesh.triangles;

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
import { Restorable, BlendMode } from "spine-core"; import { Restorable, BlendMode } from "@esotericsoftware/spine-core";
export class ManagedWebGLRenderingContext { export class ManagedWebGLRenderingContext {
public canvas: HTMLCanvasElement | OffscreenCanvas; public canvas: HTMLCanvasElement | OffscreenCanvas;

View File

@ -14,7 +14,7 @@ export * from './SkeletonRenderer';
export * from './Vector3'; export * from './Vector3';
export * from './WebGL'; export * from './WebGL';
export * from "spine-core"; export * from "@esotericsoftware/spine-core";
// Before modularization, we would expose spine-core on the global // Before modularization, we would expose spine-core on the global
// `spine` object, and spine-webgl on the global `spine.webgl` object. // `spine` object, and spine-webgl on the global `spine.webgl` object.
@ -35,9 +35,5 @@ export * from "spine-core";
// //
// This will break if esbuild renames the variable `src_exports` pointing to // This will break if esbuild renames the variable `src_exports` pointing to
// the exports object. // the exports object.
declare global {
var spine: any;
}
let exports = eval("src_exports"); let exports = eval("src_exports");
exports.webgl = exports; if (exports) exports.webgl = exports;

View File

@ -5,7 +5,7 @@
"rootDir": "./src", "rootDir": "./src",
"outDir": "./dist", "outDir": "./dist",
"paths": { "paths": {
"spine-core": [ "@esotericsoftware/spine-core": [
"../spine-core/src" "../spine-core/src"
] ]
} }

View File

@ -10,7 +10,6 @@
"ES2015" "ES2015"
], ],
"declaration": true, "declaration": true,
"declarationMap": true,
"composite": true, "composite": true,
"moduleResolution": "node", "moduleResolution": "node",
} }

View File

@ -6,6 +6,12 @@
}, },
{ {
"path": "./spine-canvas" "path": "./spine-canvas"
},
{
"path": "./spine-webgl"
},
{
"path": "./spine-player"
} }
] ]
} }