[ts][widget] First iteration of new Spine Player complete.

This commit is contained in:
badlogic 2018-11-02 16:59:48 +01:00
parent 11f00040f5
commit c0f1f24a34
7 changed files with 805 additions and 163 deletions

View File

@ -16,11 +16,11 @@ declare module spine {
setup = 0, setup = 0,
first = 1, first = 1,
replace = 2, replace = 2,
add = 3 add = 3,
} }
enum MixDirection { enum MixDirection {
in = 0, in = 0,
out = 1 out = 1,
} }
enum TimelineType { enum TimelineType {
rotate = 0, rotate = 0,
@ -37,7 +37,7 @@ declare module spine {
pathConstraintPosition = 11, pathConstraintPosition = 11,
pathConstraintSpacing = 12, pathConstraintSpacing = 12,
pathConstraintMix = 13, pathConstraintMix = 13,
twoColor = 14 twoColor = 14,
} }
abstract class CurveTimeline implements Timeline { abstract class CurveTimeline implements Timeline {
static LINEAR: number; static LINEAR: number;
@ -341,7 +341,7 @@ declare module spine {
end = 2, end = 2,
dispose = 3, dispose = 3,
complete = 4, complete = 4,
event = 5 event = 5,
} }
interface AnimationStateListener2 { interface AnimationStateListener2 {
start(entry: TrackEntry): void; start(entry: TrackEntry): void;
@ -380,8 +380,8 @@ declare module spine {
private toLoad; private toLoad;
private loaded; private loaded;
constructor(textureLoader: (image: HTMLImageElement) => any, pathPrefix?: string); constructor(textureLoader: (image: HTMLImageElement) => any, pathPrefix?: string);
private static downloadText; private static downloadText(url, success, error);
private static downloadBinary; private static downloadBinary(url, success, error);
loadText(path: string, success?: (path: string, text: string) => void, error?: (path: string, error: string) => void): void; loadText(path: string, success?: (path: string, text: string) => void, error?: (path: string, error: string) => void): void;
loadTexture(path: string, success?: (path: string, image: HTMLImageElement) => void, error?: (path: string, error: string) => void): void; loadTexture(path: string, success?: (path: string, image: HTMLImageElement) => void, error?: (path: string, error: string) => void): void;
loadTextureData(path: string, data: string, success?: (path: string, image: HTMLImageElement) => void, error?: (path: string, error: string) => void): void; loadTextureData(path: string, data: string, success?: (path: string, image: HTMLImageElement) => void, error?: (path: string, error: string) => void): void;
@ -414,7 +414,7 @@ declare module spine {
Normal = 0, Normal = 0,
Additive = 1, Additive = 1,
Multiply = 2, Multiply = 2,
Screen = 3 Screen = 3,
} }
} }
declare module spine { declare module spine {
@ -483,7 +483,7 @@ declare module spine {
OnlyTranslation = 1, OnlyTranslation = 1,
NoRotationOrReflection = 2, NoRotationOrReflection = 2,
NoScale = 3, NoScale = 3,
NoScaleOrReflection = 4 NoScaleOrReflection = 4,
} }
} }
declare module spine { declare module spine {
@ -593,17 +593,17 @@ declare module spine {
} }
enum PositionMode { enum PositionMode {
Fixed = 0, Fixed = 0,
Percent = 1 Percent = 1,
} }
enum SpacingMode { enum SpacingMode {
Length = 0, Length = 0,
Fixed = 1, Fixed = 1,
Percent = 2 Percent = 2,
} }
enum RotateMode { enum RotateMode {
Tangent = 0, Tangent = 0,
Chain = 1, Chain = 1,
ChainScale = 2 ChainScale = 2,
} }
} }
declare module spine { declare module spine {
@ -614,12 +614,12 @@ declare module spine {
private rawAssets; private rawAssets;
private errors; private errors;
constructor(pathPrefix?: string); constructor(pathPrefix?: string);
private queueAsset; private queueAsset(clientId, textureLoader, path);
loadText(clientId: string, path: string): void; loadText(clientId: string, path: string): void;
loadJson(clientId: string, path: string): void; loadJson(clientId: string, path: string): void;
loadTexture(clientId: string, textureLoader: (image: HTMLImageElement) => any, path: string): void; loadTexture(clientId: string, textureLoader: (image: HTMLImageElement) => any, path: string): void;
get(clientId: string, path: string): any; get(clientId: string, path: string): any;
private updateClientAssets; private updateClientAssets(clientAssets);
isLoadingComplete(clientId: string): boolean; isLoadingComplete(clientId: string): boolean;
dispose(): void; dispose(): void;
hasErrors(): boolean; hasErrors(): boolean;
@ -670,7 +670,7 @@ declare module spine {
findIkConstraint(constraintName: string): IkConstraint; findIkConstraint(constraintName: string): IkConstraint;
findTransformConstraint(constraintName: string): TransformConstraint; findTransformConstraint(constraintName: string): TransformConstraint;
findPathConstraint(constraintName: string): PathConstraint; findPathConstraint(constraintName: string): PathConstraint;
getBounds(offset: Vector2, size: Vector2, temp: Array<number>): void; getBounds(offset: Vector2, size: Vector2, temp?: Array<number>): void;
update(delta: number): void; update(delta: number): void;
} }
} }
@ -823,12 +823,12 @@ declare module spine {
MipMapNearestNearest = 9984, MipMapNearestNearest = 9984,
MipMapLinearNearest = 9985, MipMapLinearNearest = 9985,
MipMapNearestLinear = 9986, MipMapNearestLinear = 9986,
MipMapLinearLinear = 9987 MipMapLinearLinear = 9987,
} }
enum TextureWrap { enum TextureWrap {
MirroredRepeat = 33648, MirroredRepeat = 33648,
ClampToEdge = 33071, ClampToEdge = 33071,
Repeat = 10497 Repeat = 10497,
} }
class TextureRegion { class TextureRegion {
renderObject: any; renderObject: any;
@ -855,7 +855,7 @@ declare module spine {
pages: TextureAtlasPage[]; pages: TextureAtlasPage[];
regions: TextureAtlasRegion[]; regions: TextureAtlasRegion[];
constructor(atlasText: string, textureLoader: (path: string) => any); constructor(atlasText: string, textureLoader: (path: string) => any);
private load; private load(atlasText, textureLoader);
findRegion(name: string): TextureAtlasRegion; findRegion(name: string): TextureAtlasRegion;
dispose(): void; dispose(): void;
} }
@ -931,9 +931,9 @@ declare module spine {
private polygonIndicesPool; private polygonIndicesPool;
triangulate(verticesArray: ArrayLike<number>): Array<number>; triangulate(verticesArray: ArrayLike<number>): Array<number>;
decompose(verticesArray: Array<number>, triangles: Array<number>): Array<Array<number>>; decompose(verticesArray: Array<number>, triangles: Array<number>): Array<Array<number>>;
private static isConcave; private static isConcave(index, vertexCount, vertices, indices);
private static positiveArea; private static positiveArea(p1x, p1y, p2x, p2y, p3x, p3y);
private static winding; private static winding(p1x, p1y, p2x, p2y, p3x, p3y);
} }
} }
declare module spine { declare module spine {
@ -1105,7 +1105,7 @@ declare module spine {
Mesh = 2, Mesh = 2,
LinkedMesh = 3, LinkedMesh = 3,
Path = 4, Path = 4,
Point = 5 Point = 5,
} }
} }
declare module spine { declare module spine {
@ -1299,7 +1299,7 @@ declare module spine.webgl {
touchesPool: Pool<Touch>; touchesPool: Pool<Touch>;
private listeners; private listeners;
constructor(element: HTMLElement); constructor(element: HTMLElement);
private setupCallbacks; private setupCallbacks(element);
addListener(listener: InputListener): void; addListener(listener: InputListener): void;
removeListener(listener: InputListener): void; removeListener(listener: InputListener): void;
} }
@ -1408,7 +1408,7 @@ declare module spine.webgl {
drawWithOffset(shader: Shader, primitiveType: number, offset: number, count: number): void; drawWithOffset(shader: Shader, primitiveType: number, offset: number, count: number): void;
bind(shader: Shader): void; bind(shader: Shader): void;
unbind(shader: Shader): void; unbind(shader: Shader): void;
private update; private update();
restore(): void; restore(): void;
dispose(): void; dispose(): void;
} }
@ -1434,7 +1434,7 @@ declare module spine.webgl {
constructor(); constructor();
} }
enum VertexAttributeType { enum VertexAttributeType {
Float = 0 Float = 0,
} }
} }
declare module spine.webgl { declare module spine.webgl {
@ -1453,7 +1453,7 @@ declare module spine.webgl {
begin(shader: Shader): void; begin(shader: Shader): void;
setBlendMode(srcBlend: number, dstBlend: number): void; setBlendMode(srcBlend: number, dstBlend: number): void;
draw(texture: GLTexture, vertices: ArrayLike<number>, indices: Array<number>): void; draw(texture: GLTexture, vertices: ArrayLike<number>, indices: Array<number>): void;
private flush; private flush();
end(): void; end(): void;
getDrawCalls(): number; getDrawCalls(): number;
dispose(): void; dispose(): void;
@ -1493,13 +1493,13 @@ declare module spine.webgl {
curve(x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, segments: number, color?: Color): void; curve(x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, segments: number, color?: Color): void;
end(): void; end(): void;
resize(resizeMode: ResizeMode): void; resize(resizeMode: ResizeMode): void;
private enableRenderer; private enableRenderer(renderer);
dispose(): void; dispose(): void;
} }
enum ResizeMode { enum ResizeMode {
Stretch = 0, Stretch = 0,
Expand = 1, Expand = 1,
Fit = 2 Fit = 2,
} }
} }
declare module spine.webgl { declare module spine.webgl {
@ -1527,9 +1527,9 @@ declare module spine.webgl {
getVertexShaderSource(): string; getVertexShaderSource(): string;
getFragmentSource(): string; getFragmentSource(): string;
constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, vertexShader: string, fragmentShader: string); constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, vertexShader: string, fragmentShader: string);
private compile; private compile();
private compileShader; private compileShader(type, source);
private compileProgram; private compileProgram(vs, fs);
restore(): void; restore(): void;
bind(): void; bind(): void;
unbind(): void; unbind(): void;
@ -1576,16 +1576,16 @@ declare module spine.webgl {
polygon(polygonVertices: ArrayLike<number>, offset: number, count: number, color?: Color): void; polygon(polygonVertices: ArrayLike<number>, offset: number, count: number, color?: Color): void;
circle(filled: boolean, x: number, y: number, radius: number, color?: Color, segments?: number): void; circle(filled: boolean, x: number, y: number, radius: number, color?: Color, segments?: number): void;
curve(x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, segments: number, color?: Color): void; curve(x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, segments: number, color?: Color): void;
private vertex; private vertex(x, y, color);
end(): void; end(): void;
private flush; private flush();
private check; private check(shapeType, numVertices);
dispose(): void; dispose(): void;
} }
enum ShapeType { enum ShapeType {
Point = 0, Point = 0,
Line = 1, Line = 1,
Filled = 4 Filled = 4,
} }
} }
declare module spine.webgl { declare module spine.webgl {
@ -1688,9 +1688,22 @@ declare module spine {
atlasUrl: string; atlasUrl: string;
animation: string; animation: string;
skin: string; skin: string;
scale: number; debug: {
x: number; bones: boolean;
y: number; regions: boolean;
bounds: boolean;
paths: boolean;
points: boolean;
clipping: boolean;
meshHull: boolean;
triangles: boolean;
};
viewport: {
x: number;
y: number;
width: number;
height: number;
};
alpha: boolean; alpha: boolean;
backgroundColor: string; backgroundColor: string;
premultipliedAlpha: boolean; premultipliedAlpha: boolean;
@ -1705,18 +1718,23 @@ declare module spine {
private loadingScreen; private loadingScreen;
private assetManager; private assetManager;
private timelineSlider; private timelineSlider;
private playButton;
private loaded; private loaded;
private skeleton; private skeleton;
private animationState; private animationState;
private time; private time;
private paused; private paused;
private currentAnimation; private playTime;
private speed;
constructor(parent: HTMLElement, config: SpinePlayerConfig); constructor(parent: HTMLElement, config: SpinePlayerConfig);
validateConfig(config: SpinePlayerConfig): SpinePlayerConfig; validateConfig(config: SpinePlayerConfig): SpinePlayerConfig;
render(parent: HTMLElement, config: SpinePlayerConfig): void; render(parent: HTMLElement, config: SpinePlayerConfig): void;
drawFrame(): void; drawFrame(requestNextFrame?: boolean): void;
scale(sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number): Vector2;
loadSkeleton(): void; loadSkeleton(): void;
private resize; private play();
private pause();
private resize();
} }
} }
declare module spine { declare module spine {
@ -1740,10 +1758,10 @@ declare module spine {
private loaded; private loaded;
private bounds; private bounds;
constructor(element: HTMLElement | string, config: SpineWidgetConfig); constructor(element: HTMLElement | string, config: SpineWidgetConfig);
private validateConfig; private validateConfig(config);
private load; private load();
private render; private render();
private resize; private resize();
pause(): void; pause(): void;
play(): void; play(): void;
isPlaying(): boolean; isPlaying(): boolean;
@ -1751,7 +1769,7 @@ declare module spine {
static loadWidgets(): void; static loadWidgets(): void;
static loadWidget(widget: HTMLElement): void; static loadWidget(widget: HTMLElement): void;
static pageLoaded: boolean; static pageLoaded: boolean;
private static ready; private static ready();
static setupDOMListener(): void; static setupDOMListener(): void;
} }
class SpineWidgetConfig { class SpineWidgetConfig {

View File

@ -1,10 +1,7 @@
var __extends = (this && this.__extends) || (function () { var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) { var extendStatics = Object.setPrototypeOf ||
extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) { return function (d, b) {
extendStatics(d, b); extendStatics(d, b);
function __() { this.constructor = d; } function __() { this.constructor = d; }
@ -3829,6 +3826,7 @@ var spine;
return null; return null;
}; };
Skeleton.prototype.getBounds = function (offset, size, temp) { Skeleton.prototype.getBounds = function (offset, size, temp) {
if (temp === void 0) { temp = new Array(2); }
if (offset == null) if (offset == null)
throw new Error("offset cannot be null."); throw new Error("offset cannot be null.");
if (size == null) if (size == null)
@ -6963,7 +6961,7 @@ var spine;
} }
Input.prototype.setupCallbacks = function (element) { Input.prototype.setupCallbacks = function (element) {
var _this = this; var _this = this;
element.addEventListener("mousedown", function (ev) { var mouseDown = function (ev) {
if (ev instanceof MouseEvent) { if (ev instanceof MouseEvent) {
var rect = element.getBoundingClientRect(); var rect = element.getBoundingClientRect();
var x = ev.clientX - rect.left; var x = ev.clientX - rect.left;
@ -6975,9 +6973,11 @@ var spine;
_this.lastX = x; _this.lastX = x;
_this.lastY = y; _this.lastY = y;
_this.buttonDown = true; _this.buttonDown = true;
document.addEventListener("mousemove", mouseMove);
document.addEventListener("mouseup", mouseUp);
} }
}, true); };
element.addEventListener("mousemove", function (ev) { var mouseMove = function (ev) {
if (ev instanceof MouseEvent) { if (ev instanceof MouseEvent) {
var rect = element.getBoundingClientRect(); var rect = element.getBoundingClientRect();
var x = ev.clientX - rect.left; var x = ev.clientX - rect.left;
@ -6994,8 +6994,8 @@ var spine;
_this.lastX = x; _this.lastX = x;
_this.lastY = y; _this.lastY = y;
} }
}, true); };
element.addEventListener("mouseup", function (ev) { var mouseUp = function (ev) {
if (ev instanceof MouseEvent) { if (ev instanceof MouseEvent) {
var rect = element.getBoundingClientRect(); var rect = element.getBoundingClientRect();
var x = ev.clientX - rect.left; var x = ev.clientX - rect.left;
@ -7007,8 +7007,13 @@ var spine;
_this.lastX = x; _this.lastX = x;
_this.lastY = y; _this.lastY = y;
_this.buttonDown = false; _this.buttonDown = false;
document.removeEventListener("mousemove", mouseMove);
document.removeEventListener("mouseup", mouseUp);
} }
}, true); };
element.addEventListener("mousedown", mouseDown, true);
element.addEventListener("mousemove", mouseMove, true);
element.addEventListener("mouseup", mouseUp, true);
element.addEventListener("touchstart", function (ev) { element.addEventListener("touchstart", function (ev) {
if (_this.currTouch != null) if (_this.currTouch != null)
return; return;
@ -9412,6 +9417,7 @@ var spine;
up: function (x, y) { up: function (x, y) {
dragging = false; dragging = false;
var percentage = x / _this.slider.clientWidth; var percentage = x / _this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1));
_this.setValue(x / _this.slider.clientWidth); _this.setValue(x / _this.slider.clientWidth);
if (_this.change) if (_this.change)
_this.change(percentage); _this.change(percentage);
@ -9419,6 +9425,7 @@ var spine;
moved: function (x, y) { moved: function (x, y) {
if (dragging) { if (dragging) {
var percentage = x / _this.slider.clientWidth; var percentage = x / _this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1));
_this.setValue(x / _this.slider.clientWidth); _this.setValue(x / _this.slider.clientWidth);
if (_this.change) if (_this.change)
_this.change(percentage); _this.change(percentage);
@ -9426,6 +9433,7 @@ var spine;
}, },
dragged: function (x, y) { dragged: function (x, y) {
var percentage = x / _this.slider.clientWidth; var percentage = x / _this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1));
_this.setValue(x / _this.slider.clientWidth); _this.setValue(x / _this.slider.clientWidth);
if (_this.change) if (_this.change)
_this.change(percentage); _this.change(percentage);
@ -9443,6 +9451,8 @@ var spine;
this.config = config; this.config = config;
this.time = new spine.TimeKeeper(); this.time = new spine.TimeKeeper();
this.paused = true; this.paused = true;
this.playTime = 0;
this.speed = 1;
this.validateConfig(config); this.validateConfig(config);
this.render(parent, config); this.render(parent, config);
} }
@ -9453,12 +9463,6 @@ var spine;
throw new Error("Please specify the URL of the skeleton JSON file."); throw new Error("Please specify the URL of the skeleton JSON file.");
if (!config.atlasUrl) if (!config.atlasUrl)
throw new Error("Please specify the URL of the atlas file."); throw new Error("Please specify the URL of the atlas file.");
if (!config.scale)
config.scale = 1;
if (!config.x)
config.x = 0;
if (!config.y)
config.y = 0;
if (!config.alpha) if (!config.alpha)
config.alpha = false; config.alpha = false;
if (!config.backgroundColor) if (!config.backgroundColor)
@ -9469,11 +9473,38 @@ var spine;
config.success = function (widget) { }; config.success = function (widget) { };
if (!config.error) if (!config.error)
config.error = function (widget, msg) { }; config.error = function (widget, msg) { };
if (!config.debug)
config.debug = {
bones: false,
bounds: false,
clipping: false,
meshHull: false,
paths: false,
points: false,
regions: false,
triangles: false
};
if (!config.debug.bones)
config.debug.bones = false;
if (!config.debug.bounds)
config.debug.bounds = false;
if (!config.debug.clipping)
config.debug.clipping = false;
if (!config.debug.meshHull)
config.debug.meshHull = false;
if (!config.debug.paths)
config.debug.paths = false;
if (!config.debug.points)
config.debug.points = false;
if (!config.debug.regions)
config.debug.regions = false;
if (!config.debug.triangles)
config.debug.triangles = false;
return config; return config;
}; };
SpinePlayer.prototype.render = function (parent, config) { SpinePlayer.prototype.render = function (parent, config) {
var _this = this; var _this = this;
parent.innerHTML = "\n\t\t\t\t<div class=\"spine-player\">\n\t\t\t\t\t<canvas class=\"spine-player-canvas\"></canvas>\n\t\t\t\t\t<div class=\"spine-player-controls\">\n\t\t\t\t\t\t<div class=\"spine-player-timeline\">\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"spine-player-buttons\">\n\t\t\t\t\t\t\t<button id=\"spine-player-button-play-pause\" class=\"spine-player-button spine-player-button-icon-play\"></button>\n\t\t\t\t\t\t\t<div class=\"spine-player-button-spacer\"></div>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-speed\" class=\"spine-player-button\">\n\t\t\t\t\t\t\t\tSpeed\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-animation\" class=\"spine-player-button\">\n\t\t\t\t\t\t\t\tAnimation\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-skin\" class=\"spine-player-button\">\n\t\t\t\t\t\t\t\tSkin\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-settings\" class=\"spine-player-button\">\n\t\t\t\t\t\t\t\tSettings\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-fullscreen\" class=\"spine-player-button\">\n\t\t\t\t\t\t\t\tFullscreen\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t"; parent.innerHTML = "\n\t\t\t\t<div class=\"spine-player\">\n\t\t\t\t\t<canvas class=\"spine-player-canvas\"></canvas>\n\t\t\t\t\t<div class=\"spine-player-controls spine-player-dropdown\">\n\t\t\t\t\t\t<div class=\"spine-player-timeline\">\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class=\"spine-player-buttons\">\n\t\t\t\t\t\t\t<button id=\"spine-player-button-play-pause\" class=\"spine-player-button spine-player-button-icon-pause\"></button>\n\t\t\t\t\t\t\t<div class=\"spine-player-button-spacer\"></div>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-speed\" class=\"spine-player-button spine-player-button-icon-speed\"></button>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-animation\" class=\"spine-player-button spine-player-button-icon-animations\"></button>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-skin\" class=\"spine-player-button spine-player-button-icon-skins\"></button>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-settings\" class=\"spine-player-button spine-player-button-icon-settings\"></button>\n\t\t\t\t\t\t\t<button id=\"spine-player-button-fullscreen\" class=\"spine-player-button spine-player-button-icon-fullscreen\"></button>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"spine-player-dropdown-content spine-player-hidden\">\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t";
this.canvas = findWithClass(parent, "spine-player-canvas")[0]; this.canvas = findWithClass(parent, "spine-player-canvas")[0];
var webglConfig = { alpha: config.alpha }; var webglConfig = { alpha: config.alpha };
this.context = new spine.webgl.ManagedWebGLRenderingContext(this.canvas, webglConfig); this.context = new spine.webgl.ManagedWebGLRenderingContext(this.canvas, webglConfig);
@ -9485,18 +9516,131 @@ var spine;
requestAnimationFrame(function () { return _this.drawFrame(); }); requestAnimationFrame(function () { return _this.drawFrame(); });
var timeline = findWithClass(parent, "spine-player-timeline")[0]; var timeline = findWithClass(parent, "spine-player-timeline")[0];
this.timelineSlider = new Slider(timeline); this.timelineSlider = new Slider(timeline);
var playButton = findWithId(parent, "spine-player-button-play-pause")[0]; this.playButton = findWithId(parent, "spine-player-button-play-pause")[0];
var speedButton = findWithId(parent, "spine-player-button-speed")[0];
var animationButton = findWithId(parent, "spine-player-button-animation")[0]; var animationButton = findWithId(parent, "spine-player-button-animation")[0];
var skinButton = findWithId(parent, "spine-player-button-skin")[0]; var skinButton = findWithId(parent, "spine-player-button-skin")[0];
var settingsButton = findWithId(parent, "spine-player-button-settings")[0]; var settingsButton = findWithId(parent, "spine-player-button-settings")[0];
var fullscreenButton = findWithId(parent, "spine-player-button-fullscreen")[0]; var fullscreenButton = findWithId(parent, "spine-player-button-fullscreen")[0];
var dropdown = findWithClass(parent, "spine-player-dropdown-content")[0];
var justClicked = false;
var dismissDropdown = function (event) {
if (justClicked) {
justClicked = false;
return;
}
if (!isContained(dropdown, event.target)) {
dropdown.classList.add("spine-player-hidden");
window.onclick = null;
}
};
this.playButton.onclick = function () {
if (_this.paused)
_this.play();
else
_this.pause();
};
speedButton.onclick = function () {
dropdown.classList.remove("spine-player-hidden");
dropdown.innerHTML = "\n\t\t\t\t\t<div class=\"spine-player-row\" style=\"user-select: none; align-items: center;\">\n\t\t\t\t\t\t<div style=\"margin-right: 16px;\">Speed</div>\n\t\t\t\t\t\t<div class=\"spine-player-column\">\n\t\t\t\t\t\t\t<div class=\"spine-player-speed-slider\" style=\"margin-bottom: 4px;\"></div>\n\t\t\t\t\t\t\t<div class=\"spine-player-row\" style=\"justify-content: space-between;\">\n\t\t\t\t\t\t\t\t<div>0.1x</div>\n\t\t\t\t\t\t\t\t<div>1x</div>\n\t\t\t\t\t\t\t\t<div>2x</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t";
var sliderParent = findWithClass(dropdown, "spine-player-speed-slider")[0];
var slider = new Slider(sliderParent);
slider.setValue(_this.speed / 2);
slider.change = function (percentage) {
_this.speed = percentage * 2;
};
justClicked = true;
window.onclick = dismissDropdown;
};
animationButton.onclick = function () {
if (!_this.skeleton || _this.skeleton.data.animations.length == 0)
return;
dropdown.classList.remove("spine-player-hidden");
dropdown.innerHTML = "\n\t\t\t\t\t<div>Animations</div>\n\t\t\t\t\t<hr>\n\t\t\t\t\t<div class=\"spine-player-list\" style=\"user-select: none; align-items: center; max-height: 90px; overflow: auto;\">\n\t\t\t\t\t</div>\n\t\t\t\t";
var rows = findWithClass(dropdown, "spine-player-list")[0];
_this.skeleton.data.animations.forEach(function (animation) {
var row = document.createElement("div");
row.classList.add("spine-player-list-item");
if (animation.name == _this.config.animation)
row.classList.add("spine-player-list-item-selected");
row.innerText = animation.name;
rows.appendChild(row);
row.onclick = function () {
removeClass(rows.children, "spine-player-list-item-selected");
row.classList.add("spine-player-list-item-selected");
_this.config.animation = animation.name;
_this.playTime = 0;
_this.animationState.setAnimation(0, _this.config.animation, true);
};
});
justClicked = true;
window.onclick = dismissDropdown;
};
skinButton.onclick = function () {
if (!_this.skeleton || _this.skeleton.data.animations.length == 0)
return;
dropdown.classList.remove("spine-player-hidden");
dropdown.innerHTML = "\n\t\t\t\t\t<div>Skins</div>\n\t\t\t\t\t<hr>\n\t\t\t\t\t<div class=\"spine-player-list\" style=\"user-select: none; align-items: center; max-height: 90px; overflow: auto;\">\n\t\t\t\t\t</div>\n\t\t\t\t";
var rows = findWithClass(dropdown, "spine-player-list")[0];
_this.skeleton.data.skins.forEach(function (skin) {
var row = document.createElement("div");
row.classList.add("spine-player-list-item");
if (skin.name == _this.config.skin)
row.classList.add("spine-player-list-item-selected");
row.innerText = skin.name;
rows.appendChild(row);
row.onclick = function () {
removeClass(rows.children, "spine-player-list-item-selected");
row.classList.add("spine-player-list-item-selected");
_this.config.skin = skin.name;
_this.skeleton.setSkinByName(_this.config.skin);
_this.skeleton.setSlotsToSetupPose();
};
});
justClicked = true;
window.onclick = dismissDropdown;
};
settingsButton.onclick = function () {
if (!_this.skeleton || _this.skeleton.data.animations.length == 0)
return;
dropdown.classList.remove("spine-player-hidden");
dropdown.innerHTML = "\n\t\t\t\t\t<div>Debug</div>\n\t\t\t\t\t<hr>\n\t\t\t\t\t<div class=\"spine-player-list\" style=\"user-select: none; align-items: center; max-height: 90px; overflow: auto;\">\n\t\t\t\t\t</div>\n\t\t\t\t";
var rows = findWithClass(dropdown, "spine-player-list")[0];
var makeItem = function (name) {
var row = document.createElement("div");
row.classList.add("spine-player-list-item");
if (_this.config.debug[name] == true)
row.classList.add("spine-player-list-item-selected");
row.innerText = name;
rows.appendChild(row);
row.onclick = function () {
if (_this.config.debug[name]) {
_this.config.debug[name] = false;
row.classList.remove("spine-player-list-item-selected");
}
else {
_this.config.debug[name] = true;
row.classList.add("spine-player-list-item-selected");
}
};
};
Object.keys(_this.config.debug).forEach(function (name) {
makeItem(name);
});
justClicked = true;
window.onclick = dismissDropdown;
};
window.onresize = function () {
_this.drawFrame(false);
};
}; };
SpinePlayer.prototype.drawFrame = function () { SpinePlayer.prototype.drawFrame = function (requestNextFrame) {
var _this = this; var _this = this;
requestAnimationFrame(function () { return _this.drawFrame(); }); if (requestNextFrame === void 0) { requestNextFrame = true; }
if (requestNextFrame)
requestAnimationFrame(function () { return _this.drawFrame(); });
var ctx = this.context; var ctx = this.context;
var gl = ctx.gl; var gl = ctx.gl;
this.time.update();
var bg = new spine.Color().setFromString(this.config.backgroundColor); var bg = new spine.Color().setFromString(this.config.backgroundColor);
gl.clearColor(bg.r, bg.g, bg.b, bg.a); gl.clearColor(bg.r, bg.g, bg.b, bg.a);
gl.clear(gl.COLOR_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT);
@ -9505,27 +9649,47 @@ var spine;
this.loadSkeleton(); this.loadSkeleton();
this.sceneRenderer.resize(spine.webgl.ResizeMode.Expand); this.sceneRenderer.resize(spine.webgl.ResizeMode.Expand);
if (this.loaded) { if (this.loaded) {
this.skeleton.x = this.config.x; if (!this.paused && this.config.animation) {
this.skeleton.y = this.config.y; this.time.update();
this.skeleton.scaleX = this.skeleton.scaleY = this.config.scale; var delta = this.time.delta * this.speed;
if (!this.paused && this.currentAnimation) { var animationDuration = this.animationState.getCurrent(0).animation.duration;
this.animationState.update(this.time.delta); this.playTime += delta;
while (this.playTime >= animationDuration) {
this.playTime -= animationDuration;
}
this.playTime = Math.max(0, Math.min(this.playTime, animationDuration));
this.timelineSlider.setValue(this.playTime / animationDuration);
this.animationState.update(delta);
this.animationState.apply(this.skeleton); this.animationState.apply(this.skeleton);
this.skeleton.updateWorldTransform(); this.skeleton.updateWorldTransform();
var animation = this.skeleton.data.findAnimation(this.currentAnimation);
var animationTime = this.animationState.getCurrent(0).trackTime % animation.duration;
var percentage = animationTime / animation.duration;
this.timelineSlider.setValue(percentage);
} }
this.sceneRenderer.camera.position.x = 0; var viewportSize = this.scale(this.config.viewport.width, this.config.viewport.height, this.canvas.width, this.canvas.height);
this.sceneRenderer.camera.position.y = 0; this.sceneRenderer.camera.zoom = this.config.viewport.width / viewportSize.x;
this.sceneRenderer.camera.position.x = this.config.viewport.x + this.config.viewport.width / 2;
this.sceneRenderer.camera.position.y = this.config.viewport.y + this.config.viewport.height / 2;
this.sceneRenderer.begin(); this.sceneRenderer.begin();
this.sceneRenderer.line(0, 0, 200, 0, spine.Color.RED);
this.sceneRenderer.line(0, 0, 0, 200, spine.Color.GREEN);
this.sceneRenderer.drawSkeleton(this.skeleton, this.config.premultipliedAlpha); this.sceneRenderer.drawSkeleton(this.skeleton, this.config.premultipliedAlpha);
this.sceneRenderer.skeletonDebugRenderer.drawBones = this.config.debug.bones;
this.sceneRenderer.skeletonDebugRenderer.drawBoundingBoxes = this.config.debug.bounds;
this.sceneRenderer.skeletonDebugRenderer.drawClipping = this.config.debug.clipping;
this.sceneRenderer.skeletonDebugRenderer.drawMeshHull = this.config.debug.meshHull;
this.sceneRenderer.skeletonDebugRenderer.drawPaths = this.config.debug.paths;
this.sceneRenderer.skeletonDebugRenderer.drawRegionAttachments = this.config.debug.regions;
this.sceneRenderer.skeletonDebugRenderer.drawMeshTriangles = this.config.debug.triangles;
this.sceneRenderer.drawSkeletonDebug(this.skeleton, this.config.premultipliedAlpha);
this.sceneRenderer.end(); this.sceneRenderer.end();
this.sceneRenderer.camera.zoom = 0;
} }
}; };
SpinePlayer.prototype.scale = function (sourceWidth, sourceHeight, targetWidth, targetHeight) {
var targetRatio = targetHeight / targetWidth;
var sourceRatio = sourceHeight / sourceWidth;
var scale = targetRatio > sourceRatio ? targetWidth / sourceWidth : targetHeight / sourceHeight;
var temp = new spine.Vector2();
temp.x = sourceWidth * scale;
temp.y = sourceHeight * scale;
return temp;
};
SpinePlayer.prototype.loadSkeleton = function () { SpinePlayer.prototype.loadSkeleton = function () {
var _this = this; var _this = this;
if (this.loaded) if (this.loaded)
@ -9535,24 +9699,68 @@ var spine;
var json = new spine.SkeletonJson(new spine.AtlasAttachmentLoader(atlas)); var json = new spine.SkeletonJson(new spine.AtlasAttachmentLoader(atlas));
var skeletonData = json.readSkeletonData(jsonText); var skeletonData = json.readSkeletonData(jsonText);
this.skeleton = new spine.Skeleton(skeletonData); this.skeleton = new spine.Skeleton(skeletonData);
this.animationState = new spine.AnimationState(new spine.AnimationStateData(skeletonData)); var stateData = new spine.AnimationStateData(skeletonData);
if (this.config.animation) { stateData.defaultMix = 0.2;
this.currentAnimation = this.config.animation; this.animationState = new spine.AnimationState(stateData);
} if (!this.config.skin) {
else { if (skeletonData.skins.length > 0) {
if (skeletonData.animations.length > 0) { this.config.skin = skeletonData.skins[0].name;
this.currentAnimation = skeletonData.animations[0].name;
} }
} }
if (this.currentAnimation) { if (this.config.skin) {
this.animationState.setAnimation(0, this.currentAnimation, true); this.skeleton.setSkinByName(this.config.skin);
this.paused = false; this.skeleton.setSlotsToSetupPose();
}
if (!this.config.viewport || !this.config.viewport.x || !this.config.viewport.y || !this.config.viewport.width || !this.config.viewport.height) {
this.config.viewport = {
x: 0,
y: 0,
width: 0,
height: 0
};
this.skeleton.updateWorldTransform();
var offset = new spine.Vector2();
var size = new spine.Vector2();
this.skeleton.getBounds(offset, size);
this.config.viewport.x = offset.x + size.x / 2 - size.x / 2 * 1.2;
this.config.viewport.y = offset.y + size.y / 2 - size.y / 2 * 1.2;
this.config.viewport.width = size.x * 1.2;
this.config.viewport.height = size.y * 1.2;
}
if (!this.config.animation) {
if (skeletonData.animations.length > 0) {
this.config.animation = skeletonData.animations[0].name;
}
}
if (this.config.animation) {
this.play();
this.timelineSlider.change = function (percentage) { this.timelineSlider.change = function (percentage) {
_this.paused = true; _this.pause();
var animationDuration = _this.animationState.getCurrent(0).animation.duration;
var time = animationDuration * percentage;
_this.animationState.update(time - _this.playTime);
_this.animationState.apply(_this.skeleton);
_this.skeleton.updateWorldTransform();
_this.playTime = time;
}; };
} }
this.loaded = true; this.loaded = true;
}; };
SpinePlayer.prototype.play = function () {
this.paused = false;
this.playButton.classList.remove("spine-player-button-icon-play");
this.playButton.classList.add("spine-player-button-icon-pause");
if (this.config.animation) {
if (!this.animationState.getCurrent(0)) {
this.animationState.setAnimation(0, this.config.animation, true);
}
}
};
SpinePlayer.prototype.pause = function () {
this.paused = true;
this.playButton.classList.remove("spine-player-button-icon-pause");
this.playButton.classList.add("spine-player-button-icon-play");
};
SpinePlayer.prototype.resize = function () { SpinePlayer.prototype.resize = function () {
var canvas = this.canvas; var canvas = this.canvas;
var w = canvas.clientWidth; var w = canvas.clientWidth;
@ -9568,6 +9776,21 @@ var spine;
return SpinePlayer; return SpinePlayer;
}()); }());
spine.SpinePlayer = SpinePlayer; spine.SpinePlayer = SpinePlayer;
function isContained(dom, needle) {
if (dom === needle)
return true;
var findRecursive = function (dom, needle) {
for (var i = 0; i < dom.children.length; i++) {
var child = dom.children[i];
if (child === needle)
return true;
if (findRecursive(child, needle))
return true;
}
return false;
};
return findRecursive(dom, needle);
}
function findWithId(dom, id) { function findWithId(dom, id) {
var found = new Array(); var found = new Array();
var findRecursive = function (dom, id, found) { var findRecursive = function (dom, id, found) {
@ -9594,6 +9817,11 @@ var spine;
findRecursive(dom, className, found); findRecursive(dom, className, found);
return found; return found;
} }
function removeClass(elements, clazz) {
for (var i = 0; i < elements.length; i++) {
elements[i].classList.remove(clazz);
}
}
})(spine || (spine = {})); })(spine || (spine = {}));
var spine; var spine;
(function (spine) { (function (spine) {

File diff suppressed because one or more lines are too long

View File

@ -470,7 +470,7 @@ module spine {
* @param offset The distance from the skeleton origin to the bottom left corner of the AABB. * @param offset The distance from the skeleton origin to the bottom left corner of the AABB.
* @param size The width and height of the AABB. * @param size The width and height of the AABB.
* @param temp Working memory */ * @param temp Working memory */
getBounds (offset: Vector2, size: Vector2, temp: Array<number>) { getBounds (offset: Vector2, size: Vector2, temp: Array<number> = new Array<number>(2)) {
if (offset == null) throw new Error("offset cannot be null."); if (offset == null) throw new Error("offset cannot be null.");
if (size == null) throw new Error("size cannot be null."); if (size == null) throw new Error("size cannot be null.");
let drawOrder = this.drawOrder; let drawOrder = this.drawOrder;

View File

@ -46,7 +46,7 @@ module spine.webgl {
} }
private setupCallbacks(element: HTMLElement) { private setupCallbacks(element: HTMLElement) {
element.addEventListener("mousedown", (ev: UIEvent) => { let mouseDown = (ev: UIEvent) => {
if (ev instanceof MouseEvent) { if (ev instanceof MouseEvent) {
let rect = element.getBoundingClientRect(); let rect = element.getBoundingClientRect();
let x = ev.clientX - rect.left; let x = ev.clientX - rect.left;
@ -60,9 +60,13 @@ module spine.webgl {
this.lastX = x; this.lastX = x;
this.lastY = y; this.lastY = y;
this.buttonDown = true; this.buttonDown = true;
document.addEventListener("mousemove", mouseMove);
document.addEventListener("mouseup", mouseUp);
} }
}, true); }
element.addEventListener("mousemove", (ev: UIEvent) => {
let mouseMove = (ev: UIEvent) => {
if (ev instanceof MouseEvent) { if (ev instanceof MouseEvent) {
let rect = element.getBoundingClientRect(); let rect = element.getBoundingClientRect();
let x = ev.clientX - rect.left; let x = ev.clientX - rect.left;
@ -80,8 +84,9 @@ module spine.webgl {
this.lastX = x; this.lastX = x;
this.lastY = y; this.lastY = y;
} }
}, true); };
element.addEventListener("mouseup", (ev: UIEvent) => {
let mouseUp = (ev: UIEvent) => {
if (ev instanceof MouseEvent) { if (ev instanceof MouseEvent) {
let rect = element.getBoundingClientRect(); let rect = element.getBoundingClientRect();
let x = ev.clientX - rect.left; let x = ev.clientX - rect.left;
@ -95,8 +100,16 @@ module spine.webgl {
this.lastX = x; this.lastX = x;
this.lastY = y; this.lastY = y;
this.buttonDown = false; this.buttonDown = false;
document.removeEventListener("mousemove", mouseMove);
document.removeEventListener("mouseup", mouseUp);
} }
}, true); }
element.addEventListener("mousedown", mouseDown, true);
element.addEventListener("mousemove", mouseMove, true);
element.addEventListener("mouseup", mouseUp, true);
element.addEventListener("touchstart", (ev: TouchEvent) => { element.addEventListener("touchstart", (ev: TouchEvent) => {
if (this.currTouch != null) return; if (this.currTouch != null) return;

View File

@ -8,15 +8,42 @@ body {
} }
.spine-player { .spine-player {
box-sizing: border-box;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: red; background: none;
position: relative; position: relative;
} }
.spine-player * {
box-sizing: border-box;
font-family: "PT Sans",Arial,"Helvetica Neue",Helvetica,Tahoma,sans-serif;
color: #ffffff;
}
.spine-player .spine-player-hidden {
display: none;
}
.spine-player .spine-player-dropdown {
position: relative;
display: inline-block;
}
.spine-player .spine-player-dropdown-content {
position: absolute;
background: rgba(0, 0, 0, 0.6);
z-index: 1;
right: 2px;
bottom: 42px;
padding: 16px;
border-radius: 8px;
}
.spine-player canvas { .spine-player canvas {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 8px;
} }
.spine-player .spine-player-controls { .spine-player .spine-player-controls {
@ -52,15 +79,21 @@ body {
flex-direction: row; flex-direction: row;
width: 100%; width: 100%;
background: rgba(0, 0, 0, 0.6); background: rgba(0, 0, 0, 0.6);
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
padding: 0 8px;
} }
.spine-player .spine-player-button { .spine-player .spine-player-button {
background: none; background: none;
outline: 0; outline: 0;
border: none; border: none;
width: 32px;
height: 32px; height: 32px;
background-size: 20px;
background-repeat: no-repeat;
background-position: 6;
cursor: pointer; cursor: pointer;
color: white;
} }
.spine-player .spine-player-button-spacer { .spine-player .spine-player-button-spacer {
@ -68,14 +101,86 @@ body {
} }
.spine-player .spine-player-button-icon-play { .spine-player .spine-player-button-icon-play {
width: 32px; background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23fff%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eplay%3C%2Ftitle%3E%3Cg%20id%3D%22play%22%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2243%2023.3%204%2047%204%201%2043%2023.3%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cdefs%3E%3Cstyle%3E.icon-canvas-transparent,.icon-vs-out{fill:%23252526}.icon-canvas-transparent{opacity:0}.icon-vs-action-green{fill:%2389d185}%3C/style%3E%3C/defs%3E%3Ctitle%3Econtinue%3C/title%3E%3Cpath class='icon-canvas-transparent' d='M16 0v16H0V0z' id='canvas'/%3E%3Cpath class='icon-vs-action-green' d='M4 1.5v13L12.667 8 4 1.5z' id='iconBg'/%3E%3C/svg%3E"); }
fill: white;
.spine-player .spine-player-button-icon-play:hover {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%2362B0EE%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eplay%3C%2Ftitle%3E%3Cg%20id%3D%22play%22%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2243%2023.3%204%2047%204%201%2043%2023.3%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
} }
.spine-player .spine-player-button-icon-pause { .spine-player .spine-player-button-icon-pause {
width: 32px; background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23fff%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Epause%3C%2Ftitle%3E%3Cg%20id%3D%22pause%22%3E%3Crect%20class%3D%22cls-1%22%20x%3D%226%22%20y%3D%221%22%20width%3D%2213%22%20height%3D%2246%22%2F%3E%3Crect%20class%3D%22cls-1%22%20x%3D%2228%22%20y%3D%221%22%20width%3D%2213%22%20height%3D%2246%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cdefs%3E%3Cstyle%3E.icon-canvas-transparent,.icon-vs-out{fill:%23252526}.icon-canvas-transparent{opacity:0}.icon-vs-action-blue{fill:%2375beff}%3C/style%3E%3C/defs%3E%3Ctitle%3Epause%3C/title%3E%3Cpath class='icon-canvas-transparent' d='M16 0v16H0V0z' id='canvas'/%3E%3Cpath class='icon-vs-action-blue' d='M4 3h2.5v10H4zm5.5 0v10H12V3z' id='iconBg'/%3E%3C/svg%3E"); }
.spine-player .spine-player-button-icon-pause:hover {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%2362B0EE%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Epause%3C%2Ftitle%3E%3Cg%20id%3D%22pause%22%3E%3Crect%20class%3D%22cls-1%22%20x%3D%226%22%20y%3D%221%22%20width%3D%2213%22%20height%3D%2246%22%2F%3E%3Crect%20class%3D%22cls-1%22%20x%3D%2228%22%20y%3D%221%22%20width%3D%2213%22%20height%3D%2246%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}
.spine-player .spine-player-button-icon-speed {
background-image: url("data:image/svg+xml,%3Csvg%20id%3D%22playback%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23fff%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eplayback%3C%2Ftitle%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M48%2C28V20l-4.7-1.18a20.16%2C20.16%2C0%2C0%2C0-2-4.81l2.49-4.15L38.14%2C4.2%2C34%2C6.69a20.16%2C20.16%2C0%2C0%2C0-4.81-2L28%2C0H20L18.82%2C4.7A20.16%2C20.16%2C0%2C0%2C0%2C14%2C6.7L9.86%2C4.2%2C4.2%2C9.86%2C6.69%2C14a20.16%2C20.16%2C0%2C0%2C0-2%2C4.81L0%2C20v8l4.7%2C1.18A20.16%2C20.16%2C0%2C0%2C0%2C6.7%2C34L4.2%2C38.14%2C9.86%2C43.8%2C14%2C41.31a20.16%2C20.16%2C0%2C0%2C0%2C4.81%2C2L20%2C48h8l1.18-4.7a20.16%2C20.16%2C0%2C0%2C0%2C4.81-2l4.15%2C2.49%2C5.66-5.66L41.31%2C34a20.16%2C20.16%2C0%2C0%2C0%2C2-4.81ZM24%2C38A14%2C14%2C0%2C1%2C1%2C38%2C24%2C14%2C14%2C0%2C0%2C1%2C24%2C38Z%22%2F%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2234%2024%2018%2033%2018%2015%2034%2024%2034%2024%22%2F%3E%3C%2Fsvg%3E");
}
.spine-player .spine-player-button-icon-speed:hover {
background-image: url("data:image/svg+xml,%3Csvg%20id%3D%22playback%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%2362B0EE%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eplayback%3C%2Ftitle%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M48%2C28V20l-4.7-1.18a20.16%2C20.16%2C0%2C0%2C0-2-4.81l2.49-4.15L38.14%2C4.2%2C34%2C6.69a20.16%2C20.16%2C0%2C0%2C0-4.81-2L28%2C0H20L18.82%2C4.7A20.16%2C20.16%2C0%2C0%2C0%2C14%2C6.7L9.86%2C4.2%2C4.2%2C9.86%2C6.69%2C14a20.16%2C20.16%2C0%2C0%2C0-2%2C4.81L0%2C20v8l4.7%2C1.18A20.16%2C20.16%2C0%2C0%2C0%2C6.7%2C34L4.2%2C38.14%2C9.86%2C43.8%2C14%2C41.31a20.16%2C20.16%2C0%2C0%2C0%2C4.81%2C2L20%2C48h8l1.18-4.7a20.16%2C20.16%2C0%2C0%2C0%2C4.81-2l4.15%2C2.49%2C5.66-5.66L41.31%2C34a20.16%2C20.16%2C0%2C0%2C0%2C2-4.81ZM24%2C38A14%2C14%2C0%2C1%2C1%2C38%2C24%2C14%2C14%2C0%2C0%2C1%2C24%2C38Z%22%2F%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2234%2024%2018%2033%2018%2015%2034%2024%2034%2024%22%2F%3E%3C%2Fsvg%3E");
}
.spine-player .spine-player-button-icon-animations {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23fff%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eanimations%3C%2Ftitle%3E%3Cg%20id%3D%22animations%22%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M12%2C45V43.22a6.39%2C6.39%2C0%2C0%2C0%2C.63-.81%2C27.83%2C27.83%2C0%2C0%2C1%2C3.79-4.16c.93-.84%2C2.06-1.88%2C2.86-2.71a13.83%2C13.83%2C0%2C0%2C0%2C1.53-1.9l3.9-5.24c1-1.17.95-1.1%2C2.11%2C0l3%2C2.24a4%2C4%2C0%2C0%2C0-2.29%2C2.38c-1.37%2C3-2.39%2C4-2.68%2C4.22l-.23.18c-.54.39-1.81%2C1-1.7%2C1.54l.8%2C1.49a4.5%2C4.5%2C0%2C0%2C1%2C.39%2C1l.57%2C2.15a.69.69%2C0%2C0%2C0%2C.58.48c.47.08%2C1%2C.5%2C1.33.53%2C1.29.1%2C1.79%2C0%2C1.42-.54L26.7%2C42.72a.86.86%2C0%2C0%2C1-.2-.24%2C3.64%2C3.64%2C0%2C0%2C1-.42-2.2A5.39%2C5.39%2C0%2C0%2C1%2C26.61%2C39c1.84-2%2C6.74-6.36%2C6.74-6.36%2C1.71-1.81%2C1.4-2.52.81-3.84a27.38%2C27.38%2C0%2C0%2C0-2-3c-.41-.61-2.08-2.38-2.85-3.28-.43-.5.38-2.08.87-2.82.18-.12-.41.05%2C1.72.07a23.32%2C23.32%2C0%2C0%2C0%2C3.56-.19l1.63.61c.28%2C0%2C1.18-.09%2C1.31-.35l.12-.78c.18-.39.31-1.56-.05-1.75l-.6-.52a2.28%2C2.28%2C0%2C0%2C0-1.61.07l-.2.44c-.14.15-.52.37-.71.29l-2.24%2C0c-.5.12-1.18-.42-1.81-.73L32.05%2C15a8%2C8%2C0%2C0%2C0%2C.8-3.92%2C1.22%2C1.22%2C0%2C0%2C0-.28-.82%2C7.87%2C7.87%2C0%2C0%2C0-1.15-1.06l.11-.73c-.12-.49%2C1-.82%2C1.52-.82l.76-.33c.32%2C0%2C.68-.89.78-1.21L34.94%2C4a11.26%2C11.26%2C0%2C0%2C0%2C0-1.61C34.57.08%2C30.06-1.42%2C28.78%2C2c-.14.38-.62.77.34%2C3.21a1.55%2C1.55%2C0%2C0%2C1-.3%2C1.2L28.4%2C7a4%2C4%2C0%2C0%2C1-1.19.49c-.79%2C0-1.59-.75-4%2C.54C21%2C9.16%2C18.59%2C13%2C17.7%2C14.22a3.21%2C3.21%2C0%2C0%2C0-.61%2C1.58c-.05%2C1.16.7%2C3.74.87%2C5.75.13%2C1.53.21%2C2.52.72%2C3.06%2C1.07%2C1.14%2C2.1-.18%2C2.61-1a2.74%2C2.74%2C0%2C0%2C0-.14-1.86l-.74-.1c-.15-.15-.4-.42-.39-.64-.05-3.48-.22-3.14-.18-5.39%2C1.74-1.46%2C2.4-2.45%2C2.3-2-.2%2C1.15.28%2C2.83.09%2C4.35a6.46%2C6.46%2C0%2C0%2C1-.7%2C2.58s-2.11%2C4.22-2.14%2C4.27l-1.26%2C5.6-.7%2C1.44s-.71.54-1.59%2C1.21a9.67%2C9.67%2C0%2C0%2C0-2.27%2C3.18%2C20.16%2C20.16%2C0%2C0%2C1-1.42%2C2.83l-.87%2C1.31a1.72%2C1.72%2C0%2C0%2C1-.6.61l-1.83%2C1.1a1.39%2C1.39%2C0%2C0%2C0-.16.93l.68%2C1.71a4.07%2C4.07%2C0%2C0%2C1%2C.27%2C1.07l.17%2C1.56a.75.75%2C0%2C0%2C0%2C.71.59%2C18.13%2C18.13%2C0%2C0%2C0%2C3.26-.5c.27-.09-.29-.78-.53-1s-.45-.36-.45-.36A12.78%2C12.78%2C0%2C0%2C1%2C12%2C45Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
}
.spine-player .spine-player-button-icon-animations:hover {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%2362B0EE%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eanimations%3C%2Ftitle%3E%3Cg%20id%3D%22animations%22%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M12%2C45V43.22a6.39%2C6.39%2C0%2C0%2C0%2C.63-.81%2C27.83%2C27.83%2C0%2C0%2C1%2C3.79-4.16c.93-.84%2C2.06-1.88%2C2.86-2.71a13.83%2C13.83%2C0%2C0%2C0%2C1.53-1.9l3.9-5.24c1-1.17.95-1.1%2C2.11%2C0l3%2C2.24a4%2C4%2C0%2C0%2C0-2.29%2C2.38c-1.37%2C3-2.39%2C4-2.68%2C4.22l-.23.18c-.54.39-1.81%2C1-1.7%2C1.54l.8%2C1.49a4.5%2C4.5%2C0%2C0%2C1%2C.39%2C1l.57%2C2.15a.69.69%2C0%2C0%2C0%2C.58.48c.47.08%2C1%2C.5%2C1.33.53%2C1.29.1%2C1.79%2C0%2C1.42-.54L26.7%2C42.72a.86.86%2C0%2C0%2C1-.2-.24%2C3.64%2C3.64%2C0%2C0%2C1-.42-2.2A5.39%2C5.39%2C0%2C0%2C1%2C26.61%2C39c1.84-2%2C6.74-6.36%2C6.74-6.36%2C1.71-1.81%2C1.4-2.52.81-3.84a27.38%2C27.38%2C0%2C0%2C0-2-3c-.41-.61-2.08-2.38-2.85-3.28-.43-.5.38-2.08.87-2.82.18-.12-.41.05%2C1.72.07a23.32%2C23.32%2C0%2C0%2C0%2C3.56-.19l1.63.61c.28%2C0%2C1.18-.09%2C1.31-.35l.12-.78c.18-.39.31-1.56-.05-1.75l-.6-.52a2.28%2C2.28%2C0%2C0%2C0-1.61.07l-.2.44c-.14.15-.52.37-.71.29l-2.24%2C0c-.5.12-1.18-.42-1.81-.73L32.05%2C15a8%2C8%2C0%2C0%2C0%2C.8-3.92%2C1.22%2C1.22%2C0%2C0%2C0-.28-.82%2C7.87%2C7.87%2C0%2C0%2C0-1.15-1.06l.11-.73c-.12-.49%2C1-.82%2C1.52-.82l.76-.33c.32%2C0%2C.68-.89.78-1.21L34.94%2C4a11.26%2C11.26%2C0%2C0%2C0%2C0-1.61C34.57.08%2C30.06-1.42%2C28.78%2C2c-.14.38-.62.77.34%2C3.21a1.55%2C1.55%2C0%2C0%2C1-.3%2C1.2L28.4%2C7a4%2C4%2C0%2C0%2C1-1.19.49c-.79%2C0-1.59-.75-4%2C.54C21%2C9.16%2C18.59%2C13%2C17.7%2C14.22a3.21%2C3.21%2C0%2C0%2C0-.61%2C1.58c-.05%2C1.16.7%2C3.74.87%2C5.75.13%2C1.53.21%2C2.52.72%2C3.06%2C1.07%2C1.14%2C2.1-.18%2C2.61-1a2.74%2C2.74%2C0%2C0%2C0-.14-1.86l-.74-.1c-.15-.15-.4-.42-.39-.64-.05-3.48-.22-3.14-.18-5.39%2C1.74-1.46%2C2.4-2.45%2C2.3-2-.2%2C1.15.28%2C2.83.09%2C4.35a6.46%2C6.46%2C0%2C0%2C1-.7%2C2.58s-2.11%2C4.22-2.14%2C4.27l-1.26%2C5.6-.7%2C1.44s-.71.54-1.59%2C1.21a9.67%2C9.67%2C0%2C0%2C0-2.27%2C3.18%2C20.16%2C20.16%2C0%2C0%2C1-1.42%2C2.83l-.87%2C1.31a1.72%2C1.72%2C0%2C0%2C1-.6.61l-1.83%2C1.1a1.39%2C1.39%2C0%2C0%2C0-.16.93l.68%2C1.71a4.07%2C4.07%2C0%2C0%2C1%2C.27%2C1.07l.17%2C1.56a.75.75%2C0%2C0%2C0%2C.71.59%2C18.13%2C18.13%2C0%2C0%2C0%2C3.26-.5c.27-.09-.29-.78-.53-1s-.45-.36-.45-.36A12.78%2C12.78%2C0%2C0%2C1%2C12%2C45Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
}
.spine-player .spine-player-button-icon-skins {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23fff%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eskins%3C%2Ftitle%3E%3Cg%20id%3D%22skins%22%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M36%2C12.54l-6.92%2C1-.79%2C1.2c-1%2C.25-2-.62-3-.55V12.33a1.35%2C1.35%2C0%2C0%2C1%2C.55-1.07c3-2.24%2C3.28-3.75%2C3.28-5.34A5.06%2C5.06%2C0%2C0%2C0%2C24%2C.76c-2.54%2C0-4.38.71-5.49%2C2.13a5.74%2C5.74%2C0%2C0%2C0-.9%2C4.57l2.48-.61a3.17%2C3.17%2C0%2C0%2C1%2C.45-2.4c.6-.75%2C1.75-1.13%2C3.42-1.13%2C2.56%2C0%2C2.56%2C1.24%2C2.56%2C2.56%2C0%2C.92%2C0%2C1.65-2.26%2C3.34a3.92%2C3.92%2C0%2C0%2C0-1.58%2C3.12v1.86c-1-.07-2%2C.8-3%2C.55l-.79-1.2-6.92-1c-2.25%2C0-4.35%2C2.09-5.64%2C3.93L1%2C24c3.83%2C5.11%2C10.22%2C5.11%2C10.22%2C5.11V41.93c0%2C2.34%2C2.68%2C3.88%2C5.59%2C4.86a22.59%2C22.59%2C0%2C0%2C0%2C14.37%2C0c2.91-1%2C5.59-2.52%2C5.59-4.86V29.15S43.17%2C29.15%2C47%2C24l-5.33-7.57C40.38%2C14.63%2C38.27%2C12.54%2C36%2C12.54ZM23.32%2C20.09%2C21%2C17l1.8-.6a3.79%2C3.79%2C0%2C0%2C1%2C2.4%2C0L27%2C17l-2.32%2C3.09A.85.85%2C0%2C0%2C1%2C23.32%2C20.09Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}
.spine-player .spine-player-button-icon-skins:hover {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%2362B0EE%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eskins%3C%2Ftitle%3E%3Cg%20id%3D%22skins%22%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M36%2C12.54l-6.92%2C1-.79%2C1.2c-1%2C.25-2-.62-3-.55V12.33a1.35%2C1.35%2C0%2C0%2C1%2C.55-1.07c3-2.24%2C3.28-3.75%2C3.28-5.34A5.06%2C5.06%2C0%2C0%2C0%2C24%2C.76c-2.54%2C0-4.38.71-5.49%2C2.13a5.74%2C5.74%2C0%2C0%2C0-.9%2C4.57l2.48-.61a3.17%2C3.17%2C0%2C0%2C1%2C.45-2.4c.6-.75%2C1.75-1.13%2C3.42-1.13%2C2.56%2C0%2C2.56%2C1.24%2C2.56%2C2.56%2C0%2C.92%2C0%2C1.65-2.26%2C3.34a3.92%2C3.92%2C0%2C0%2C0-1.58%2C3.12v1.86c-1-.07-2%2C.8-3%2C.55l-.79-1.2-6.92-1c-2.25%2C0-4.35%2C2.09-5.64%2C3.93L1%2C24c3.83%2C5.11%2C10.22%2C5.11%2C10.22%2C5.11V41.93c0%2C2.34%2C2.68%2C3.88%2C5.59%2C4.86a22.59%2C22.59%2C0%2C0%2C0%2C14.37%2C0c2.91-1%2C5.59-2.52%2C5.59-4.86V29.15S43.17%2C29.15%2C47%2C24l-5.33-7.57C40.38%2C14.63%2C38.27%2C12.54%2C36%2C12.54ZM23.32%2C20.09%2C21%2C17l1.8-.6a3.79%2C3.79%2C0%2C0%2C1%2C2.4%2C0L27%2C17l-2.32%2C3.09A.85.85%2C0%2C0%2C1%2C23.32%2C20.09Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}
.spine-player .spine-player-button-icon-settings {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23fff%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Esettings%3C%2Ftitle%3E%3Cg%20id%3D%22settings%22%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M40%2C3H8A5%2C5%2C0%2C0%2C0%2C3%2C8V40a5%2C5%2C0%2C0%2C0%2C5%2C5H40a5%2C5%2C0%2C0%2C0%2C5-5V8A5%2C5%2C0%2C0%2C0%2C40%2C3ZM16%2C40H9V33h7Zm0-12H9V21h7Zm0-12H9V9h7ZM39%2C38H20V35H39Zm0-12H20V23H39Zm0-12H20V11H39Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}
.spine-player .spine-player-button-icon-settings:hover {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%2362B0EE%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Esettings%3C%2Ftitle%3E%3Cg%20id%3D%22settings%22%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M40%2C3H8A5%2C5%2C0%2C0%2C0%2C3%2C8V40a5%2C5%2C0%2C0%2C0%2C5%2C5H40a5%2C5%2C0%2C0%2C0%2C5-5V8A5%2C5%2C0%2C0%2C0%2C40%2C3ZM16%2C40H9V33h7Zm0-12H9V21h7Zm0-12H9V9h7ZM39%2C38H20V35H39Zm0-12H20V23H39Zm0-12H20V11H39Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}
.spine-player .spine-player-button-icon-fullscreen {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23fff%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eexpand%3C%2Ftitle%3E%3Cg%20id%3D%22settings%22%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2230.14%208%2040%208%2040%2017.86%2044.5%2017.86%2044.5%203.5%2030.14%203.5%2030.14%208%22%2F%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%228%2017.86%208%208%2017.86%208%2017.86%203.5%203.5%203.5%203.5%2017.86%208%2017.86%22%2F%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2240%2030.14%2040%2040%2030.14%2040%2030.14%2044.5%2044.5%2044.5%2044.5%2030.14%2040%2030.14%22%2F%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2217.86%2040%208%2040%208%2030.14%203.5%2030.14%203.5%2044.5%2017.86%2044.5%2017.86%2040%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}
.spine-player .spine-player-button-icon-fullscreen:hover {
background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2048%2048%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%2362B0EE%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eexpand%3C%2Ftitle%3E%3Cg%20id%3D%22settings%22%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2230.14%208%2040%208%2040%2017.86%2044.5%2017.86%2044.5%203.5%2030.14%203.5%2030.14%208%22%2F%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%228%2017.86%208%208%2017.86%208%2017.86%203.5%203.5%203.5%203.5%2017.86%208%2017.86%22%2F%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2240%2030.14%2040%2040%2030.14%2040%2030.14%2044.5%2044.5%2044.5%2044.5%2030.14%2040%2030.14%22%2F%3E%3Cpolygon%20class%3D%22cls-1%22%20points%3D%2217.86%2040%208%2040%208%2030.14%203.5%2030.14%203.5%2044.5%2017.86%2044.5%2017.86%2040%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}
.spine-player-column {
display: flex;
flex-direction: column;
}
.spine-player-row {
display: flex;
flex-direction: row;
}
.spine-player-list-item {
padding: 0 4px;
cursor: pointer;
}
.spine-player-list-item-selected {
background: rgba(101, 176, 238, 0.605);
}
.spine-player-list-item:hover {
background: #62B0EE;
}
.spine-player-speed-slider {
width: 150px;
} }
</style> </style>
@ -85,12 +190,22 @@ body {
</body> </body>
<script> <script>
new spine.SpinePlayer(document.getElementById("container"), { new spine.SpinePlayer(document.getElementById("container"), {
jsonUrl: "assets/spineboy-ess.json", jsonUrl: "assets/raptor-pro.json",
atlasUrl: "assets/spineboy.atlas", atlasUrl: "assets/raptor.atlas",
backgroundColor: "#cccccc", backgroundColor: "#cccccc",
scale: 0.5, scale: 1,
x: 0, x: 0,
y: 0 y: 0,
debug: {
bones: true,
regions: true,
bounds: true,
paths: true,
points: true,
clipping: true,
meshHull: true,
triangles: true
}
}); });
</script> </script>
</body> </body>

View File

@ -34,9 +34,22 @@
atlasUrl: string; atlasUrl: string;
animation: string; animation: string;
skin: string; skin: string;
scale: number; debug: {
x: number; bones: boolean;
y: number; regions: boolean;
bounds: boolean;
paths: boolean;
points: boolean;
clipping: boolean;
meshHull: boolean;
triangles: boolean;
},
viewport: {
x: number,
y: number,
width: number,
height: number
}
alpha: boolean; alpha: boolean;
backgroundColor: string; backgroundColor: string;
premultipliedAlpha: boolean; premultipliedAlpha: boolean;
@ -68,18 +81,21 @@
up: (x, y) => { up: (x, y) => {
dragging = false; dragging = false;
let percentage = x / this.slider.clientWidth; let percentage = x / this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1));
this.setValue(x / this.slider.clientWidth); this.setValue(x / this.slider.clientWidth);
if (this.change) this.change(percentage); if (this.change) this.change(percentage);
}, },
moved: (x, y) => { moved: (x, y) => {
if (dragging) { if (dragging) {
let percentage = x / this.slider.clientWidth; let percentage = x / this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1));
this.setValue(x / this.slider.clientWidth); this.setValue(x / this.slider.clientWidth);
if (this.change) this.change(percentage); if (this.change) this.change(percentage);
} }
}, },
dragged: (x, y) => { dragged: (x, y) => {
let percentage = x / this.slider.clientWidth; let percentage = x / this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1));
this.setValue(x / this.slider.clientWidth); this.setValue(x / this.slider.clientWidth);
if (this.change) this.change(percentage); if (this.change) this.change(percentage);
} }
@ -99,13 +115,15 @@
private loadingScreen: spine.webgl.LoadingScreen; private loadingScreen: spine.webgl.LoadingScreen;
private assetManager: spine.webgl.AssetManager; private assetManager: spine.webgl.AssetManager;
private timelineSlider: Slider; private timelineSlider: Slider;
private playButton: HTMLElement;
private loaded: boolean; private loaded: boolean;
private skeleton: Skeleton; private skeleton: Skeleton;
private animationState: AnimationState; private animationState: AnimationState;
private time = new TimeKeeper(); private time = new TimeKeeper();
private paused = true; private paused = true;
private currentAnimation: string; private playTime = 0;
private speed = 1;
constructor(parent: HTMLElement, private config: SpinePlayerConfig) { constructor(parent: HTMLElement, private config: SpinePlayerConfig) {
this.validateConfig(config); this.validateConfig(config);
@ -116,14 +134,29 @@
if (!config) throw new Error("Please pass a configuration to new.spine.SpinePlayer()."); if (!config) throw new Error("Please pass a configuration to new.spine.SpinePlayer().");
if (!config.jsonUrl) throw new Error("Please specify the URL of the skeleton JSON file."); if (!config.jsonUrl) throw new Error("Please specify the URL of the skeleton JSON file.");
if (!config.atlasUrl) throw new Error("Please specify the URL of the atlas file."); if (!config.atlasUrl) throw new Error("Please specify the URL of the atlas file.");
if (!config.scale) config.scale = 1;
if (!config.x) config.x = 0;
if (!config.y) config.y = 0;
if (!config.alpha) config.alpha = false; if (!config.alpha) config.alpha = false;
if (!config.backgroundColor) config.backgroundColor = "#000000"; if (!config.backgroundColor) config.backgroundColor = "#000000";
if (!config.premultipliedAlpha) config.premultipliedAlpha = false; if (!config.premultipliedAlpha) config.premultipliedAlpha = false;
if (!config.success) config.success = (widget) => {}; if (!config.success) config.success = (widget) => {};
if (!config.error) config.error = (widget, msg) => {}; if (!config.error) config.error = (widget, msg) => {};
if (!config.debug) config.debug = {
bones: false,
bounds: false,
clipping: false,
meshHull: false,
paths: false,
points: false,
regions: false,
triangles: false
}
if (!config.debug.bones) config.debug.bones = false;
if (!config.debug.bounds) config.debug.bounds = false;
if (!config.debug.clipping) config.debug.clipping = false;
if (!config.debug.meshHull) config.debug.meshHull = false;
if (!config.debug.paths) config.debug.paths = false;
if (!config.debug.points) config.debug.points = false;
if (!config.debug.regions) config.debug.regions = false;
if (!config.debug.triangles) config.debug.triangles = false;
return config; return config;
} }
@ -131,27 +164,20 @@
parent.innerHTML = /*html*/` parent.innerHTML = /*html*/`
<div class="spine-player"> <div class="spine-player">
<canvas class="spine-player-canvas"></canvas> <canvas class="spine-player-canvas"></canvas>
<div class="spine-player-controls"> <div class="spine-player-controls spine-player-dropdown">
<div class="spine-player-timeline"> <div class="spine-player-timeline">
</div> </div>
<div class="spine-player-buttons"> <div class="spine-player-buttons">
<button id="spine-player-button-play-pause" class="spine-player-button spine-player-button-icon-play"></button> <button id="spine-player-button-play-pause" class="spine-player-button spine-player-button-icon-pause"></button>
<div class="spine-player-button-spacer"></div> <div class="spine-player-button-spacer"></div>
<button id="spine-player-button-speed" class="spine-player-button"> <button id="spine-player-button-speed" class="spine-player-button spine-player-button-icon-speed"></button>
Speed <button id="spine-player-button-animation" class="spine-player-button spine-player-button-icon-animations"></button>
</button> <button id="spine-player-button-skin" class="spine-player-button spine-player-button-icon-skins"></button>
<button id="spine-player-button-animation" class="spine-player-button"> <button id="spine-player-button-settings" class="spine-player-button spine-player-button-icon-settings"></button>
Animation <button id="spine-player-button-fullscreen" class="spine-player-button spine-player-button-icon-fullscreen"></button>
</button> </div>
<button id="spine-player-button-skin" class="spine-player-button">
Skin <div class="spine-player-dropdown-content spine-player-hidden">
</button>
<button id="spine-player-button-settings" class="spine-player-button">
Settings
</button>
<button id="spine-player-button-fullscreen" class="spine-player-button">
Fullscreen
</button>
</div> </div>
</div> </div>
</div> </div>
@ -177,18 +203,163 @@
// Setup the event listeners for UI elements // Setup the event listeners for UI elements
let timeline = findWithClass(parent, "spine-player-timeline")[0]; let timeline = findWithClass(parent, "spine-player-timeline")[0];
this.timelineSlider = new Slider(timeline); this.timelineSlider = new Slider(timeline);
let playButton = findWithId(parent, "spine-player-button-play-pause")[0]; this.playButton = findWithId(parent, "spine-player-button-play-pause")[0];
let speedButton = findWithId(parent, "spine-player-button-speed")[0];
let animationButton = findWithId(parent, "spine-player-button-animation")[0]; let animationButton = findWithId(parent, "spine-player-button-animation")[0];
let skinButton = findWithId(parent, "spine-player-button-skin")[0]; let skinButton = findWithId(parent, "spine-player-button-skin")[0];
let settingsButton = findWithId(parent, "spine-player-button-settings")[0]; let settingsButton = findWithId(parent, "spine-player-button-settings")[0];
let fullscreenButton = findWithId(parent, "spine-player-button-fullscreen")[0]; let fullscreenButton = findWithId(parent, "spine-player-button-fullscreen")[0];
let dropdown = findWithClass(parent, "spine-player-dropdown-content")[0];
var justClicked = false;
let dismissDropdown = function (event: any) {
if (justClicked) {
justClicked = false;
return;
}
if (!isContained(dropdown, event.target)) {
dropdown.classList.add("spine-player-hidden");
window.onclick = null;
}
}
this.playButton.onclick = () => {
if (this.paused) this.play()
else this.pause();
}
speedButton.onclick = () => {
dropdown.classList.remove("spine-player-hidden");
dropdown.innerHTML = /*html*/`
<div class="spine-player-row" style="user-select: none; align-items: center;">
<div style="margin-right: 16px;">Speed</div>
<div class="spine-player-column">
<div class="spine-player-speed-slider" style="margin-bottom: 4px;"></div>
<div class="spine-player-row" style="justify-content: space-between;">
<div>0.1x</div>
<div>1x</div>
<div>2x</div>
</div>
</div>
</div>
`;
let sliderParent = findWithClass(dropdown, "spine-player-speed-slider")[0];
let slider = new Slider(sliderParent);
slider.setValue(this.speed / 2);
slider.change = (percentage) => {
this.speed = percentage * 2;
}
justClicked = true;
window.onclick = dismissDropdown;
}
animationButton.onclick = () => {
if (!this.skeleton || this.skeleton.data.animations.length == 0) return;
dropdown.classList.remove("spine-player-hidden");
dropdown.innerHTML = /*html*/`
<div>Animations</div>
<hr>
<div class="spine-player-list" style="user-select: none; align-items: center; max-height: 90px; overflow: auto;">
</div>
`;
let rows = findWithClass(dropdown, "spine-player-list")[0];
this.skeleton.data.animations.forEach((animation) => {
let row = document.createElement("div");
row.classList.add("spine-player-list-item");
if (animation.name == this.config.animation) row.classList.add("spine-player-list-item-selected");
row.innerText = animation.name;
rows.appendChild(row);
row.onclick = () => {
removeClass(rows.children, "spine-player-list-item-selected");
row.classList.add("spine-player-list-item-selected");
this.config.animation = animation.name;
this.playTime = 0;
this.animationState.setAnimation(0, this.config.animation, true);
}
});
justClicked = true;
window.onclick = dismissDropdown;
}
skinButton.onclick = () => {
if (!this.skeleton || this.skeleton.data.animations.length == 0) return;
dropdown.classList.remove("spine-player-hidden");
dropdown.innerHTML = /*html*/`
<div>Skins</div>
<hr>
<div class="spine-player-list" style="user-select: none; align-items: center; max-height: 90px; overflow: auto;">
</div>
`;
let rows = findWithClass(dropdown, "spine-player-list")[0];
this.skeleton.data.skins.forEach((skin) => {
let row = document.createElement("div");
row.classList.add("spine-player-list-item");
if (skin.name == this.config.skin) row.classList.add("spine-player-list-item-selected");
row.innerText = skin.name;
rows.appendChild(row);
row.onclick = () => {
removeClass(rows.children, "spine-player-list-item-selected");
row.classList.add("spine-player-list-item-selected");
this.config.skin = skin.name;
this.skeleton.setSkinByName(this.config.skin);
this.skeleton.setSlotsToSetupPose();
}
});
justClicked = true;
window.onclick = dismissDropdown;
}
settingsButton.onclick = () => {
if (!this.skeleton || this.skeleton.data.animations.length == 0) return;
dropdown.classList.remove("spine-player-hidden");
dropdown.innerHTML = /*html*/`
<div>Debug</div>
<hr>
<div class="spine-player-list" style="user-select: none; align-items: center; max-height: 90px; overflow: auto;">
</div>
`;
let rows = findWithClass(dropdown, "spine-player-list")[0];
let makeItem = (name: string) => {
let row = document.createElement("div");
row.classList.add("spine-player-list-item");
if ((this.config.debug as any)[name] == true) row.classList.add("spine-player-list-item-selected");
row.innerText = name
rows.appendChild(row);
row.onclick = () => {
if ((this.config.debug as any)[name]) {
(this.config.debug as any)[name] = false;
row.classList.remove("spine-player-list-item-selected");
} else {
(this.config.debug as any)[name] = true;
row.classList.add("spine-player-list-item-selected");
}
}
};
Object.keys(this.config.debug).forEach((name) => {
makeItem(name);
});
justClicked = true;
window.onclick = dismissDropdown;
}
// Register a global resize handler to redraw and avoid flicker
window.onresize = () => {
this.drawFrame(false);
}
} }
drawFrame () { drawFrame (requestNextFrame = true) {
requestAnimationFrame(() => this.drawFrame()); if (requestNextFrame) requestAnimationFrame(() => this.drawFrame());
let ctx = this.context; let ctx = this.context;
let gl = ctx.gl; let gl = ctx.gl;
this.time.update();
// Clear the viewport // Clear the viewport
let bg = new Color().setFromString(this.config.backgroundColor); let bg = new Color().setFromString(this.config.backgroundColor);
@ -206,30 +377,55 @@
// Update and draw the skeleton // Update and draw the skeleton
if (this.loaded) { if (this.loaded) {
this.skeleton.x = this.config.x; if (!this.paused && this.config.animation) {
this.skeleton.y = this.config.y; this.time.update();
this.skeleton.scaleX = this.skeleton.scaleY = this.config.scale; let delta = this.time.delta * this.speed;
if (!this.paused && this.currentAnimation) { let animationDuration = this.animationState.getCurrent(0).animation.duration;
this.animationState.update(this.time.delta); this.playTime += delta;
while (this.playTime >= animationDuration) {
this.playTime -= animationDuration;
}
this.playTime = Math.max(0, Math.min(this.playTime, animationDuration));
this.timelineSlider.setValue(this.playTime / animationDuration);
this.animationState.update(delta);
this.animationState.apply(this.skeleton); this.animationState.apply(this.skeleton);
this.skeleton.updateWorldTransform(); this.skeleton.updateWorldTransform();
let animation = this.skeleton.data.findAnimation(this.currentAnimation);
let animationTime = this.animationState.getCurrent(0).trackTime % animation.duration;
let percentage = animationTime / animation.duration;
this.timelineSlider.setValue(percentage);
} }
this.sceneRenderer.camera.position.x = 0; let viewportSize = this.scale(this.config.viewport.width, this.config.viewport.height, this.canvas.width, this.canvas.height);
this.sceneRenderer.camera.position.y = 0;
this.sceneRenderer.camera.zoom = this.config.viewport.width / viewportSize.x;
this.sceneRenderer.camera.position.x = this.config.viewport.x + this.config.viewport.width / 2;
this.sceneRenderer.camera.position.y = this.config.viewport.y + this.config.viewport.height / 2;
this.sceneRenderer.begin(); this.sceneRenderer.begin();
this.sceneRenderer.line(0, 0, 200, 0, Color.RED);
this.sceneRenderer.line(0, 0, 0, 200, Color.GREEN);
this.sceneRenderer.drawSkeleton(this.skeleton, this.config.premultipliedAlpha); this.sceneRenderer.drawSkeleton(this.skeleton, this.config.premultipliedAlpha);
this.sceneRenderer.skeletonDebugRenderer.drawBones = this.config.debug.bones;
this.sceneRenderer.skeletonDebugRenderer.drawBoundingBoxes = this.config.debug.bounds;
this.sceneRenderer.skeletonDebugRenderer.drawClipping = this.config.debug.clipping;
this.sceneRenderer.skeletonDebugRenderer.drawMeshHull = this.config.debug.meshHull;
this.sceneRenderer.skeletonDebugRenderer.drawPaths = this.config.debug.paths;
this.sceneRenderer.skeletonDebugRenderer.drawRegionAttachments = this.config.debug.regions;
this.sceneRenderer.skeletonDebugRenderer.drawMeshTriangles = this.config.debug.triangles;
this.sceneRenderer.drawSkeletonDebug(this.skeleton, this.config.premultipliedAlpha);
this.sceneRenderer.end(); this.sceneRenderer.end();
this.sceneRenderer.camera.zoom = 0;
} }
} }
scale(sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number): Vector2 {
let targetRatio = targetHeight / targetWidth;
let sourceRatio = sourceHeight / sourceWidth;
let scale = targetRatio > sourceRatio ? targetWidth / sourceWidth : targetHeight / sourceHeight;
let temp = new spine.Vector2();
temp.x = sourceWidth * scale;
temp.y = sourceHeight * scale;
return temp;
}
loadSkeleton () { loadSkeleton () {
if (this.loaded) return; if (this.loaded) return;
@ -238,27 +434,80 @@
let json = new SkeletonJson(new AtlasAttachmentLoader(atlas)); let json = new SkeletonJson(new AtlasAttachmentLoader(atlas));
let skeletonData = json.readSkeletonData(jsonText); let skeletonData = json.readSkeletonData(jsonText);
this.skeleton = new Skeleton(skeletonData); this.skeleton = new Skeleton(skeletonData);
this.animationState = new AnimationState(new AnimationStateData(skeletonData)); let stateData = new AnimationStateData(skeletonData);
stateData.defaultMix = 0.2;
this.animationState = new AnimationState(stateData);
// Setup the first animation // Setup skin
if (this.config.animation) { if (!this.config.skin) {
this.currentAnimation = this.config.animation; if (skeletonData.skins.length > 0) {
} else { this.config.skin = skeletonData.skins[0].name;
if (skeletonData.animations.length > 0) {
this.currentAnimation = skeletonData.animations[0].name;
} }
} }
if(this.currentAnimation) { if (this.config.skin) {
this.animationState.setAnimation(0, this.currentAnimation, true); this.skeleton.setSkinByName(this.config.skin);
this.paused = false; this.skeleton.setSlotsToSetupPose();
}
// Setup viewport
if (!this.config.viewport || !this.config.viewport.x || !this.config.viewport.y || !this.config.viewport.width || !this.config.viewport.height) {
this.config.viewport = {
x: 0,
y: 0,
width: 0,
height: 0
}
this.skeleton.updateWorldTransform();
let offset = new spine.Vector2();
let size = new spine.Vector2();
this.skeleton.getBounds(offset, size);
this.config.viewport.x = offset.x + size.x / 2 - size.x / 2 * 1.2;
this.config.viewport.y = offset.y + size.y / 2 - size.y / 2 * 1.2;
this.config.viewport.width = size.x * 1.2;
this.config.viewport.height = size.y * 1.2;
}
// Setup the first animation
if (!this.config.animation) {
if (skeletonData.animations.length > 0) {
this.config.animation = skeletonData.animations[0].name;
}
}
if(this.config.animation) {
this.play()
this.timelineSlider.change = (percentage) => { this.timelineSlider.change = (percentage) => {
this.paused = true; this.pause();
var animationDuration = this.animationState.getCurrent(0).animation.duration;
var time = animationDuration * percentage;
this.animationState.update(time - this.playTime);
this.animationState.apply(this.skeleton);
this.skeleton.updateWorldTransform();
this.playTime = time;
} }
} }
this.loaded = true; this.loaded = true;
} }
private play () {
this.paused = false;
this.playButton.classList.remove("spine-player-button-icon-play");
this.playButton.classList.add("spine-player-button-icon-pause");
if (this.config.animation) {
if (!this.animationState.getCurrent(0)) {
this.animationState.setAnimation(0, this.config.animation, true);
}
}
}
private pause () {
this.paused = true;
this.playButton.classList.remove("spine-player-button-icon-pause");
this.playButton.classList.add("spine-player-button-icon-play");
}
private resize () { private resize () {
let canvas = this.canvas; let canvas = this.canvas;
let w = canvas.clientWidth; let w = canvas.clientWidth;
@ -274,6 +523,19 @@
} }
} }
function isContained(dom: HTMLElement, needle: HTMLElement): boolean {
if (dom === needle) return true;
let findRecursive = (dom: HTMLElement, needle: HTMLElement) => {
for(var i = 0; i < dom.children.length; i++) {
let child = dom.children[i] as HTMLElement;
if (child === needle) return true;
if (findRecursive(child, needle)) return true;
}
return false;
};
return findRecursive(dom, needle);
}
function findWithId(dom: HTMLElement, id: string): HTMLElement[] { function findWithId(dom: HTMLElement, id: string): HTMLElement[] {
let found = new Array<HTMLElement>() let found = new Array<HTMLElement>()
let findRecursive = (dom: HTMLElement, id: string, found: HTMLElement[]) => { let findRecursive = (dom: HTMLElement, id: string, found: HTMLElement[]) => {
@ -299,4 +561,10 @@
findRecursive(dom, className, found); findRecursive(dom, className, found);
return found; return found;
} }
function removeClass(elements: HTMLCollection, clazz: string) {
for (var i = 0; i < elements.length; i++) {
elements[i].classList.remove(clazz);
}
}
} }