[ts][player] Added user defined global and per animation viewport. Viewports can also have padding.

This commit is contained in:
badlogic 2018-11-23 14:18:48 +01:00
parent 8610fd5446
commit f6eb468d32
5 changed files with 235 additions and 78 deletions

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,20 +1718,12 @@ declare module spine {
y: number;
width: number;
height: number;
padLeft: string;
padRight: string;
padTop: string;
padBottom: string;
animations: Map<{
x: number;
y: number;
width: number;
height: number;
padLeft: string;
padRight: string;
padTop: string;
padBottom: string;
}>;
padLeft: string | number;
padRight: string | number;
padTop: string | number;
padBottom: string | number;
animations: Map<Viewport>;
debugRender: boolean;
};
alpha: boolean;
backgroundColor: string;
@ -1779,6 +1781,7 @@ declare module spine {
private play();
private pause();
private setAnimation(animation);
private percentageToWorldUnit(size, percentageOrAbsolute);
private calculateAnimationViewport(animationName);
}
}

View File

@ -9854,7 +9854,12 @@ var spine;
this.animationState.apply(this.skeleton);
}
this.skeleton.updateWorldTransform();
var viewport = this.currentViewport;
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 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;
@ -9892,6 +9897,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;
}
@ -9956,6 +9965,25 @@ var spine;
this.skeleton.setSkinByName(this.config.skin);
this.skeleton.setSlotsToSetupPose();
}
if (!this.config.viewport) {
this.config.viewport = {
animations: {},
debugRender: false
};
}
if (typeof this.config.viewport.debugRender === "undefined")
this.config.viewport.debugRender = false;
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) {
if (!_this.skeleton.data.findAnimation(animation)) {
@ -9988,18 +10016,6 @@ var spine;
_this.playTime = time;
};
}
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,
padLeft: "0",
padRight: "0",
padTop: "0",
padBottom: "0"
};
}
this.setupInput();
if (skeletonData.skins.length == 1)
this.skinButton.classList.add("spine-player-hidden");
@ -10112,11 +10128,66 @@ var spine;
};
SpinePlayer.prototype.setAnimation = function (animation) {
this.previousViewport = this.currentViewport;
this.currentViewport = this.calculateAnimationViewport(animation);
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.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();
@ -10145,10 +10216,10 @@ var spine;
size.x = maxX - minX;
size.y = maxY - minY;
return {
x: offset.x + size.x / 2 - size.x / 2 * 1.2,
y: offset.y + size.y / 2 - size.y / 2 * 1.2,
width: size.x * 1.2,
height: size.y * 1.2
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);

File diff suppressed because one or more lines are too long

View File

@ -22,10 +22,19 @@ body {
jsonUrl: "assets/spineboy-pro.json",
atlasUrl: "assets/spineboy-pma.atlas",
premultipliedAlpha: true,
animations: ["walk", "jump"],
controlBones: ["root"],
backgroundColor: "#cccccc",
fullScreenBackgroundColor: "#cc0000",
viewport: {
padLeft: "10%",
padRight: "10%",
padTop: "10%",
padBottom: "10%",
debugRender: true,
animations: {
"jump": {
padTop: "-20%"
}
}
}
});
</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,20 +86,12 @@
y: number
width: number
height: number
padLeft: string
padRight: string
padTop: string
padBottom: string
animations: Map<{
x: number,
y: number,
width: number,
height: number,
padLeft: string,
padRight: string,
padTop: string,
padBottom: string,
}>
padLeft: string | number
padRight: string | number
padTop: string | number
padBottom: string | number
animations: Map<Viewport>
debugRender: boolean
}
/* Optional: whether the canvas should be transparent. Default: false. */
@ -270,13 +273,6 @@
}
}
interface Viewport {
x: number
y: number
width: number
height: number
}
export class SpinePlayer {
static HOVER_COLOR_INNER = new spine.Color(0.478, 0, 0, 0.25);
static HOVER_COLOR_OUTER = new spine.Color(1, 1, 1, 1);
@ -700,7 +696,13 @@
this.skeleton.updateWorldTransform();
let viewport = this.currentViewport;
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)
}
let viewportSize = this.scale(viewport.width, viewport.height, this.canvas.width, this.canvas.height);
this.sceneRenderer.camera.zoom = viewport.width / viewportSize.x;
@ -745,6 +747,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;
@ -818,6 +826,27 @@
this.skeleton.setSlotsToSetupPose();
}
// 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
}
}
if (typeof this.config.viewport.debugRender === "undefined") this.config.viewport.debugRender = false;
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.
if (this.config.animations && this.config.animations.length > 0) {
this.config.animations.forEach(animation => {
@ -855,20 +884,6 @@
}
}
// 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,
padLeft: "0",
padRight: "0",
padTop: "0",
padBottom: "0"
}
}
// Setup the input processor and controllable bones
this.setupInput();
@ -988,14 +1003,73 @@
}
private setAnimation (animation: string) {
// Determine viewport
this.previousViewport = this.currentViewport;
this.currentViewport = this.calculateAnimationViewport(animation);
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.animationState.clearTracks();
this.skeleton.setToSetupPose();
this.animationState.setAnimation(0, this.config.animation, true);
}
private calculateAnimationViewport (animationName: string): Viewport {
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()
@ -1028,10 +1102,10 @@
size.y = maxY - minY;
return {
x: offset.x + size.x / 2 - size.x / 2 * 1.2,
y: offset.y + size.y / 2 - size.y / 2 * 1.2,
width: size.x * 1.2,
height: size.y * 1.2
x: offset.x,
y: offset.y,
width: size.x,
height: size.y
};
}
}