fuzzyworld

explodorama / files / src / ui / timeline.ts

timeline.ts

Blame: the commit that last changed each line · file · history

b8849d36 2026-11-01 Pavel Safronov1/**
2 * The bar under a scene that shows how much of it is left, with a mark where the moment it is
3 * about comes (user request, 2026-10-31): in the demo's panel and under each dialog's clip. It
4 * fills a turn at a time; the mark lights once the moment has passed, and is left out when the moment is the last turn.
5 */
6export interface Timeline {
7 /**
8 * `done` of `total` turns played; the scene's moment is turn `moment` (0-based; the last if undefined).
9 * Hidden with no turns. The fill moves to its new width over `seconds` (how long the turn about to
10 * play takes), so it keeps moving rather than stepping a turn at a time.
11 */
12 set(done: number, total: number, moment?: number, seconds?: number): void
13}
14
15export function bindTimeline(el: HTMLElement): Timeline {
16 el.classList.add('timeline')
17 const fill = document.createElement('i')
18 const mark = document.createElement('b')
19 el.replaceChildren(fill, mark)
20 return {
21 set(done, total, moment, seconds = 0.5) {
22 el.hidden = total <= 0
23 if (total <= 0) return
24 const width = `${(Math.min(done, total) / total) * 100}%`
25 if (done === 0) {
26 // A new loop: jump back to empty at once, rather than draining from full.
27 fill.style.transition = 'none'
28 fill.style.width = width
29 void fill.offsetWidth
30 fill.style.transition = ''
31 } else {
32 fill.style.transitionDuration = `${seconds}s`
33 fill.style.width = width
34 }
35 // The mark says where the moment is; at the very end it says nothing, so it isn't shown.
36 const at = moment ?? total - 1
37 mark.hidden = at >= total - 1
38 mark.style.left = `${((at + 1) / total) * 100}%`
39 // Lit once the turn after the moment has begun, i.e. the moment has shown.
40 mark.classList.toggle('passed', done > at + 1)
41 },
42 }
43}
44
45/** A timeline in a new element placed after `anchor`. */
46export function timelineAfter(anchor: Element): Timeline {
47 const el = document.createElement('div')
48 anchor.after(el)
49 return bindTimeline(el)
50}