[ts][player] Add interactive option to allow click/touch interaction. Closes #2852.

This commit is contained in:
Davide Tantillo 2025-05-16 12:23:40 +02:00
parent e24b1a25d8
commit fd20a0c009

View File

@ -148,6 +148,9 @@ export interface SpinePlayerConfig {
filter settings from the texture atlas are used. Default: true */ filter settings from the texture atlas are used. Default: true */
mipmaps?: boolean mipmaps?: boolean
/* Optional: Whether the player responds to user click/touch (play/pause, or control bones). Default: true */
interactive?: boolean
/* Optional: List of bone names that the user can drag to position. Default: none */ /* Optional: List of bone names that the user can drag to position. Default: none */
controlBones?: string[] controlBones?: string[]
@ -306,6 +309,7 @@ export class SpinePlayer implements Disposable {
if (config.premultipliedAlpha === void 0) config.premultipliedAlpha = true; if (config.premultipliedAlpha === void 0) config.premultipliedAlpha = true;
if (config.preserveDrawingBuffer === void 0) config.preserveDrawingBuffer = false; if (config.preserveDrawingBuffer === void 0) config.preserveDrawingBuffer = false;
if (config.mipmaps === void 0) config.mipmaps = true; if (config.mipmaps === void 0) config.mipmaps = true;
if (config.interactive === void 0) config.interactive = true;
if (!config.debug) config.debug = { if (!config.debug) config.debug = {
bones: false, bones: false,
clipping: false, clipping: false,
@ -586,57 +590,60 @@ export class SpinePlayer implements Disposable {
let skeleton = this.skeleton!; let skeleton = this.skeleton!;
let renderer = this.sceneRenderer!; let renderer = this.sceneRenderer!;
let closest = function (x: number, y: number): Bone | null { if (config.interactive) {
mouse.set(x, canvas.clientHeight - y, 0) let closest = function (x: number, y: number): Bone | null {
offset.x = offset.y = 0; mouse.set(x, canvas.clientHeight - y, 0)
let bestDistance = 24, index = 0; offset.x = offset.y = 0;
let best: Bone | null = null; let bestDistance = 24, index = 0;
for (let i = 0; i < controlBones.length; i++) { let best: Bone | null = null;
selectedBones[i] = null; for (let i = 0; i < controlBones.length; i++) {
let bone = skeleton.findBone(controlBones[i]); selectedBones[i] = null;
if (!bone) continue; let bone = skeleton.findBone(controlBones[i]);
let distance = renderer.camera.worldToScreen( if (!bone) continue;
coords.set(bone.worldX, bone.worldY, 0), let distance = renderer.camera.worldToScreen(
canvas.clientWidth, canvas.clientHeight).distance(mouse); coords.set(bone.worldX, bone.worldY, 0),
if (distance < bestDistance) { canvas.clientWidth, canvas.clientHeight).distance(mouse);
bestDistance = distance; if (distance < bestDistance) {
best = bone; bestDistance = distance;
index = i; best = bone;
offset.x = coords.x - mouse.x; index = i;
offset.y = coords.y - mouse.y; offset.x = coords.x - mouse.x;
} offset.y = coords.y - mouse.y;
}
if (best) selectedBones[index] = best;
return best;
};
new Input(canvas).addListener({
down: (x, y) => {
target = closest(x, y);
},
up: () => {
if (target)
target = null;
else if (config.showControls)
(this.paused ? this.play() : this.pause());
},
dragged: (x, y) => {
if (target) {
x = MathUtils.clamp(x + offset.x, 0, canvas.clientWidth)
y = MathUtils.clamp(y - offset.y, 0, canvas.clientHeight);
renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.clientWidth, canvas.clientHeight);
if (target.parent) {
target.parent.worldToLocal(position.set(coords.x - skeleton.x, coords.y - skeleton.y));
target.x = position.x;
target.y = position.y;
} else {
target.x = coords.x - skeleton.x;
target.y = coords.y - skeleton.y;
} }
} }
}, if (best) selectedBones[index] = best;
moved: (x, y) => closest(x, y) return best;
}); };
new Input(canvas).addListener({
down: (x, y) => {
target = closest(x, y);
},
up: () => {
if (target)
target = null;
else if (config.showControls)
(this.paused ? this.play() : this.pause());
},
dragged: (x, y) => {
if (target) {
x = MathUtils.clamp(x + offset.x, 0, canvas.clientWidth)
y = MathUtils.clamp(y - offset.y, 0, canvas.clientHeight);
renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.clientWidth, canvas.clientHeight);
if (target.parent) {
target.parent.worldToLocal(position.set(coords.x - skeleton.x, coords.y - skeleton.y));
target.x = position.x;
target.y = position.y;
} else {
target.x = coords.x - skeleton.x;
target.y = coords.y - skeleton.y;
}
}
},
moved: (x, y) => closest(x, y)
});
}
if (config.showControls) { if (config.showControls) {
// For manual hover to work, we need to disable hidding controls if the mouse/touch entered the clickable area of a child of the controls. // For manual hover to work, we need to disable hidding controls if the mouse/touch entered the clickable area of a child of the controls.