mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 07:14:55 +08:00
[ts][player] Added user defined global and per animation viewport. Viewports can also have padding.
This commit is contained in:
parent
8610fd5446
commit
f6eb468d32
31
spine-ts/build/spine-widget.d.ts
vendored
31
spine-ts/build/spine-widget.d.ts
vendored
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
@ -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>
|
||||
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user