mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 09:16:01 +08:00
[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:
parent
8b49ad91cb
commit
94eec340c4
@ -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);
|
||||||
|
|||||||
3398
spine-ts/build/spine-widget.d.ts
vendored
3398
spine-ts/build/spine-widget.d.ts
vendored
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
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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 () {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user