Created
February 8, 2025 14:20
-
-
Save mitian233/a3575e7d01b5d3295fa972c3a5bf2f0d to your computer and use it in GitHub Desktop.
use-spine.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { spine } from './spine-widget/index.js'; | |
const spineDir = '/spine/'; | |
const idleMaxTime = 60000; | |
const edge = 40; | |
interface model { | |
name: string; | |
skin: string; | |
animations: { | |
start: string; | |
idle: string[]; | |
}; | |
}; | |
interface config { | |
enable: boolean; | |
models: model[]; | |
styles: { | |
widget: { | |
x: string; | |
y: string; | |
} | |
}; | |
} | |
interface anmDefination { | |
name: string; | |
loop: boolean; | |
} | |
class SpineModel { | |
private config: config; | |
private model: model; | |
private urlPrefix: string; | |
private skin: string; | |
private json: string; | |
private atlas: string; | |
private widget: any; | |
private widgetElement: HTMLElement | null; | |
private triggerEvents: string[]; | |
private lastInteractTime: number; | |
private localX: number; | |
private localY: number; | |
constructor(widgetDiv = "#spine-widget", config: config) { | |
this.config = config; | |
this.model = config.models[Math.floor(Math.random() * config.models.length)]; | |
this.urlPrefix = spineDir + this.model.name; | |
this.skin = this.model.skin; | |
this.json = this.urlPrefix + '/data.json'; | |
this.atlas = this.urlPrefix + '/data.atlas'; | |
this.widget = null; | |
this.widgetElement = document.querySelector(widgetDiv); | |
this.triggerEvents = ['mousedown', 'touchstart', 'scroll']; | |
this.lastInteractTime = Date.now(); | |
this.localX = 0; | |
this.localY = 0; | |
this.load(); | |
} | |
load() { | |
this.config.styles.widget.x && this.widgetElement?.style.setProperty('x', this.config.styles.widget.x); | |
this.config.styles.widget.y && this.widgetElement?.style.setProperty('y', this.config.styles.widget.y); | |
new spine.SpineWidget(this.widgetElement!, { | |
animation: this.model.animations.start, | |
atlas: this.atlas, | |
json: this.json, | |
skin: this.skin, | |
backgroundColor: '#00000000', | |
alpha: true, | |
loop: false, | |
fitToCanvas: true, | |
success: this.successCallback.bind(this), | |
}) | |
} | |
successCallback(w: any) { | |
this.widget = w; | |
this.initWidgetActions(); | |
this.initDragging(); | |
this.triggerEvents.forEach((i) => | |
window.addEventListener(i, this.changeIdleAnimation.bind(this)) | |
); | |
} | |
initWidgetActions() { | |
this.widget.canvas.onclick = this.interact.bind(this); | |
this.widget.state.addListener({ | |
complete: (t: { loop: boolean; animation: { name: string } }) => { | |
t.loop || this.isIdle() | |
? this.playRandAnimation({ | |
name: t.animation.name, | |
loop: true, | |
}) | |
: this.playRandAnimation(this.getAnimationList('idle')); | |
}, | |
}); | |
} | |
isIdle() { | |
let currentAnm = this.widget.state.tracks[0].animation; | |
for (const i of this.getAnimationList('idle')) { | |
if (currentAnm.name === i.name) { | |
return !0; | |
} | |
} | |
return !1; | |
} | |
playRandAnimation(anm: anmDefination | anmDefination[]) { | |
if (Array.isArray(anm)) { | |
let a = anm[Math.floor(Math.random() * anm.length)]; | |
this.widget.state.setAnimation(0, a.name, a.loop); | |
} else { | |
this.widget.state.setAnimation(0, anm.name, anm.loop); | |
} | |
} | |
getAnimationList(name: string) { | |
if (name === 'start') { | |
return [{ | |
name: this.model.animations.start, | |
loop: !1, | |
}] | |
} else { | |
return this.model.animations.idle.map((n: string) => ({ | |
name: n, | |
loop: !1, | |
})); | |
} | |
} | |
interact() { | |
!this.isIdle() | |
? console.warn('too much manipulations') | |
: ((this.lastInteractTime = Date.now()), | |
this.playRandAnimation( | |
this.widget.skeleton.data.animations.map((n: anmDefination) => ({ | |
name: n.name, | |
loop: !1, | |
})) | |
)); | |
} | |
changeIdleAnimation() { | |
let time = Date.now(), | |
interval = time - this.lastInteractTime; | |
if (this.isIdle() && interval >= idleMaxTime) { | |
(this.lastInteractTime = time), this.playRandAnimation(this.getAnimationList('idle')); | |
} | |
} | |
initDragging() { | |
function i(t: TouchEvent | MouseEvent): { x: number; y: number } { | |
let e = document.documentElement.scrollLeft, | |
i = document.documentElement.scrollTop; | |
if ('targetTouches' in t && t.targetTouches.length > 0) { | |
e += t.targetTouches[0].clientX; | |
i += t.targetTouches[0].clientY; | |
} else if ('clientX' in t && 'clientY' in t) { | |
e += t.clientX; | |
i += t.clientY; | |
} | |
return { | |
x: e, | |
y: i, | |
}; | |
} | |
function e(t: MouseEvent | TouchEvent): void { | |
t.cancelable && t.preventDefault(); | |
} | |
const n = (t: number, e: number): void => { | |
(t = Math.max(0 - edge, t)), | |
(e = Math.max(0, e)), | |
(t = Math.min(document.body.clientWidth + edge - this.widgetElement!.clientWidth, t)), | |
(e = Math.min(document.body.clientHeight - this.widgetElement!.clientHeight, e)), | |
(this.widgetElement!.style.left = t + 'px'), | |
(this.widgetElement!.style.top = e + 'px'); | |
}, | |
o = (t: TouchEvent | MouseEvent): void => { | |
let { x: e, y: top } = i(t); | |
(this.localX = e - this.widgetElement!.offsetLeft), | |
(this.localY = top - this.widgetElement!.offsetTop); | |
}, | |
s = (t: MouseEvent | TouchEvent): void => { | |
let { x: e, y: top } = i(t); | |
n(e - this.localX, top - this.localY), | |
window?.getSelection && window.getSelection()!.removeAllRanges();// : document.getSelection.empty() | |
}, | |
t = { | |
passive: true, | |
}, | |
a = { | |
passive: false, | |
}; | |
this.widgetElement!.addEventListener('mousedown', (t: MouseEvent) => { | |
o(t), document.addEventListener('mousemove', s); | |
}), | |
this.widgetElement!.addEventListener( | |
'touchstart', | |
(t: TouchEvent) => { | |
o(t), document.addEventListener('touchmove', e, a); | |
}, | |
t | |
), | |
this.widgetElement!.addEventListener('touchmove', s, t), | |
document.addEventListener('mouseup', () => document.removeEventListener('mousemove', s)), | |
this.widgetElement!.addEventListener('touchend', () => | |
document.removeEventListener('touchmove', e) | |
), | |
window.addEventListener('resize', () => { | |
let t = this.widgetElement!.style; | |
let e, i; | |
t.left && | |
t.top && | |
((e = Number.parseInt(t.left.substring(0, t.left.length - 2))), | |
(i = Number.parseInt(t.top.substring(0, t.top.length - 2))), | |
n(e, i)); | |
}); | |
} | |
} | |
export { SpineModel }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment