mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 17:56:04 +08:00
[ts] AssetManager and Player updates to support sharing assets.
* Removed # parsing from SpinePlayer, replaced with config.jsonField. * Refactored AssetMananger to split downloading from asset creation. * Allow Downloader to be specified in SpinePlayer config so multiple players don't download the same assets. * Added AssetManager#loadJson so the text is only parsed once. * TextureAtlas is still parsed multiple times. :( * AssetMananger clean up, catch more errors, log error, don't keep rendering after an error.
This commit is contained in:
parent
214c778bac
commit
43cb6dfe22
@ -31,8 +31,8 @@
|
|||||||
|
|
||||||
module spine.canvas {
|
module spine.canvas {
|
||||||
export class AssetManager extends spine.AssetManager {
|
export class AssetManager extends spine.AssetManager {
|
||||||
constructor (pathPrefix: string = "") {
|
constructor (pathPrefix: string = "", downloader: Downloader = null) {
|
||||||
super((image: HTMLImageElement) => { return new spine.canvas.CanvasTexture(image); }, pathPrefix);
|
super((image: HTMLImageElement) => { return new spine.canvas.CanvasTexture(image); }, pathPrefix, downloader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,117 +31,91 @@ module spine {
|
|||||||
export class AssetManager implements Disposable {
|
export class AssetManager implements Disposable {
|
||||||
private pathPrefix: string;
|
private pathPrefix: string;
|
||||||
private textureLoader: (image: HTMLImageElement) => any;
|
private textureLoader: (image: HTMLImageElement) => any;
|
||||||
|
private downloader: Downloader;
|
||||||
private assets: Map<any> = {};
|
private assets: Map<any> = {};
|
||||||
private errors: Map<string> = {};
|
private errors: Map<string> = {};
|
||||||
private toLoad = 0;
|
private toLoad = 0;
|
||||||
private loaded = 0;
|
private loaded = 0;
|
||||||
private rawDataUris: Map<string> = {};
|
|
||||||
|
|
||||||
constructor (textureLoader: (image: HTMLImageElement) => any, pathPrefix: string = "") {
|
constructor (textureLoader: (image: HTMLImageElement) => any, pathPrefix: string = "", downloader: Downloader = null) {
|
||||||
this.textureLoader = textureLoader;
|
this.textureLoader = textureLoader;
|
||||||
this.pathPrefix = pathPrefix;
|
this.pathPrefix = pathPrefix;
|
||||||
|
this.downloader = downloader || new Downloader();
|
||||||
}
|
}
|
||||||
|
|
||||||
private downloadText (url: string, success: (data: string) => void, error: (status: number, responseText: string) => void) {
|
private start (path: string): string {
|
||||||
let request = new XMLHttpRequest();
|
this.toLoad++;
|
||||||
request.overrideMimeType("text/html");
|
return this.pathPrefix + path;
|
||||||
if (this.rawDataUris[url]) url = this.rawDataUris[url];
|
|
||||||
request.open("GET", url, true);
|
|
||||||
request.onload = () => {
|
|
||||||
if (request.status == 200) {
|
|
||||||
success(request.responseText);
|
|
||||||
} else {
|
|
||||||
error(request.status, request.responseText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.onerror = () => {
|
|
||||||
error(request.status, request.responseText);
|
|
||||||
}
|
|
||||||
request.send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private downloadBinary (url: string, success: (data: Uint8Array) => void, error: (status: number, responseText: string) => void) {
|
private success (path: string, callback: (path: string, data: any) => void, asset: any) {
|
||||||
let request = new XMLHttpRequest();
|
this.toLoad--;
|
||||||
if (this.rawDataUris[url]) url = this.rawDataUris[url];
|
this.loaded++;
|
||||||
request.open("GET", url, true);
|
this.assets[path] = asset;
|
||||||
request.responseType = "arraybuffer";
|
if (callback) callback(path, asset);
|
||||||
request.onload = () => {
|
|
||||||
if (request.status == 200) {
|
|
||||||
success(new Uint8Array(request.response as ArrayBuffer));
|
|
||||||
} else {
|
|
||||||
error(request.status, request.responseText);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
request.onerror = () => {
|
private error (path: string, callback: (path: string, error: string) => void, message: string) {
|
||||||
error(request.status, request.responseText);
|
this.toLoad--;
|
||||||
}
|
this.loaded++;
|
||||||
request.send();
|
this.errors[path] = message;
|
||||||
|
if (callback) callback(path, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRawDataURI(path: string, data: string) {
|
setRawDataURI(path: string, data: string) {
|
||||||
this.rawDataUris[this.pathPrefix + path] = data;
|
this.downloader.rawDataUris[this.pathPrefix + path] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadBinary(path: string,
|
loadBinary(path: string,
|
||||||
success: (path: string, binary: Uint8Array) => void = null,
|
success: (path: string, binary: Uint8Array) => void = null,
|
||||||
error: (path: string, error: string) => void = null) {
|
error: (path: string, error: string) => void = null) {
|
||||||
path = this.pathPrefix + path;
|
path = this.start(path);
|
||||||
this.toLoad++;
|
|
||||||
|
|
||||||
this.downloadBinary(path, (data: Uint8Array): void => {
|
this.downloader.downloadBinary(path, (data: Uint8Array): void => {
|
||||||
this.assets[path] = data;
|
this.success(path, success, data);
|
||||||
if (success) success(path, data);
|
}, (status: number, responseText: string): void => {
|
||||||
this.toLoad--;
|
this.error(path, error, `Couldn't load binary ${path}: status ${status}, ${responseText}`);
|
||||||
this.loaded++;
|
|
||||||
}, (state: number, responseText: string): void => {
|
|
||||||
this.errors[path] = `Couldn't load binary ${path}: status ${status}, ${responseText}`;
|
|
||||||
if (error) error(path, `Couldn't load binary ${path}: status ${status}, ${responseText}`);
|
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadText(path: string,
|
loadText(path: string,
|
||||||
success: (path: string, text: string) => void = null,
|
success: (path: string, text: string) => void = null,
|
||||||
error: (path: string, error: string) => void = null) {
|
error: (path: string, error: string) => void = null) {
|
||||||
path = this.pathPrefix + path;
|
path = this.start(path);
|
||||||
this.toLoad++;
|
|
||||||
|
|
||||||
this.downloadText(path, (data: string): void => {
|
this.downloader.downloadText(path, (data: string): void => {
|
||||||
this.assets[path] = data;
|
this.success(path, success, data);
|
||||||
if (success) success(path, data);
|
}, (status: number, responseText: string): void => {
|
||||||
this.toLoad--;
|
this.error(path, error, `Couldn't load text ${path}: status ${status}, ${responseText}`);
|
||||||
this.loaded++;
|
});
|
||||||
}, (state: number, responseText: string): void => {
|
}
|
||||||
this.errors[path] = `Couldn't load text ${path}: status ${status}, ${responseText}`;
|
|
||||||
if (error) error(path, `Couldn't load text ${path}: status ${status}, ${responseText}`);
|
loadJson(path: string,
|
||||||
this.toLoad--;
|
success: (path: string, object: object) => void = null,
|
||||||
this.loaded++;
|
error: (path: string, error: string) => void = null) {
|
||||||
|
path = this.start(path);
|
||||||
|
|
||||||
|
this.downloader.downloadJson(path, (data: object): void => {
|
||||||
|
this.success(path, success, data);
|
||||||
|
}, (status: number, responseText: string): void => {
|
||||||
|
this.error(path, error, `Couldn't load JSON ${path}: status ${status}, ${responseText}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTexture (path: string,
|
loadTexture (path: string,
|
||||||
success: (path: string, image: HTMLImageElement) => void = null,
|
success: (path: string, image: HTMLImageElement) => void = null,
|
||||||
error: (path: string, error: string) => void = null) {
|
error: (path: string, error: string) => void = null) {
|
||||||
path = this.pathPrefix + path;
|
path = this.start(path);
|
||||||
let storagePath = path;
|
|
||||||
this.toLoad++;
|
|
||||||
let img = new Image();
|
let img = new Image();
|
||||||
img.crossOrigin = "anonymous";
|
img.crossOrigin = "anonymous";
|
||||||
img.onload = (ev) => {
|
img.onload = (ev) => {
|
||||||
let texture = this.textureLoader(img);
|
this.success(path, success, this.textureLoader(img));
|
||||||
this.assets[storagePath] = texture;
|
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
if (success) success(path, img);
|
|
||||||
}
|
}
|
||||||
img.onerror = (ev) => {
|
img.onerror = (ev) => {
|
||||||
this.errors[path] = `Couldn't load image ${path}`;
|
this.error(path, error, `Couldn't load image ${path}`);
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
if (error) error(path, `Couldn't load image ${path}`);
|
|
||||||
}
|
}
|
||||||
if (this.rawDataUris[path]) path = this.rawDataUris[path];
|
if (this.downloader.rawDataUris[path]) path = this.downloader.rawDataUris[path];
|
||||||
img.src = path;
|
img.src = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,11 +123,10 @@ module spine {
|
|||||||
success: (path: string, atlas: TextureAtlas) => void = null,
|
success: (path: string, atlas: TextureAtlas) => void = null,
|
||||||
error: (path: string, error: string) => void = null
|
error: (path: string, error: string) => void = null
|
||||||
) {
|
) {
|
||||||
|
path = this.start(path);
|
||||||
let parent = path.lastIndexOf("/") >= 0 ? path.substring(0, path.lastIndexOf("/")) : "";
|
let parent = path.lastIndexOf("/") >= 0 ? path.substring(0, path.lastIndexOf("/")) : "";
|
||||||
path = this.pathPrefix + path;
|
|
||||||
this.toLoad++;
|
|
||||||
|
|
||||||
this.downloadText(path, (atlasData: string): void => {
|
this.downloader.downloadText(path, (atlasData: string): void => {
|
||||||
let pagesLoaded: any = { count: 0 };
|
let pagesLoaded: any = { count: 0 };
|
||||||
let atlasPages = new Array<string>();
|
let atlasPages = new Array<string>();
|
||||||
try {
|
try {
|
||||||
@ -165,11 +138,7 @@ module spine {
|
|||||||
return new FakeTexture(image);
|
return new FakeTexture(image);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let ex = e as Error;
|
this.error(path, error, `Couldn't load texture atlas ${path}: ${e.message}`);
|
||||||
this.errors[path] = `Couldn't load texture atlas ${path}: ${ex.message}`;
|
|
||||||
if (error) error(path, `Couldn't load texture atlas ${path}: ${ex.message}`);
|
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,57 +150,37 @@ module spine {
|
|||||||
if (pagesLoaded.count == atlasPages.length) {
|
if (pagesLoaded.count == atlasPages.length) {
|
||||||
if (!pageLoadError) {
|
if (!pageLoadError) {
|
||||||
try {
|
try {
|
||||||
let atlas = new TextureAtlas(atlasData, (path: string) => {
|
this.success(path, success, new TextureAtlas(atlasData, (path: string) => {
|
||||||
return this.get(parent == "" ? path : parent + "/" + path);
|
return this.get(parent == "" ? path : parent + "/" + path);
|
||||||
});
|
}));
|
||||||
this.assets[path] = atlas;
|
|
||||||
if (success) success(path, atlas);
|
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let ex = e as Error;
|
this.error(path, error, `Couldn't load texture atlas ${path}: ${e.message}`);
|
||||||
this.errors[path] = `Couldn't load texture atlas ${path}: ${ex.message}`;
|
|
||||||
if (error) error(path, `Couldn't load texture atlas ${path}: ${ex.message}`);
|
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.errors[path] = `Couldn't load texture atlas page ${imagePath}} of atlas ${path}`;
|
|
||||||
if (error) error(path, `Couldn't load texture atlas page ${imagePath} of atlas ${path}`);
|
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
}
|
}
|
||||||
|
} else
|
||||||
|
this.error(path, error, `Couldn't load texture atlas page ${imagePath}} of atlas ${path}`);
|
||||||
}
|
}
|
||||||
}, (imagePath: string, errorMessage: string) => {
|
}, (imagePath: string, errorMessage: string) => {
|
||||||
pageLoadError = true;
|
pageLoadError = true;
|
||||||
pagesLoaded.count++;
|
pagesLoaded.count++;
|
||||||
|
|
||||||
if (pagesLoaded.count == atlasPages.length) {
|
if (pagesLoaded.count == atlasPages.length)
|
||||||
this.errors[path] = `Couldn't load texture atlas page ${imagePath}} of atlas ${path}`;
|
this.error(path, error, `Couldn't load texture atlas page ${imagePath}} of atlas ${path}`);
|
||||||
if (error) error(path, `Couldn't load texture atlas page ${imagePath} of atlas ${path}`);
|
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, (state: number, responseText: string): void => {
|
}, (status: number, responseText: string): void => {
|
||||||
this.errors[path] = `Couldn't load texture atlas ${path}: status ${status}, ${responseText}`;
|
this.error(path, error, `Couldn't load texture atlas ${path}: status ${status}, ${responseText}`);
|
||||||
if (error) error(path, `Couldn't load texture atlas ${path}: status ${status}, ${responseText}`);
|
|
||||||
this.toLoad--;
|
|
||||||
this.loaded++;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get (path: string) {
|
get (path: string) {
|
||||||
path = this.pathPrefix + path;
|
return this.assets[this.pathPrefix + path];
|
||||||
return this.assets[path];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remove (path: string) {
|
remove (path: string) {
|
||||||
path = this.pathPrefix + path;
|
path = this.pathPrefix + path;
|
||||||
let asset = this.assets[path];
|
let asset = this.assets[path];
|
||||||
if ((<any>asset).dispose) (<any>asset).dispose();
|
if ((<any>asset).dispose) (<any>asset).dispose();
|
||||||
this.assets[path] = null;
|
delete this.assets[path];
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll () {
|
removeAll () {
|
||||||
@ -266,4 +215,66 @@ module spine {
|
|||||||
return this.errors;
|
return this.errors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Downloader {
|
||||||
|
private callbacks: Map<Array<Function>> = {};
|
||||||
|
rawDataUris: Map<string> = {};
|
||||||
|
|
||||||
|
downloadText (url: string, success: (data: string) => void, error: (status: number, responseText: string) => void) {
|
||||||
|
if (this.rawDataUris[url]) url = this.rawDataUris[url];
|
||||||
|
if (this.start(url, success, error)) return;
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
request.overrideMimeType("text/html");
|
||||||
|
request.open("GET", url, true);
|
||||||
|
let done = () => {
|
||||||
|
this.finish(url, request.status, request.responseText);
|
||||||
|
};
|
||||||
|
request.onload = done;
|
||||||
|
request.onerror = done;
|
||||||
|
request.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadJson (url: string, success: (data: object) => void, error: (status: number, responseText: string) => void) {
|
||||||
|
this.downloadText(url, (data: string): void => {
|
||||||
|
success(JSON.parse(data));
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadBinary (url: string, success: (data: Uint8Array) => void, error: (status: number, responseText: string) => void) {
|
||||||
|
if (this.rawDataUris[url]) url = this.rawDataUris[url];
|
||||||
|
if (this.start(url, success, error)) return;
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
request.open("GET", url, true);
|
||||||
|
request.responseType = "arraybuffer";
|
||||||
|
let onerror = () => {
|
||||||
|
this.finish(url, request.status, request.responseText);
|
||||||
|
};
|
||||||
|
request.onload = () => {
|
||||||
|
if (request.status == 200)
|
||||||
|
this.finish(url, 200, new Uint8Array(request.response as ArrayBuffer));
|
||||||
|
else
|
||||||
|
onerror();
|
||||||
|
};
|
||||||
|
request.onerror = onerror;
|
||||||
|
request.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
private start (url: string, success: any, error: any) {
|
||||||
|
let callbacks = this.callbacks[url];
|
||||||
|
try {
|
||||||
|
if (callbacks) return true;
|
||||||
|
this.callbacks[url] = callbacks = [];
|
||||||
|
} finally {
|
||||||
|
callbacks.push(success, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private finish (url: string, status: number, data: any) {
|
||||||
|
let callbacks = this.callbacks[url];
|
||||||
|
delete this.callbacks[url];
|
||||||
|
let args = status == 200 ? [data] : [status, data];
|
||||||
|
for (let i = args.length - 1, n = callbacks.length; i < n; i += 2)
|
||||||
|
callbacks[i].apply(null, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,13 +40,16 @@ module spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SpinePlayerConfig {
|
export interface SpinePlayerConfig {
|
||||||
/* the URL of the skeleton .json file */
|
/* The URL of the skeleton JSON file (.json). */
|
||||||
jsonUrl: string
|
jsonUrl: string
|
||||||
|
|
||||||
/* the URL of the skeleton .skel file */
|
/* Optional: the name of a field in the JSON that holds the skeleton data. */
|
||||||
|
jsonField: string
|
||||||
|
|
||||||
|
/* The URL of the skeleton binary file (.skel). */
|
||||||
skelUrl: string
|
skelUrl: string
|
||||||
|
|
||||||
/* the URL of the skeleton .atlas file. Atlas page images are automatically resolved. */
|
/* The URL of the skeleton atlas file (.atlas). Atlas page images are automatically resolved. */
|
||||||
atlasUrl: string
|
atlasUrl: string
|
||||||
|
|
||||||
/* Raw data URIs, mapping from a path to base 64 encoded raw data. When the player
|
/* Raw data URIs, mapping from a path to base 64 encoded raw data. When the player
|
||||||
@ -132,6 +135,9 @@ module spine {
|
|||||||
|
|
||||||
/* Optional: callback when the widget could not be loaded. */
|
/* Optional: callback when the widget could not be loaded. */
|
||||||
error: (widget: SpinePlayer, msg: string) => void
|
error: (widget: SpinePlayer, msg: string) => void
|
||||||
|
|
||||||
|
/* Optional: the specified downloader is used for the player's asset manager, allowing multiple players to share assets. */
|
||||||
|
downloader: spine.Downloader
|
||||||
}
|
}
|
||||||
|
|
||||||
class Popup {
|
class Popup {
|
||||||
@ -304,6 +310,7 @@ module spine {
|
|||||||
private loadingScreen: spine.webgl.LoadingScreen;
|
private loadingScreen: spine.webgl.LoadingScreen;
|
||||||
private assetManager: spine.webgl.AssetManager;
|
private assetManager: spine.webgl.AssetManager;
|
||||||
|
|
||||||
|
public error: boolean;
|
||||||
// Whether the skeleton was loaded
|
// Whether the skeleton was loaded
|
||||||
public loaded: boolean;
|
public loaded: boolean;
|
||||||
// The loaded skeleton
|
// The loaded skeleton
|
||||||
@ -326,8 +333,7 @@ module spine {
|
|||||||
private stopRequestAnimationFrame = false;
|
private stopRequestAnimationFrame = false;
|
||||||
|
|
||||||
constructor(parent: HTMLElement | string, private config: SpinePlayerConfig) {
|
constructor(parent: HTMLElement | string, private config: SpinePlayerConfig) {
|
||||||
if (typeof parent === "string") this.parent = document.getElementById(parent);
|
this.parent = typeof parent === "string" ? document.getElementById(parent) : parent;
|
||||||
else this.parent = parent;
|
|
||||||
this.parent.appendChild(this.render());
|
this.parent.appendChild(this.render());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,7 +367,7 @@ module spine {
|
|||||||
if (typeof config.debug.meshes === "undefined") config.debug.meshes = false;
|
if (typeof config.debug.meshes === "undefined") config.debug.meshes = false;
|
||||||
|
|
||||||
if (config.animations && config.animation) {
|
if (config.animations && config.animation) {
|
||||||
if (config.animations.indexOf(config.animation) < 0) throw new Error("Default animation '" + config.animation + "' is not contained in the list of selectable animations " + escapeHtml(JSON.stringify(this.config.animations)) + ".");
|
if (config.animations.indexOf(config.animation) < 0) throw new Error("Default animation '" + config.animation + "' is not contained in the list of selectable animations: " + escapeHtml(JSON.stringify(this.config.animations)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.skins && config.skin) {
|
if (config.skins && config.skin) {
|
||||||
@ -370,19 +376,20 @@ module spine {
|
|||||||
|
|
||||||
if (!config.controlBones) config.controlBones = [];
|
if (!config.controlBones) config.controlBones = [];
|
||||||
|
|
||||||
if (typeof config.showControls === "undefined")
|
if (typeof config.showControls === "undefined") config.showControls = true;
|
||||||
config.showControls = true;
|
|
||||||
|
|
||||||
if (typeof config.defaultMix === "undefined")
|
if (typeof config.defaultMix === "undefined") config.defaultMix = 0.25;
|
||||||
config.defaultMix = 0.25;
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
showError(error: string) {
|
showError(error: string) {
|
||||||
|
if (this.error) return;
|
||||||
|
this.error = true;
|
||||||
|
console.log(error);
|
||||||
let errorDom = findWithClass(this.dom, "spine-player-error")[0];
|
let errorDom = findWithClass(this.dom, "spine-player-error")[0];
|
||||||
errorDom.classList.remove("spine-player-hidden");
|
errorDom.classList.remove("spine-player-hidden");
|
||||||
errorDom.innerHTML = `<p style="text-align: center; align-self: center;">${error}</p>`;
|
errorDom.innerHTML = '<p style="text-align: center; align-self: center;">' + error.replace("\n", "<br><br>") + '</p>';
|
||||||
this.config.error(this, error);
|
this.config.error(this, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,7 +421,7 @@ module spine {
|
|||||||
this.config = this.validateConfig(config);
|
this.config = this.validateConfig(config);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError(e);
|
this.showError(e);
|
||||||
return dom
|
return dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -426,12 +433,12 @@ module spine {
|
|||||||
this.sceneRenderer = new spine.webgl.SceneRenderer(this.canvas, this.context, true);
|
this.sceneRenderer = new spine.webgl.SceneRenderer(this.canvas, this.context, true);
|
||||||
this.loadingScreen = new spine.webgl.LoadingScreen(this.sceneRenderer);
|
this.loadingScreen = new spine.webgl.LoadingScreen(this.sceneRenderer);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError("Sorry, your browser does not support WebGL.<br><br>Please use the latest version of Firefox, Chrome, Edge, or Safari.");
|
this.showError("Sorry, your browser does not support WebGL.\nPlease use the latest version of Firefox, Chrome, Edge, or Safari.");
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the assets
|
// Load the assets
|
||||||
this.assetManager = new spine.webgl.AssetManager(this.context);
|
this.assetManager = new spine.webgl.AssetManager(this.context, "", config.downloader);
|
||||||
if (config.rawDataURIs) {
|
if (config.rawDataURIs) {
|
||||||
for (let path in config.rawDataURIs) {
|
for (let path in config.rawDataURIs) {
|
||||||
let data = config.rawDataURIs[path];
|
let data = config.rawDataURIs[path];
|
||||||
@ -439,11 +446,9 @@ module spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let jsonUrl = config.jsonUrl;
|
let jsonUrl = config.jsonUrl;
|
||||||
if (jsonUrl) {
|
if (jsonUrl)
|
||||||
let hash = jsonUrl.indexOf("#");
|
this.assetManager.loadJson(jsonUrl);
|
||||||
if (hash != -1) jsonUrl = jsonUrl.substr(0, hash);
|
else
|
||||||
this.assetManager.loadText(jsonUrl);
|
|
||||||
} else
|
|
||||||
this.assetManager.loadBinary(config.skelUrl);
|
this.assetManager.loadBinary(config.skelUrl);
|
||||||
this.assetManager.loadTextureAtlas(config.atlasUrl);
|
this.assetManager.loadTextureAtlas(config.atlasUrl);
|
||||||
if (config.backgroundImage && config.backgroundImage.url)
|
if (config.backgroundImage && config.backgroundImage.url)
|
||||||
@ -597,9 +602,7 @@ module spine {
|
|||||||
let rows = findWithClass(popup.dom, "spine-player-list")[0];
|
let rows = findWithClass(popup.dom, "spine-player-list")[0];
|
||||||
this.skeleton.data.animations.forEach((animation) => {
|
this.skeleton.data.animations.forEach((animation) => {
|
||||||
// skip animations not whitelisted if a whitelist is given
|
// skip animations not whitelisted if a whitelist is given
|
||||||
if (this.config.animations && this.config.animations.indexOf(animation.name) < 0) {
|
if (this.config.animations && this.config.animations.indexOf(animation.name) < 0) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let row = createElement(/*html*/`
|
let row = createElement(/*html*/`
|
||||||
<li class="spine-player-list-item selectable">
|
<li class="spine-player-list-item selectable">
|
||||||
@ -727,7 +730,8 @@ module spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawFrame (requestNextFrame = true) {
|
drawFrame (requestNextFrame = true) {
|
||||||
if (requestNextFrame && !this.stopRequestAnimationFrame) requestAnimationFrame(() => this.drawFrame());
|
try {
|
||||||
|
if (requestNextFrame && !this.stopRequestAnimationFrame && !this.error) requestAnimationFrame(() => this.drawFrame());
|
||||||
let ctx = this.context;
|
let ctx = this.context;
|
||||||
let gl = ctx.gl;
|
let gl = ctx.gl;
|
||||||
|
|
||||||
@ -847,6 +851,9 @@ module spine {
|
|||||||
|
|
||||||
this.sceneRenderer.camera.zoom = 0;
|
this.sceneRenderer.camera.zoom = 0;
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.showError(`Error: Unable to render skeleton.\n${e.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scale(sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number): Vector2 {
|
scale(sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number): Vector2 {
|
||||||
@ -861,9 +868,10 @@ module spine {
|
|||||||
|
|
||||||
loadSkeleton () {
|
loadSkeleton () {
|
||||||
if (this.loaded) return;
|
if (this.loaded) return;
|
||||||
|
if (this.error) return;
|
||||||
|
|
||||||
if (this.assetManager.hasErrors()) {
|
if (this.assetManager.hasErrors()) {
|
||||||
this.showError("Error: Assets could not be loaded.<br><br>" + escapeHtml(JSON.stringify(this.assetManager.getErrors())));
|
this.showError("Error: Assets could not be loaded.\n" + escapeHtml(JSON.stringify(this.assetManager.getErrors())));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,19 +879,17 @@ module spine {
|
|||||||
let skeletonData: SkeletonData;
|
let skeletonData: SkeletonData;
|
||||||
let jsonUrl = this.config.jsonUrl;
|
let jsonUrl = this.config.jsonUrl;
|
||||||
if (jsonUrl) {
|
if (jsonUrl) {
|
||||||
let hash = jsonUrl.indexOf("#");
|
|
||||||
let field = null;
|
|
||||||
if (hash != -1) {
|
|
||||||
field = jsonUrl.substr(hash + 1);
|
|
||||||
jsonUrl = jsonUrl.substr(0, hash);
|
|
||||||
}
|
|
||||||
let jsonText = this.assetManager.get(jsonUrl);
|
|
||||||
if (field) jsonText = JSON.parse(jsonText)[field];
|
|
||||||
let json = new SkeletonJson(new AtlasAttachmentLoader(atlas));
|
|
||||||
try {
|
try {
|
||||||
skeletonData = json.readSkeletonData(jsonText);
|
let jsonData = this.assetManager.get(jsonUrl);
|
||||||
|
if (!jsonData) throw new Error("Empty JSON data.");
|
||||||
|
if (this.config.jsonField) {
|
||||||
|
jsonData = jsonData[this.config.jsonField];
|
||||||
|
if (!jsonData) throw new Error("JSON field not found: " + this.config.jsonField);
|
||||||
|
}
|
||||||
|
let json = new SkeletonJson(new AtlasAttachmentLoader(atlas));
|
||||||
|
skeletonData = json.readSkeletonData(jsonData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError("Error: Could not load skeleton JSON.<br><br>" + e.toString());
|
this.showError(`Error: Could not load skeleton JSON.\n${e.message}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -892,7 +898,7 @@ module spine {
|
|||||||
try {
|
try {
|
||||||
skeletonData = binary.readSkeletonData(binaryData);
|
skeletonData = binary.readSkeletonData(binaryData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.showError("Error: Could not load skeleton binary.<br><br>" + e.toString());
|
this.showError(`Error: Could not load skeleton binary.\n${e.message}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -905,22 +911,21 @@ module spine {
|
|||||||
if (this.config.controlBones) {
|
if (this.config.controlBones) {
|
||||||
this.config.controlBones.forEach(bone => {
|
this.config.controlBones.forEach(bone => {
|
||||||
if (!skeletonData.findBone(bone)) {
|
if (!skeletonData.findBone(bone)) {
|
||||||
this.showError(`Error: control bone '${bone}' does not exist in skeleton.`);
|
this.showError(`Error: Control bone does not exist in skeleton: ${bone}`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup skin
|
// Setup skin
|
||||||
if (!this.config.skin) {
|
if (!this.config.skin) {
|
||||||
if (skeletonData.skins.length > 0) {
|
if (skeletonData.skins.length > 0) this.config.skin = skeletonData.skins[0].name;
|
||||||
this.config.skin = skeletonData.skins[0].name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.skins && this.config.skin.length > 0) {
|
if (this.config.skins && this.config.skin.length > 0) {
|
||||||
this.config.skins.forEach(skin => {
|
this.config.skins.forEach(skin => {
|
||||||
if (!this.skeleton.data.findSkin(skin)) {
|
if (!this.skeleton.data.findSkin(skin)) {
|
||||||
this.showError(`Error: skin '${skin}' in selectable skin list does not exist in skeleton.`);
|
this.showError(`Error: Skin in config list does not exist in skeleton: ${skin}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -928,16 +933,14 @@ module spine {
|
|||||||
|
|
||||||
if (this.config.skin) {
|
if (this.config.skin) {
|
||||||
if (!this.skeleton.data.findSkin(this.config.skin)) {
|
if (!this.skeleton.data.findSkin(this.config.skin)) {
|
||||||
this.showError(`Error: skin '${this.config.skin}' does not exist in skeleton.`);
|
this.showError(`Error: Skin does not exist in skeleton: ${this.config.skin}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.skeleton.setSkinByName(this.config.skin);
|
this.skeleton.setSkinByName(this.config.skin);
|
||||||
this.skeleton.setSlotsToSetupPose();
|
this.skeleton.setSlotsToSetupPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup empty viewport if none is given and check
|
// Setup empty viewport if none is given and check if all animations for which viewports where given exist.
|
||||||
// if all animations for which viewports where given
|
|
||||||
// exist.
|
|
||||||
if (!this.config.viewport) {
|
if (!this.config.viewport) {
|
||||||
(this.config.viewport as any) = {
|
(this.config.viewport as any) = {
|
||||||
animations: {},
|
animations: {},
|
||||||
@ -952,7 +955,7 @@ module spine {
|
|||||||
} else {
|
} else {
|
||||||
Object.getOwnPropertyNames(this.config.viewport.animations).forEach((animation: string) => {
|
Object.getOwnPropertyNames(this.config.viewport.animations).forEach((animation: string) => {
|
||||||
if (!skeletonData.findAnimation(animation)) {
|
if (!skeletonData.findAnimation(animation)) {
|
||||||
this.showError(`Error: animation '${animation}' for which a viewport was specified does not exist in skeleton.`);
|
this.showError(`Error: Animation for which a viewport was specified does not exist in skeleton: ${animation}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -962,7 +965,7 @@ module spine {
|
|||||||
if (this.config.animations && this.config.animations.length > 0) {
|
if (this.config.animations && this.config.animations.length > 0) {
|
||||||
this.config.animations.forEach(animation => {
|
this.config.animations.forEach(animation => {
|
||||||
if (!this.skeleton.data.findAnimation(animation)) {
|
if (!this.skeleton.data.findAnimation(animation)) {
|
||||||
this.showError(`Error: animation '${animation}' in selectable animation list does not exist in skeleton.`);
|
this.showError(`Error: Animation in config list does not exist in skeleton: ${animation}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -980,7 +983,7 @@ module spine {
|
|||||||
|
|
||||||
if(this.config.animation) {
|
if(this.config.animation) {
|
||||||
if (!skeletonData.findAnimation(this.config.animation)) {
|
if (!skeletonData.findAnimation(this.config.animation)) {
|
||||||
this.showError(`Error: animation '${this.config.animation}' does not exist in skeleton.`);
|
this.showError(`Error: Animation does not exist in skeleton: ${this.config.animation}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.play()
|
this.play()
|
||||||
@ -1127,12 +1130,9 @@ module spine {
|
|||||||
this.playButton.classList.remove("spine-player-button-icon-play");
|
this.playButton.classList.remove("spine-player-button-icon-play");
|
||||||
this.playButton.classList.add("spine-player-button-icon-pause");
|
this.playButton.classList.add("spine-player-button-icon-pause");
|
||||||
|
|
||||||
if (this.config.animation) {
|
if (this.config.animation && !this.animationState.getCurrent(0))
|
||||||
if (!this.animationState.getCurrent(0)) {
|
|
||||||
this.setAnimation(this.config.animation);
|
this.setAnimation(this.config.animation);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private pause () {
|
private pause () {
|
||||||
this.paused = true;
|
this.paused = true;
|
||||||
@ -1237,9 +1237,8 @@ module spine {
|
|||||||
maxX = Math.max(offset.x + size.x, maxX);
|
maxX = Math.max(offset.x + size.x, maxX);
|
||||||
minY = Math.min(offset.y, minY);
|
minY = Math.min(offset.y, minY);
|
||||||
maxY = Math.max(offset.y + size.y, maxY);
|
maxY = Math.max(offset.y + size.y, maxY);
|
||||||
} else {
|
} else
|
||||||
console.log("Bounds of animation " + animationName + " are NaN");
|
console.log("Animation bounds are NaN: " + animationName);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset.x = minX;
|
offset.x = minX;
|
||||||
|
|||||||
@ -29,10 +29,10 @@
|
|||||||
|
|
||||||
module spine.threejs {
|
module spine.threejs {
|
||||||
export class AssetManager extends spine.AssetManager {
|
export class AssetManager extends spine.AssetManager {
|
||||||
constructor (pathPrefix: string = "") {
|
constructor (pathPrefix: string = "", downloader: Downloader = null) {
|
||||||
super((image: HTMLImageElement) => {
|
super((image: HTMLImageElement) => {
|
||||||
return new ThreeJsTexture(image);
|
return new ThreeJsTexture(image);
|
||||||
}, pathPrefix);
|
}, pathPrefix, downloader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,10 +29,10 @@
|
|||||||
|
|
||||||
module spine.webgl {
|
module spine.webgl {
|
||||||
export class AssetManager extends spine.AssetManager {
|
export class AssetManager extends spine.AssetManager {
|
||||||
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "") {
|
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 spine.webgl.GLTexture(context, image);
|
||||||
}, pathPrefix);
|
}, pathPrefix, downloader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user