bible-strong-avatar-lab/src/features/animation/playback.ts
smontlouis 3b2e134169 feat(studio): prepare app for GitHub release
Add distribution metadata, Strobi app icons, CI, and comprehensive documentation. Migrate the project to pnpm and reorganize the Studio into domain-focused modules and components.
2026-08-13 18:56:47 +02:00

69 lines
2.0 KiB
TypeScript

import { advanceSequenceCursor, type AvatarSequence } from './sequences'
export type PlaybackTimeline = {
position: number
direction: 1 | -1
stepDueAt: number | null
stepRemainingMs: number
blinkDueAt: number | null
blinkRemainingMs: number
}
export const createPlaybackTimeline = (): PlaybackTimeline => ({
position: 0,
direction: 1,
stepDueAt: null,
stepRemainingMs: 0,
blinkDueAt: null,
blinkRemainingMs: 0,
})
export const beginPlayback = (timeline: PlaybackTimeline, preservePosition = false) => {
return {
...timeline,
position: preservePosition ? timeline.position : 0,
direction: preservePosition ? timeline.direction : 1,
stepDueAt: null,
blinkDueAt: null,
} satisfies PlaybackTimeline
}
export const schedulePlaybackStep = (timeline: PlaybackTimeline, now: number, delayMs: number) => {
return { ...timeline, stepRemainingMs: delayMs, stepDueAt: now + delayMs }
}
export const schedulePlaybackBlink = (timeline: PlaybackTimeline, now: number, delayMs: number) => {
return { ...timeline, blinkRemainingMs: delayMs, blinkDueAt: now + delayMs }
}
export const pausePlaybackTimeline = (timeline: PlaybackTimeline, now: number) => {
return {
...timeline,
stepRemainingMs:
timeline.stepDueAt === null
? timeline.stepRemainingMs
: Math.max(timeline.stepDueAt - now, 0),
blinkRemainingMs:
timeline.blinkDueAt === null
? timeline.blinkRemainingMs
: Math.max(timeline.blinkDueAt - now, 0),
stepDueAt: null,
blinkDueAt: null,
}
}
export const advancePlaybackTimeline = (timeline: PlaybackTimeline, sequence: AvatarSequence) => {
const cursor = advanceSequenceCursor(sequence, timeline.position, timeline.direction)
return {
cursor,
timeline: {
...timeline,
position: cursor.index,
direction: cursor.direction,
stepDueAt: null,
},
}
}
export const stopPlaybackTimeline = (_timeline: PlaybackTimeline) => createPlaybackTimeline()