[ts][player] Fixed text selection on speed slider modification in FF, added snapping to speed slider, added hiding timeline if mouse doesn't move for 1 second except when popup is active or mouse is over controls, decided against popup centered over button, removed Show prefix from debug popup, added hiding buttons if there is only one skin/animation, fixed control bone not moving when paused.

This commit is contained in:
badlogic 2018-11-14 14:29:27 +01:00
parent 8b49ad91cb
commit 94eec340c4
7 changed files with 12094 additions and 11964 deletions

View File

@ -444,7 +444,7 @@ void test (SkeletonData* skeletonData, Atlas* atlas) {
Skeleton skeleton(skeletonData); Skeleton skeleton(skeletonData);
AnimationStateData animationStateData(skeletonData); AnimationStateData animationStateData(skeletonData);
AnimationState animationState(&animationStateData); AnimationState animationState(&animationStateData);
animationState.setAnimation(0, "drive", true); animationState.setAnimation(0, "idle", true);
float d = 3; float d = 3;
for (int i = 0; i < 1; i++) { for (int i = 0; i < 1; i++) {
@ -459,6 +459,7 @@ int main () {
DebugExtension dbgExtension(SpineExtension::getInstance()); DebugExtension dbgExtension(SpineExtension::getInstance());
SpineExtension::setInstance(&dbgExtension); SpineExtension::setInstance(&dbgExtension);
testcase(test, "data/yellow_god.json", "data/yellow_god.skel", "data/yellow_god.atlas", 0.6f);
testcase(spineboy, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy.atlas", 0.6f); testcase(spineboy, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy.atlas", 0.6f);
testcase(stretchymanStrechyIk, "data/stretchyman-stretchy-ik.json", "data/stretchyman-stretchy-ik.skel", "data/stretchyman.atlas", 0.6f); testcase(stretchymanStrechyIk, "data/stretchyman-stretchy-ik.json", "data/stretchyman-stretchy-ik.skel", "data/stretchyman.atlas", 0.6f);
testcase(spineboy, "data/spineboy-ess.json", "data/spineboy-ess.skel", "data/spineboy.atlas", 0.6f); testcase(spineboy, "data/spineboy-ess.json", "data/spineboy-ess.skel", "data/spineboy.atlas", 0.6f);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -152,6 +152,8 @@
right: 2px; right: 2px;
bottom: 42px; bottom: 42px;
border-radius: 4px; border-radius: 4px;
max-height: 400%;
overflow: auto;
} }
.spine-player-popup-title { .spine-player-popup-title {
@ -180,13 +182,17 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
opacity: 0;
transition: opacity 0.2s;
} }
.spine-player:hover .spine-player-controls { .spine-player-controls.hidden {
pointer-events: none;
opacity: 0;
transition: opacity 0.4s;
}
.spine-player-controls.visible {
opacity: 1; opacity: 1;
transition: opacity 0.2s; transition: opacity 0.4s;
} }
/** Player buttons **/ /** Player buttons **/
@ -208,7 +214,7 @@
height: 32px; height: 32px;
background-size: 20px; background-size: 20px;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 6; background-position: center;
cursor: pointer; cursor: pointer;
} }

View File

@ -1,15 +1,21 @@
<!doctype html>
<html> <html>
<script src="../../build/spine-widget.js"></script> <head>
<link rel="stylesheet" href="../css/spine-player.css"> <meta charset="utf-8">
<script src="../../build/spine-widget.js"></script>
<link rel="stylesheet" href="../css/spine-player.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<style> <style>
body { body {
background: gray; background: gray;
margin: 0px;
} }
</style> </style>
<body> <body>
<div id="container" style="width: 100%; height: 100%;"></div> <div id="container" style="width: 100%; height: 100vh;"></div>
</body> </body>
<script> <script>
new spine.SpinePlayer(document.getElementById("container"), { new spine.SpinePlayer(document.getElementById("container"), {
@ -17,7 +23,8 @@ body {
atlasUrl: "assets/spineboy.atlas", atlasUrl: "assets/spineboy.atlas",
animations: ["walk", "jump"], animations: ["walk", "jump"],
controlBones: ["root"], controlBones: ["root"],
backgroundColor: "#cccccc" backgroundColor: "#cccccc",
showControls: false,
}); });
</script> </script>
</body> </body>

View File

@ -170,6 +170,8 @@
private value: HTMLElement; private value: HTMLElement;
public change: (percentage: number) => void; public change: (percentage: number) => void;
constructor(public snaps = 0, public snapPercentage = 0.1) { }
render(): HTMLElement { render(): HTMLElement {
this.slider = createElement(/*html*/` this.slider = createElement(/*html*/`
<div class="spine-player-slider"> <div class="spine-player-slider">
@ -188,7 +190,7 @@
up: (x, y) => { up: (x, y) => {
dragging = false; dragging = false;
let percentage = x / this.slider.clientWidth; let percentage = x / this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1)); percentage = percentage = Math.max(0, Math.min(percentage, 1));
this.setValue(x / this.slider.clientWidth); this.setValue(x / this.slider.clientWidth);
if (this.change) this.change(percentage); if (this.change) this.change(percentage);
}, },
@ -196,23 +198,34 @@
if (dragging) { if (dragging) {
let percentage = x / this.slider.clientWidth; let percentage = x / this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1)); percentage = Math.max(0, Math.min(percentage, 1));
this.setValue(x / this.slider.clientWidth); percentage = this.setValue(x / this.slider.clientWidth);
if (this.change) this.change(percentage); if (this.change) this.change(percentage);
} }
}, },
dragged: (x, y) => { dragged: (x, y) => {
let percentage = x / this.slider.clientWidth; let percentage = x / this.slider.clientWidth;
percentage = Math.max(0, Math.min(percentage, 1)); percentage = Math.max(0, Math.min(percentage, 1));
this.setValue(x / this.slider.clientWidth); percentage = this.setValue(x / this.slider.clientWidth);
if (this.change) this.change(percentage); if (this.change) this.change(percentage);
} }
}); });
return this.slider; return this.slider;
} }
setValue(percentage: number) { setValue(percentage: number): number {
percentage = Math.max(0, Math.min(1, percentage)); percentage = Math.max(0, Math.min(1, percentage));
if (this.snaps > 0) {
let modulo = percentage % (1 / this.snaps);
// floor
if (modulo < (1 / this.snaps) * this.snapPercentage) {
percentage = percentage - modulo;
} else if (modulo > (1 / this.snaps) - (1 / this.snaps) * this.snapPercentage) {
percentage = percentage - modulo + (1 / this.snaps);
}
percentage = Math.max(0, Math.min(1, percentage));
}
this.value.style.width = "" + (percentage * 100) + "%"; this.value.style.width = "" + (percentage * 100) + "%";
return percentage;
} }
} }
@ -228,6 +241,8 @@
private canvas: HTMLCanvasElement; private canvas: HTMLCanvasElement;
private timelineSlider: Slider; private timelineSlider: Slider;
private playButton: HTMLElement; private playButton: HTMLElement;
private skinButton: HTMLElement;
private animationButton: HTMLElement;
private context: spine.webgl.ManagedWebGLRenderingContext; private context: spine.webgl.ManagedWebGLRenderingContext;
private loadingScreen: spine.webgl.LoadingScreen; private loadingScreen: spine.webgl.LoadingScreen;
@ -303,7 +318,7 @@
<div class="spine-player"> <div class="spine-player">
<canvas class="spine-player-canvas"></canvas> <canvas class="spine-player-canvas"></canvas>
<div class="spine-player-error spine-player-hidden"></div> <div class="spine-player-error spine-player-hidden"></div>
<div class="spine-player-controls spine-player-popup-parent"> <div class="spine-player-controls spine-player-popup-parent hidden">
<div class="spine-player-timeline"> <div class="spine-player-timeline">
</div> </div>
<div class="spine-player-buttons"> <div class="spine-player-buttons">
@ -357,8 +372,8 @@
timeline.appendChild(this.timelineSlider.render()); timeline.appendChild(this.timelineSlider.render());
this.playButton = findWithId(dom, "spine-player-button-play-pause")[0]; this.playButton = findWithId(dom, "spine-player-button-play-pause")[0];
let speedButton = findWithId(dom, "spine-player-button-speed")[0]; let speedButton = findWithId(dom, "spine-player-button-speed")[0];
let animationButton = findWithId(dom, "spine-player-button-animation")[0]; this.animationButton = findWithId(dom, "spine-player-button-animation")[0];
let skinButton = findWithId(dom, "spine-player-button-skin")[0]; this.skinButton = findWithId(dom, "spine-player-button-skin")[0];
let settingsButton = findWithId(dom, "spine-player-button-settings")[0]; let settingsButton = findWithId(dom, "spine-player-button-settings")[0];
let fullscreenButton = findWithId(dom, "spine-player-button-fullscreen")[0]; let fullscreenButton = findWithId(dom, "spine-player-button-fullscreen")[0];
@ -371,11 +386,11 @@
this.showSpeedDialog(); this.showSpeedDialog();
} }
animationButton.onclick = () => { this.animationButton.onclick = () => {
this.showAnimationsDialog(); this.showAnimationsDialog();
} }
skinButton.onclick = () => { this.skinButton.onclick = () => {
this.showSkinsDialog(); this.showSkinsDialog();
} }
@ -404,8 +419,6 @@
this.drawFrame(false); this.drawFrame(false);
} }
if (!config.showControls) findWithClass(dom, "spine-player-controls ")[0].classList.add("spine-player-hidden");
return dom; return dom;
} }
@ -424,7 +437,7 @@
</div> </div>
`); `);
let sliderParent = findWithClass(popup.dom, "spine-player-speed-slider")[0]; let sliderParent = findWithClass(popup.dom, "spine-player-speed-slider")[0];
let slider = new Slider(); let slider = new Slider(2);
sliderParent.appendChild(slider.render()); sliderParent.appendChild(slider.render());
slider.setValue(this.speed / 2); slider.setValue(this.speed / 2);
slider.change = (percentage) => { slider.change = (percentage) => {
@ -532,14 +545,14 @@
rows.appendChild(row); rows.appendChild(row);
}; };
makeItem("Show bones", "bones"); makeItem("Bones", "bones");
makeItem("Show regions", "regions"); makeItem("Regions", "regions");
makeItem("Show meshes", "meshes"); makeItem("Meshes", "meshes");
makeItem("Show bounds", "bounds"); makeItem("Bounds", "bounds");
makeItem("Show paths", "paths"); makeItem("Paths", "paths");
makeItem("Show clipping", "clipping"); makeItem("Clipping", "clipping");
makeItem("Show points", "points"); makeItem("Points", "points");
makeItem("Show hulls", "hulls"); makeItem("Hulls", "hulls");
popup.show(); popup.show();
} }
@ -580,9 +593,10 @@
this.animationState.update(delta); this.animationState.update(delta);
this.animationState.apply(this.skeleton); this.animationState.apply(this.skeleton);
this.skeleton.updateWorldTransform();
} }
this.skeleton.updateWorldTransform();
let viewportSize = this.scale(this.config.viewport.width, this.config.viewport.height, this.canvas.width, this.canvas.height); let 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.zoom = this.config.viewport.width / viewportSize.x;
@ -720,12 +734,6 @@
} }
// Setup the animations after viewport, so default bounds don't get messed up. // Setup the animations after viewport, so default bounds don't get messed up.
if (!this.config.animation) {
if (skeletonData.animations.length > 0) {
this.config.animation = skeletonData.animations[0].name;
}
}
if (this.config.animations && this.config.animations.length > 0) { if (this.config.animations && this.config.animations.length > 0) {
this.config.animations.forEach(animation => { this.config.animations.forEach(animation => {
if (!this.skeleton.data.findAnimation(animation)) { if (!this.skeleton.data.findAnimation(animation)) {
@ -733,6 +741,16 @@
return; return;
} }
}); });
if (!this.config.animation) {
this.config.animation = this.config.animations[0];
}
}
if (!this.config.animation) {
if (skeletonData.animations.length > 0) {
this.config.animation = skeletonData.animations[0].name;
}
} }
if(this.config.animation) { if(this.config.animation) {
@ -755,6 +773,10 @@
// Setup the input processor and controllable bones // Setup the input processor and controllable bones
this.setupInput(); this.setupInput();
// Hide skin and animation if there's only the default skin / no animation
if (skeletonData.skins.length == 1) this.skinButton.classList.add("spine-player-hidden");
if (skeletonData.animations.length == 1) this.animationButton.classList.add("spine-player-hidden");
this.config.success(this); this.config.success(this);
this.loaded = true; this.loaded = true;
} }
@ -780,9 +802,11 @@
target = bone; target = bone;
} }
} }
handleHover();
}, },
up: (x, y) => { up: (x, y) => {
target = null; target = null;
handleHover();
}, },
dragged: (x, y) => { dragged: (x, y) => {
if (target != null) { if (target != null) {
@ -796,6 +820,7 @@
target.y = coords.y - skeleton.y; target.y = coords.y - skeleton.y;
} }
} }
handleHover();
}, },
moved: (x, y) => { moved: (x, y) => {
for (var i = 0; i < controlBones.length; i++) { for (var i = 0; i < controlBones.length; i++) {
@ -808,8 +833,42 @@
selectedBones[i] = null; selectedBones[i] = null;
} }
} }
handleHover();
} }
}); });
// For the manual hover to work, we need to disable
// hidding the controls if the mouse/touch entered
// the clickable area of a child of the controls
let mouseOverChildren = false;
canvas.onmouseover = (ev) => {
mouseOverChildren = false;
}
canvas.onmouseout = (ev) => {
if (ev.relatedTarget == null) {
mouseOverChildren = false;
} else {
mouseOverChildren = isContained(this.dom, (ev.relatedTarget as any));
}
}
let cancelId = 0;
let handleHover = () => {
if (!this.config.showControls) return;
clearTimeout(cancelId);
this.playerControls.classList.remove("hidden");
this.playerControls.classList.add("visible");
let remove = () => {
let popup = findWithClass(this.dom, "spine-player-popup");
if (popup.length == 0 && !mouseOverChildren) {
this.playerControls.classList.remove("visible");
this.playerControls.classList.add("hidden");
} else {
cancelId = setTimeout(remove, 1000);
}
};
cancelId = setTimeout(remove, 1000);
}
} }
private play () { private play () {