Merge branch '3.7-beta' into 3.7-beta-cpp

This commit is contained in:
badlogic 2018-11-23 14:47:09 +01:00
commit 7bea40c81a
9 changed files with 620 additions and 128 deletions

View File

@ -1841,10 +1841,10 @@ declare module spine {
validateConfig(config: SpinePlayerConfig): SpinePlayerConfig;
showError(error: string): void;
render(): HTMLElement;
showSpeedDialog(): void;
showAnimationsDialog(): void;
showSkinsDialog(): void;
showSettingsDialog(): void;
showSpeedDialog(speedButton: HTMLElement): void;
showAnimationsDialog(animationsButton: HTMLElement): void;
showSkinsDialog(skinButton: HTMLElement): void;
showSettingsDialog(settingsButton: HTMLElement): void;
drawFrame(requestNextFrame?: boolean): void;
scale(sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number): Vector2;
loadSkeleton(): void;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1683,6 +1683,16 @@ declare module spine.webgl {
}
}
declare module spine {
interface Viewport {
x: number;
y: number;
width: number;
height: number;
padLeft: string | number;
padRight: string | number;
padTop: string | number;
padBottom: string | number;
}
interface SpinePlayerConfig {
jsonUrl: string;
atlasUrl: string;
@ -1708,6 +1718,13 @@ declare module spine {
y: number;
width: number;
height: number;
padLeft: string | number;
padRight: string | number;
padTop: string | number;
padBottom: string | number;
animations: Map<Viewport>;
debugRender: boolean;
transitionTime: number;
};
alpha: boolean;
backgroundColor: string;
@ -1746,21 +1763,28 @@ declare module spine {
private paused;
private playTime;
private speed;
private animationViewports;
private currentViewport;
private previousViewport;
private viewportTransitionStart;
private selectedBones;
constructor(parent: HTMLElement, config: SpinePlayerConfig);
validateConfig(config: SpinePlayerConfig): SpinePlayerConfig;
showError(error: string): void;
render(): HTMLElement;
showSpeedDialog(): void;
showAnimationsDialog(): void;
showSkinsDialog(): void;
showSettingsDialog(): void;
showSpeedDialog(speedButton: HTMLElement): void;
showAnimationsDialog(animationsButton: HTMLElement): void;
showSkinsDialog(skinButton: HTMLElement): void;
showSettingsDialog(settingsButton: HTMLElement): void;
drawFrame(requestNextFrame?: boolean): void;
scale(sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number): Vector2;
loadSkeleton(): void;
setupInput(): void;
private play();
private pause();
private setAnimation(animation);
private percentageToWorldUnit(size, percentageOrAbsolute);
private calculateAnimationViewport(animationName);
}
}
declare module spine {

View File

@ -9402,14 +9402,26 @@ var spine;
var spine;
(function (spine) {
var Popup = (function () {
function Popup(parent, htmlContent) {
function Popup(player, parent, htmlContent) {
this.player = player;
this.dom = createElement("\n\t\t\t\t<div class=\"spine-player-popup spine-player-hidden\">\n\t\t\t\t</div>\n\t\t\t");
this.dom.innerHTML = htmlContent;
parent.appendChild(this.dom);
}
Popup.prototype.show = function () {
Popup.prototype.show = function (dismissedListener) {
var _this = this;
if (dismissedListener === void 0) { dismissedListener = function () { }; }
this.dom.classList.remove("spine-player-hidden");
var dismissed = false;
var resize = function () {
if (!dismissed)
requestAnimationFrame(resize);
var bottomOffset = Math.abs(_this.dom.getBoundingClientRect().bottom - _this.player.getBoundingClientRect().bottom);
var rightOffset = Math.abs(_this.dom.getBoundingClientRect().right - _this.player.getBoundingClientRect().right);
var maxHeight = _this.player.clientHeight - bottomOffset - rightOffset;
_this.dom.style.maxHeight = maxHeight + "px";
};
requestAnimationFrame(resize);
var justClicked = true;
var windowClickListener = function (event) {
if (justClicked) {
@ -9419,6 +9431,8 @@ var spine;
if (!isContained(_this.dom, event.target)) {
_this.dom.parentNode.removeChild(_this.dom);
window.removeEventListener("click", windowClickListener);
dismissedListener();
dismissed = true;
}
};
window.addEventListener("click", windowClickListener);
@ -9525,6 +9539,10 @@ var spine;
this.paused = true;
this.playTime = 0;
this.speed = 1;
this.animationViewports = {};
this.currentViewport = null;
this.previousViewport = null;
this.viewportTransitionStart = 0;
parent.appendChild(this.render());
}
SpinePlayer.prototype.validateConfig = function (config) {
@ -9639,19 +9657,38 @@ var spine;
_this.pause();
};
speedButton.onclick = function () {
_this.showSpeedDialog();
_this.showSpeedDialog(speedButton);
};
this.animationButton.onclick = function () {
_this.showAnimationsDialog();
_this.showAnimationsDialog(_this.animationButton);
};
this.skinButton.onclick = function () {
_this.showSkinsDialog();
_this.showSkinsDialog(_this.skinButton);
};
settingsButton.onclick = function () {
_this.showSettingsDialog();
_this.showSettingsDialog(settingsButton);
};
var oldWidth = this.canvas.clientWidth;
var oldHeight = this.canvas.clientHeight;
var oldStyleWidth = this.canvas.style.width;
var oldStyleHeight = this.canvas.style.height;
var isFullscreen = false;
fullscreenButton.onclick = function () {
var fullscreenChanged = function () {
isFullscreen = !isFullscreen;
if (!isFullscreen) {
_this.canvas.style.width = "" + oldWidth + "px";
_this.canvas.style.height = "" + oldHeight + "px";
_this.drawFrame(false);
requestAnimationFrame(function () {
_this.canvas.style.width = oldStyleWidth;
_this.canvas.style.height = oldStyleHeight;
});
}
};
var doc = document;
dom.onfullscreenchange = fullscreenChanged;
dom.onwebkitfullscreenchange = fullscreenChanged;
if (doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement) {
if (doc.exitFullscreen)
doc.exitFullscreen();
@ -9663,6 +9700,10 @@ var spine;
doc.msExitFullscreen();
}
else {
oldWidth = _this.canvas.clientWidth;
oldHeight = _this.canvas.clientHeight;
oldStyleWidth = _this.canvas.style.width;
oldStyleHeight = _this.canvas.style.height;
var player = dom;
if (player.requestFullscreen)
player.requestFullscreen();
@ -9675,16 +9716,16 @@ var spine;
}
};
logoButton.onclick = function () {
window.location = "http://esotericsoftware.com";
window.open("http://esotericsoftware.com");
};
window.onresize = function () {
_this.drawFrame(false);
};
return dom;
};
SpinePlayer.prototype.showSpeedDialog = function () {
SpinePlayer.prototype.showSpeedDialog = function (speedButton) {
var _this = this;
var popup = new Popup(this.playerControls, "\n\t\t\t\t<div class=\"spine-player-row\" style=\"user-select: none; align-items: center; padding: 8px;\">\n\t\t\t\t\t<div style=\"margin-right: 16px;\">Speed</div>\n\t\t\t\t\t<div class=\"spine-player-column\">\n\t\t\t\t\t\t<div class=\"spine-player-speed-slider\" style=\"margin-bottom: 4px;\"></div>\n\t\t\t\t\t\t<div class=\"spine-player-row\" style=\"justify-content: space-between;\">\n\t\t\t\t\t\t\t<div>0.1x</div>\n\t\t\t\t\t\t\t<div>1x</div>\n\t\t\t\t\t\t\t<div>2x</div>\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");
var popup = new Popup(this.dom, this.playerControls, "\n\t\t\t\t<div class=\"spine-player-popup-title\">Speed</div>\n\t\t\t\t<hr>\n\t\t\t\t<div class=\"spine-player-row\" style=\"user-select: none; align-items: center; padding: 8px;\">\n\t\t\t\t\t<div class=\"spine-player-column\">\n\t\t\t\t\t\t<div class=\"spine-player-speed-slider\" style=\"margin-bottom: 4px;\"></div>\n\t\t\t\t\t\t<div class=\"spine-player-row\" style=\"justify-content: space-between;\">\n\t\t\t\t\t\t\t<div>0.1x</div>\n\t\t\t\t\t\t\t<div>1x</div>\n\t\t\t\t\t\t\t<div>2x</div>\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");
var sliderParent = findWithClass(popup.dom, "spine-player-speed-slider")[0];
var slider = new Slider(2, 0.1, true);
sliderParent.appendChild(slider.render());
@ -9692,13 +9733,16 @@ var spine;
slider.change = function (percentage) {
_this.speed = percentage * 2;
};
popup.show();
speedButton.classList.add("spine-player-button-icon-speed-selected");
popup.show(function () {
speedButton.classList.remove("spine-player-button-icon-speed-selected");
});
};
SpinePlayer.prototype.showAnimationsDialog = function () {
SpinePlayer.prototype.showAnimationsDialog = function (animationsButton) {
var _this = this;
if (!this.skeleton || this.skeleton.data.animations.length == 0)
return;
var popup = new Popup(this.playerControls, "\n\t\t\t\t<div class=\"spine-player-popup-title\">Animations</div>\n\t\t\t\t<hr>\n\t\t\t\t<ul class=\"spine-player-list\"></ul>\n\t\t\t");
var popup = new Popup(this.dom, this.playerControls, "\n\t\t\t\t<div class=\"spine-player-popup-title\">Animations</div>\n\t\t\t\t<hr>\n\t\t\t\t<ul class=\"spine-player-list\"></ul>\n\t\t\t");
var rows = findWithClass(popup.dom, "spine-player-list")[0];
this.skeleton.data.animations.forEach(function (animation) {
if (_this.config.animations && _this.config.animations.indexOf(animation.name) < 0) {
@ -9714,16 +9758,19 @@ var spine;
row.classList.add("selected");
_this.config.animation = animation.name;
_this.playTime = 0;
_this.animationState.setAnimation(0, _this.config.animation, true);
_this.setAnimation(animation.name);
};
});
popup.show();
animationsButton.classList.add("spine-player-button-icon-animations-selected");
popup.show(function () {
animationsButton.classList.remove("spine-player-button-icon-animations-selected");
});
};
SpinePlayer.prototype.showSkinsDialog = function () {
SpinePlayer.prototype.showSkinsDialog = function (skinButton) {
var _this = this;
if (!this.skeleton || this.skeleton.data.animations.length == 0)
return;
var popup = new Popup(this.playerControls, "\n\t\t\t\t<div class=\"spine-player-popup-title\">Skins</div>\n\t\t\t\t<hr>\n\t\t\t\t<ul class=\"spine-player-list\"></ul>\n\t\t\t");
var popup = new Popup(this.dom, this.playerControls, "\n\t\t\t\t<div class=\"spine-player-popup-title\">Skins</div>\n\t\t\t\t<hr>\n\t\t\t\t<ul class=\"spine-player-list\"></ul>\n\t\t\t");
var rows = findWithClass(popup.dom, "spine-player-list")[0];
this.skeleton.data.skins.forEach(function (skin) {
if (_this.config.skins && _this.config.skins.indexOf(skin.name) < 0) {
@ -9742,13 +9789,16 @@ var spine;
_this.skeleton.setSlotsToSetupPose();
};
});
popup.show();
skinButton.classList.add("spine-player-button-icon-skins-selected");
popup.show(function () {
skinButton.classList.remove("spine-player-button-icon-skins-selected");
});
};
SpinePlayer.prototype.showSettingsDialog = function () {
SpinePlayer.prototype.showSettingsDialog = function (settingsButton) {
var _this = this;
if (!this.skeleton || this.skeleton.data.animations.length == 0)
return;
var popup = new Popup(this.playerControls, "\n\t\t\t\t<div class=\"spine-player-popup-title\">Debug</div>\n\t\t\t\t<hr>\n\t\t\t\t<ul class=\"spine-player-list\">\n\t\t\t\t</li>\n\t\t\t");
var popup = new Popup(this.dom, this.playerControls, "\n\t\t\t\t<div class=\"spine-player-popup-title\">Debug</div>\n\t\t\t\t<hr>\n\t\t\t\t<ul class=\"spine-player-list\">\n\t\t\t\t</li>\n\t\t\t");
var rows = findWithClass(popup.dom, "spine-player-list")[0];
var makeItem = function (label, name) {
var row = createElement("<li class=\"spine-player-list-item\"></li>");
@ -9768,7 +9818,10 @@ var spine;
makeItem("Clipping", "clipping");
makeItem("Points", "points");
makeItem("Hulls", "hulls");
popup.show();
settingsButton.classList.add("spine-player-button-icon-settings-selected");
popup.show(function () {
settingsButton.classList.remove("spine-player-button-icon-settings-selected");
});
};
SpinePlayer.prototype.drawFrame = function (requestNextFrame) {
var _this = this;
@ -9802,15 +9855,36 @@ var spine;
this.animationState.apply(this.skeleton);
}
this.skeleton.updateWorldTransform();
var viewportSize = this.scale(this.config.viewport.width, this.config.viewport.height, this.canvas.width, this.canvas.height);
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;
var viewport = {
x: this.currentViewport.x - this.currentViewport.padLeft,
y: this.currentViewport.y - this.currentViewport.padBottom,
width: this.currentViewport.width + this.currentViewport.padLeft + this.currentViewport.padRight,
height: this.currentViewport.height + this.currentViewport.padBottom + this.currentViewport.padTop
};
var transitionAlpha = ((performance.now() - this.viewportTransitionStart) / 1000) / this.config.viewport.transitionTime;
if (this.previousViewport && transitionAlpha < 1) {
var oldViewport = {
x: this.previousViewport.x - this.previousViewport.padLeft,
y: this.previousViewport.y - this.previousViewport.padBottom,
width: this.previousViewport.width + this.previousViewport.padLeft + this.previousViewport.padRight,
height: this.previousViewport.height + this.previousViewport.padBottom + this.previousViewport.padTop
};
viewport = {
x: oldViewport.x + (viewport.x - oldViewport.x) * transitionAlpha,
y: oldViewport.y + (viewport.y - oldViewport.y) * transitionAlpha,
width: oldViewport.width + (viewport.width - oldViewport.width) * transitionAlpha,
height: oldViewport.height + (viewport.height - oldViewport.height) * transitionAlpha
};
}
var viewportSize = this.scale(viewport.width, viewport.height, this.canvas.width, this.canvas.height);
this.sceneRenderer.camera.zoom = viewport.width / viewportSize.x;
this.sceneRenderer.camera.position.x = viewport.x + viewport.width / 2;
this.sceneRenderer.camera.position.y = viewport.y + viewport.height / 2;
this.sceneRenderer.begin();
if (this.config.backgroundImage && this.config.backgroundImage.url) {
var bgImage = this.assetManager.get(this.config.backgroundImage.url);
if (!this.config.backgroundImage.x) {
this.sceneRenderer.drawTexture(bgImage, this.config.viewport.x, this.config.viewport.y, this.config.viewport.width, this.config.viewport.height);
this.sceneRenderer.drawTexture(bgImage, viewport.x, viewport.y, viewport.width, viewport.height);
}
else {
this.sceneRenderer.drawTexture(bgImage, this.config.backgroundImage.x, this.config.backgroundImage.y, this.config.backgroundImage.width, this.config.backgroundImage.height);
@ -9839,6 +9913,10 @@ var spine;
this.sceneRenderer.circle(false, skeleton.x + bone.worldX, skeleton.y + bone.worldY, 20, colorOuter);
}
gl.lineWidth(1);
if (this.config.viewport.debugRender) {
this.sceneRenderer.rect(false, this.currentViewport.x, this.currentViewport.y, this.currentViewport.width, this.currentViewport.height, spine.Color.GREEN);
this.sceneRenderer.rect(false, viewport.x, viewport.y, viewport.width, viewport.height, spine.Color.RED);
}
this.sceneRenderer.end();
this.sceneRenderer.camera.zoom = 0;
}
@ -9903,21 +9981,27 @@ var spine;
this.skeleton.setSkinByName(this.config.skin);
this.skeleton.setSlotsToSetupPose();
}
if (!this.config.viewport || !this.config.viewport.x || !this.config.viewport.y || !this.config.viewport.width || !this.config.viewport.height) {
if (!this.config.viewport) {
this.config.viewport = {
x: 0,
y: 0,
width: 0,
height: 0
animations: {},
debugRender: false,
transitionTime: 0.2
};
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 (typeof this.config.viewport.debugRender === "undefined")
this.config.viewport.debugRender = false;
if (typeof this.config.viewport.transitionTime === "undefined")
this.config.viewport.transitionTime = 0.2;
if (!this.config.viewport.animations) {
this.config.viewport.animations = {};
}
else {
Object.getOwnPropertyNames(this.config.viewport.animations).forEach(function (animation) {
if (!skeletonData.findAnimation(animation)) {
_this.showError("Error: animation '" + animation + "' for which a viewport was specified does not exist in skeleton.");
return;
}
});
}
if (this.config.animations && this.config.animations.length > 0) {
this.config.animations.forEach(function (animation) {
@ -10052,7 +10136,7 @@ var spine;
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);
this.setAnimation(this.config.animation);
}
}
};
@ -10061,6 +10145,103 @@ var spine;
this.playButton.classList.remove("spine-player-button-icon-pause");
this.playButton.classList.add("spine-player-button-icon-play");
};
SpinePlayer.prototype.setAnimation = function (animation) {
this.previousViewport = this.currentViewport;
var animViewport = this.calculateAnimationViewport(animation);
var viewport = {
x: animViewport.x,
y: animViewport.y,
width: animViewport.width,
height: animViewport.height,
padLeft: "10%",
padRight: "10%",
padTop: "10%",
padBottom: "10%"
};
var globalViewport = this.config.viewport;
if (typeof globalViewport.x !== "undefined" && typeof globalViewport.y !== "undefined" && typeof globalViewport.width !== "undefined" && typeof globalViewport.height !== "undefined") {
viewport.x = globalViewport.x;
viewport.y = globalViewport.y;
viewport.width = globalViewport.width;
viewport.height = globalViewport.height;
}
if (typeof globalViewport.padLeft !== "undefined")
viewport.padLeft = globalViewport.padLeft;
if (typeof globalViewport.padRight !== "undefined")
viewport.padRight = globalViewport.padRight;
if (typeof globalViewport.padTop !== "undefined")
viewport.padTop = globalViewport.padTop;
if (typeof globalViewport.padBottom !== "undefined")
viewport.padBottom = globalViewport.padBottom;
var userAnimViewport = this.config.viewport.animations[animation];
if (userAnimViewport) {
if (typeof userAnimViewport.x !== "undefined" && typeof userAnimViewport.y !== "undefined" && typeof userAnimViewport.width !== "undefined" && typeof userAnimViewport.height !== "undefined") {
viewport.x = userAnimViewport.x;
viewport.y = userAnimViewport.y;
viewport.width = userAnimViewport.width;
viewport.height = userAnimViewport.height;
}
if (typeof userAnimViewport.padLeft !== "undefined")
viewport.padLeft = userAnimViewport.padLeft;
if (typeof userAnimViewport.padRight !== "undefined")
viewport.padRight = userAnimViewport.padRight;
if (typeof userAnimViewport.padTop !== "undefined")
viewport.padTop = userAnimViewport.padTop;
if (typeof userAnimViewport.padBottom !== "undefined")
viewport.padBottom = userAnimViewport.padBottom;
}
viewport.padLeft = this.percentageToWorldUnit(viewport.width, viewport.padLeft);
viewport.padRight = this.percentageToWorldUnit(viewport.width, viewport.padRight);
viewport.padBottom = this.percentageToWorldUnit(viewport.height, viewport.padBottom);
viewport.padTop = this.percentageToWorldUnit(viewport.height, viewport.padTop);
this.currentViewport = viewport;
this.viewportTransitionStart = performance.now();
this.animationState.clearTracks();
this.skeleton.setToSetupPose();
this.animationState.setAnimation(0, this.config.animation, true);
};
SpinePlayer.prototype.percentageToWorldUnit = function (size, percentageOrAbsolute) {
if (typeof percentageOrAbsolute === "string") {
return size * parseFloat(percentageOrAbsolute.substr(0, percentageOrAbsolute.length - 1)) / 100;
}
else {
return percentageOrAbsolute;
}
};
SpinePlayer.prototype.calculateAnimationViewport = function (animationName) {
var animation = this.skeleton.data.findAnimation(animationName);
this.animationState.clearTracks();
this.skeleton.setToSetupPose();
this.animationState.setAnimationWith(0, animation, true);
var steps = 100;
var stepTime = animation.duration > 0 ? animation.duration / steps : 0;
var minX = 100000000;
var maxX = -100000000;
var minY = 100000000;
var maxY = -100000000;
var offset = new spine.Vector2();
var size = new spine.Vector2();
for (var i = 0; i < steps; i++) {
this.animationState.update(stepTime);
this.animationState.apply(this.skeleton);
this.skeleton.updateWorldTransform();
this.skeleton.getBounds(offset, size);
minX = Math.min(offset.x, minX);
maxX = Math.max(offset.x + size.x, maxX);
minY = Math.min(offset.y, minY);
maxY = Math.max(offset.y + size.y, maxY);
}
offset.x = minX;
offset.y = minY;
size.x = maxX - minX;
size.y = maxY - minY;
return {
x: offset.x,
y: offset.y,
width: size.x,
height: size.y
};
};
SpinePlayer.HOVER_COLOR_INNER = new spine.Color(0.478, 0, 0, 0.25);
SpinePlayer.HOVER_COLOR_OUTER = new spine.Color(1, 1, 1, 1);
SpinePlayer.NON_HOVER_COLOR_INNER = new spine.Color(0.478, 0, 0, 0.5);

File diff suppressed because one or more lines are too long

View File

@ -50,20 +50,20 @@
position: absolute;
bottom: 0;
height: 2px;
background: rgb(98, 176, 238, 0.6);
background: rgba(98, 176, 238, 0.6);
cursor: pointer;
}
.spine-player-slider:hover .spine-player-slider-value {
height: 4px;
background: rgb(98, 176, 238, 1);
transition: height 0.5s;
background: rgba(98, 176, 238, 1);
transition: height 0.2s;
}
.spine-player-slider-value.hovering {
height: 4px;
background: rgb(98, 176, 238, 1);
transition: height 0.5s;
background: rgba(98, 176, 238, 1);
transition: height 0.2s;
}
.spine-player-slider.big {
@ -73,7 +73,7 @@
.spine-player-slider.big .spine-player-slider-value {
height: 12px;
background: rgb(98, 176, 238, 1);
background: rgba(98, 176, 238, 1);
}
/** Column and row layout elements **/
@ -178,11 +178,12 @@
left: 0px;
top: -2px;
filter: drop-shadow(0 0 1px #333);
transition: left 0.2s; /* animates switch changing */
transition: transform 0.2s;
}
.spine-player-switch.active .spine-player-switch-knob {
background: #fff;
left: 18px;
transform: translateX(18px);
transition: transform 0.2s;
}
/** Popup **/
@ -278,6 +279,10 @@
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-button-icon-play-selected {
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-button-icon-pause {
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");
}
@ -286,6 +291,10 @@
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-button-icon-pause-selected {
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-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");
}
@ -294,6 +303,10 @@
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-button-icon-speed-selected {
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-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")
}
@ -302,6 +315,10 @@
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-button-icon-animations-selected {
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-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");
width: 31px;
@ -312,6 +329,10 @@
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-button-icon-skins-selected {
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-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");
margin-top: 1px;
@ -321,6 +342,10 @@
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-button-icon-settings-selected {
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-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");
margin-top: 1px;
@ -330,6 +355,10 @@
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-button-icon-fullscreen-selected {
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-button-icon-spine-logo {
height: 20px;
position: relative;
@ -340,6 +369,8 @@
width: auto !important;
cursor: pointer;
transition: transform 0.2s;
box-shadow: none !important;
filter: drop-shadow(0 0 1px #333);
}
.spine-player-button-icon-spine-logo:hover {

View File

@ -15,17 +15,17 @@ body {
</style>
<body>
<div id="container" style="width: 480px; height: 320px;"></div>
<div id="container" style="width: 100%; height: 90vh;"></div>
</body>
<script>
new spine.SpinePlayer(document.getElementById("container"), {
jsonUrl: "assets/spineboy-pro.json",
atlasUrl: "assets/spineboy-pma.atlas",
premultipliedAlpha: true,
animations: ["walk", "jump"],
controlBones: ["root"],
backgroundColor: "#cccccc",
fullScreenBackgroundColor: "#cc0000",
viewport: {
debugRender: true,
}
});
</script>
</body>

View File

@ -29,6 +29,17 @@
*****************************************************************************/
module spine {
export interface Viewport {
x: number,
y: number,
width: number,
height: number,
padLeft: string | number
padRight: string | number
padTop: string | number
padBottom: string | number
}
export interface SpinePlayerConfig {
/* the URL of the skeleton .json file */
jsonUrl: string
@ -75,6 +86,13 @@
y: number
width: number
height: number
padLeft: string | number
padRight: string | number
padTop: string | number
padBottom: string | number
animations: Map<Viewport>
debugRender: boolean,
transitionTime: number
}
/* Optional: whether the canvas should be transparent. Default: false. */
@ -108,7 +126,7 @@
class Popup {
public dom: HTMLElement;
constructor(parent: HTMLElement, htmlContent: string) {
constructor(private player: HTMLElement, parent: HTMLElement, htmlContent: string) {
this.dom = createElement(/*html*/`
<div class="spine-player-popup spine-player-hidden">
</div>
@ -117,8 +135,22 @@
parent.appendChild(this.dom);
}
show () {
show (dismissedListener = () => {}) {
this.dom.classList.remove("spine-player-hidden");
// Make sure the popup isn't bigger than the player.
var dismissed = false;
let resize = () => {
if (!dismissed) requestAnimationFrame(resize);
let bottomOffset = Math.abs(this.dom.getBoundingClientRect().bottom - this.player.getBoundingClientRect().bottom);
let rightOffset = Math.abs(this.dom.getBoundingClientRect().right - this.player.getBoundingClientRect().right);
let maxHeight = this.player.clientHeight - bottomOffset - rightOffset;
this.dom.style.maxHeight = maxHeight + "px";
}
requestAnimationFrame(resize);
// Dismiss when clicking somewhere else outside
// the popup
var justClicked = true;
let windowClickListener = (event: any) => {
if (justClicked) {
@ -128,6 +160,8 @@
if (!isContained(this.dom, event.target)) {
this.dom.parentNode.removeChild(this.dom);
window.removeEventListener("click", windowClickListener);
dismissedListener();
dismissed = true;
}
}
window.addEventListener("click", windowClickListener);
@ -217,6 +251,8 @@
if (this.change) this.change(percentage);
}
});
return this.slider;
}
@ -265,6 +301,11 @@
private playTime = 0;
private speed = 1;
private animationViewports: Map<Viewport> = {}
private currentViewport: Viewport = null;
private previousViewport: Viewport = null;
private viewportTransitionStart = 0;
private selectedBones: Bone[];
constructor(parent: HTMLElement, private config: SpinePlayerConfig) {
@ -396,29 +437,56 @@
}
speedButton.onclick = () => {
this.showSpeedDialog();
this.showSpeedDialog(speedButton);
}
this.animationButton.onclick = () => {
this.showAnimationsDialog();
this.showAnimationsDialog(this.animationButton);
}
this.skinButton.onclick = () => {
this.showSkinsDialog();
this.showSkinsDialog(this.skinButton);
}
settingsButton.onclick = () => {
this.showSettingsDialog();
this.showSettingsDialog(settingsButton);
}
let oldWidth = this.canvas.clientWidth;
let oldHeight = this.canvas.clientHeight;
let oldStyleWidth = this.canvas.style.width;
let oldStyleHeight = this.canvas.style.height;
var isFullscreen = false;
fullscreenButton.onclick = () => {
let fullscreenChanged = () => {
isFullscreen = !isFullscreen;
if (!isFullscreen) {
this.canvas.style.width = "" + oldWidth + "px";
this.canvas.style.height = "" + oldHeight + "px";
this.drawFrame(false);
// Got to reset the style to whatever the user set
// after the next layouting.
requestAnimationFrame(() => {
this.canvas.style.width = oldStyleWidth;
this.canvas.style.height = oldStyleHeight;
});
}
};
let doc = document as any;
(dom as any).onfullscreenchange = fullscreenChanged;
dom.onwebkitfullscreenchange = fullscreenChanged;
if(doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement) {
if (doc.exitFullscreen) doc.exitFullscreen();
else if (doc.mozCancelFullScreen) doc.mozCancelFullScreen();
else if (doc.webkitExitFullscreen) doc.webkitExitFullscreen()
else if (doc.msExitFullscreen) doc.msExitFullscreen();
} else {
oldWidth = this.canvas.clientWidth;
oldHeight = this.canvas.clientHeight;
oldStyleWidth = this.canvas.style.width;
oldStyleHeight = this.canvas.style.height;
let player = dom as any;
if (player.requestFullscreen) player.requestFullscreen();
else if (player.webkitRequestFullScreen) player.webkitRequestFullScreen();
@ -428,7 +496,7 @@
};
logoButton.onclick = () => {
(window.location as any)= "http://esotericsoftware.com";
window.open("http://esotericsoftware.com");
};
// Register a global resize handler to redraw and avoid flicker
@ -439,10 +507,11 @@
return dom;
}
showSpeedDialog () {
let popup = new Popup(this.playerControls, /*html*/`
showSpeedDialog (speedButton: HTMLElement) {
let popup = new Popup(this.dom, this.playerControls, /*html*/`
<div class="spine-player-popup-title">Speed</div>
<hr>
<div class="spine-player-row" style="user-select: none; align-items: center; padding: 8px;">
<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;">
@ -460,13 +529,16 @@
slider.change = (percentage) => {
this.speed = percentage * 2;
}
popup.show();
speedButton.classList.add("spine-player-button-icon-speed-selected")
popup.show(() => {
speedButton.classList.remove("spine-player-button-icon-speed-selected")
});
}
showAnimationsDialog () {
showAnimationsDialog (animationsButton: HTMLElement) {
if (!this.skeleton || this.skeleton.data.animations.length == 0) return;
let popup = new Popup(this.playerControls, /*html*/`
let popup = new Popup(this.dom, this.playerControls, /*html*/`
<div class="spine-player-popup-title">Animations</div>
<hr>
<ul class="spine-player-list"></ul>
@ -495,16 +567,19 @@
row.classList.add("selected");
this.config.animation = animation.name;
this.playTime = 0;
this.animationState.setAnimation(0, this.config.animation, true);
this.setAnimation(animation.name);
}
});
popup.show();
animationsButton.classList.add("spine-player-button-icon-animations-selected")
popup.show(() => {
animationsButton.classList.remove("spine-player-button-icon-animations-selected")
});
}
showSkinsDialog () {
showSkinsDialog (skinButton: HTMLElement) {
if (!this.skeleton || this.skeleton.data.animations.length == 0) return;
let popup = new Popup(this.playerControls, /*html*/`
let popup = new Popup(this.dom, this.playerControls, /*html*/`
<div class="spine-player-popup-title">Skins</div>
<hr>
<ul class="spine-player-list"></ul>
@ -537,13 +612,16 @@
}
});
popup.show();
skinButton.classList.add("spine-player-button-icon-skins-selected")
popup.show(() => {
skinButton.classList.remove("spine-player-button-icon-skins-selected")
});
}
showSettingsDialog () {
showSettingsDialog (settingsButton: HTMLElement) {
if (!this.skeleton || this.skeleton.data.animations.length == 0) return;
let popup = new Popup(this.playerControls, /*html*/`
let popup = new Popup(this.dom, this.playerControls, /*html*/`
<div class="spine-player-popup-title">Debug</div>
<hr>
<ul class="spine-player-list">
@ -571,7 +649,10 @@
makeItem("Points", "points");
makeItem("Hulls", "hulls");
popup.show();
settingsButton.classList.add("spine-player-button-icon-settings-selected")
popup.show(() => {
settingsButton.classList.remove("spine-player-button-icon-settings-selected")
});
}
drawFrame (requestNextFrame = true) {
@ -617,11 +698,35 @@
this.skeleton.updateWorldTransform();
let viewportSize = this.scale(this.config.viewport.width, this.config.viewport.height, this.canvas.width, this.canvas.height);
let viewport = {
x: this.currentViewport.x - (this.currentViewport.padLeft as number),
y: this.currentViewport.y - (this.currentViewport.padBottom as number),
width: this.currentViewport.width + (this.currentViewport.padLeft as number) + (this.currentViewport.padRight as number),
height: this.currentViewport.height + (this.currentViewport.padBottom as number) + (this.currentViewport.padTop as number)
}
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;
let transitionAlpha = ((performance.now() - this.viewportTransitionStart) / 1000) / this.config.viewport.transitionTime;
if (this.previousViewport && transitionAlpha < 1) {
let oldViewport = {
x: this.previousViewport.x - (this.previousViewport.padLeft as number),
y: this.previousViewport.y - (this.previousViewport.padBottom as number),
width: this.previousViewport.width + (this.previousViewport.padLeft as number) + (this.previousViewport.padRight as number),
height: this.previousViewport.height + (this.previousViewport.padBottom as number) + (this.previousViewport.padTop as number)
}
viewport = {
x: oldViewport.x + (viewport.x - oldViewport.x) * transitionAlpha,
y: oldViewport.y + (viewport.y - oldViewport.y) * transitionAlpha,
width: oldViewport.width + (viewport.width - oldViewport.width) * transitionAlpha,
height: oldViewport.height + (viewport.height - oldViewport.height) * transitionAlpha
}
}
let viewportSize = this.scale(viewport.width, viewport.height, this.canvas.width, this.canvas.height);
this.sceneRenderer.camera.zoom = viewport.width / viewportSize.x;
this.sceneRenderer.camera.position.x = viewport.x + viewport.width / 2;
this.sceneRenderer.camera.position.y = viewport.y + viewport.height / 2;
this.sceneRenderer.begin();
@ -629,7 +734,7 @@
if (this.config.backgroundImage && this.config.backgroundImage.url) {
let bgImage = this.assetManager.get(this.config.backgroundImage.url);
if (!this.config.backgroundImage.x) {
this.sceneRenderer.drawTexture(bgImage, this.config.viewport.x, this.config.viewport.y, this.config.viewport.width, this.config.viewport.height);
this.sceneRenderer.drawTexture(bgImage, viewport.x, viewport.y, viewport.width, viewport.height);
} else {
this.sceneRenderer.drawTexture(bgImage, this.config.backgroundImage.x, this.config.backgroundImage.y, this.config.backgroundImage.width, this.config.backgroundImage.height);
}
@ -661,6 +766,12 @@
}
gl.lineWidth(1);
// Render the viewport bounds
if (this.config.viewport.debugRender) {
this.sceneRenderer.rect(false, this.currentViewport.x, this.currentViewport.y, this.currentViewport.width, this.currentViewport.height, Color.GREEN);
this.sceneRenderer.rect(false, viewport.x, viewport.y, viewport.width, viewport.height, Color.RED);
}
this.sceneRenderer.end();
this.sceneRenderer.camera.zoom = 0;
@ -734,23 +845,27 @@
this.skeleton.setSlotsToSetupPose();
}
// Setup viewport after skin is set
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
// Setup empty viewport if none is given and check
// if all animations for which viewports where given
// exist.
if (!this.config.viewport) {
(this.config.viewport as any) = {
animations: {},
debugRender: false,
transitionTime: 0.2
}
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;
}
if (typeof this.config.viewport.debugRender === "undefined") this.config.viewport.debugRender = false;
if (typeof this.config.viewport.transitionTime === "undefined") this.config.viewport.transitionTime = 0.2;
if (!this.config.viewport.animations) {
this.config.viewport.animations = {};
} else {
Object.getOwnPropertyNames(this.config.viewport.animations).forEach((animation: string) => {
if (!skeletonData.findAnimation(animation)) {
this.showError(`Error: animation '${animation}' for which a viewport was specified does not exist in skeleton.`);
return;
}
});
}
// Setup the animations after viewport, so default bounds don't get messed up.
@ -897,7 +1012,7 @@
if (this.config.animation) {
if (!this.animationState.getCurrent(0)) {
this.animationState.setAnimation(0, this.config.animation, true);
this.setAnimation(this.config.animation);
}
}
}
@ -907,6 +1022,114 @@
this.playButton.classList.remove("spine-player-button-icon-pause");
this.playButton.classList.add("spine-player-button-icon-play");
}
private setAnimation (animation: string) {
// Determine viewport
this.previousViewport = this.currentViewport;
let animViewport = this.calculateAnimationViewport(animation);
// The calculated animation viewport is the base
let viewport: Viewport = {
x: animViewport.x,
y: animViewport.y,
width: animViewport.width,
height: animViewport.height,
padLeft: "10%",
padRight: "10%",
padTop: "10%",
padBottom: "10%"
}
// Override with global viewport settings if they exist
let globalViewport = this.config.viewport;
if (typeof globalViewport.x !== "undefined" && typeof globalViewport.y !== "undefined" && typeof globalViewport.width !== "undefined" && typeof globalViewport.height !== "undefined") {
viewport.x = globalViewport.x;
viewport.y = globalViewport.y;
viewport.width = globalViewport.width;
viewport.height = globalViewport.height;
}
if (typeof globalViewport.padLeft !== "undefined") viewport.padLeft = globalViewport.padLeft;
if (typeof globalViewport.padRight !== "undefined") viewport.padRight = globalViewport.padRight;
if (typeof globalViewport.padTop !== "undefined") viewport.padTop = globalViewport.padTop;
if (typeof globalViewport.padBottom !== "undefined") viewport.padBottom = globalViewport.padBottom;
// Override with animation viewport settings given by user for final result.
let userAnimViewport = this.config.viewport.animations[animation];
if (userAnimViewport) {
if (typeof userAnimViewport.x !== "undefined" && typeof userAnimViewport.y !== "undefined" && typeof userAnimViewport.width !== "undefined" && typeof userAnimViewport.height !== "undefined") {
viewport.x = userAnimViewport.x;
viewport.y = userAnimViewport.y;
viewport.width = userAnimViewport.width;
viewport.height = userAnimViewport.height;
}
if (typeof userAnimViewport.padLeft !== "undefined") viewport.padLeft = userAnimViewport.padLeft;
if (typeof userAnimViewport.padRight !== "undefined") viewport.padRight = userAnimViewport.padRight;
if (typeof userAnimViewport.padTop !== "undefined") viewport.padTop = userAnimViewport.padTop;
if (typeof userAnimViewport.padBottom !== "undefined") viewport.padBottom = userAnimViewport.padBottom;
}
// Translate percentage paddings to world units
viewport.padLeft = this.percentageToWorldUnit(viewport.width, viewport.padLeft);
viewport.padRight = this.percentageToWorldUnit(viewport.width, viewport.padRight);
viewport.padBottom = this.percentageToWorldUnit(viewport.height, viewport.padBottom);
viewport.padTop = this.percentageToWorldUnit(viewport.height, viewport.padTop);
// Adjust x, y, width, and height by padding.
this.currentViewport = viewport;
this.viewportTransitionStart = performance.now();
this.animationState.clearTracks();
this.skeleton.setToSetupPose();
this.animationState.setAnimation(0, this.config.animation, true);
}
private percentageToWorldUnit(size: number, percentageOrAbsolute: string | number): number {
if (typeof percentageOrAbsolute === "string") {
return size * parseFloat(percentageOrAbsolute.substr(0, percentageOrAbsolute.length - 1)) / 100;
} else {
return percentageOrAbsolute;
}
}
private calculateAnimationViewport (animationName: string) {
let animation = this.skeleton.data.findAnimation(animationName);
this.animationState.clearTracks();
this.skeleton.setToSetupPose()
this.animationState.setAnimationWith(0, animation, true);
let steps = 100;
let stepTime = animation.duration > 0 ? animation.duration / steps : 0;
let minX = 100000000;
let maxX = -100000000;
let minY = 100000000;
let maxY = -100000000;
let offset = new spine.Vector2();
let size = new spine.Vector2();
for (var i = 0; i < steps; i++) {
this.animationState.update(stepTime);
this.animationState.apply(this.skeleton);
this.skeleton.updateWorldTransform();
this.skeleton.getBounds(offset, size);
minX = Math.min(offset.x, minX);
maxX = Math.max(offset.x + size.x, maxX);
minY = Math.min(offset.y, minY);
maxY = Math.max(offset.y + size.y, maxY);
}
offset.x = minX;
offset.y = minY;
size.x = maxX - minX;
size.y = maxY - minY;
return {
x: offset.x,
y: offset.y,
width: size.x,
height: size.y
};
}
}
function isContained(dom: HTMLElement, needle: HTMLElement): boolean {