ninelands-reddit/scripts/capture/stage.ts
Lukas Müllner d3ab781395 feat: Trailer, Devpost-Galerie und Vimeo-Cover per npm run capture:trailer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 19:57:04 +01:00

90 lines
2.4 KiB
TypeScript

// Bühne für Trailer und Galerie (scripts/capture/trailer.mjs steuert sie über window.stage).
import './stage.css'
const $ = <T extends HTMLElement>(id: string) =>
document.getElementById(id) as T
const text = $('text')
const title = $('title')
const sub = $('sub')
const foot = $('foot')
const finger = $('finger')
const splash = $<HTMLIFrameElement>('splash')
const game = $<HTMLIFrameElement>('game')
const clip = $<HTMLVideoElement>('clip')
const post = $('post')
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms))
/** Handy so groß wie möglich (88 % der Höhe) */
function fit(): void {
document.documentElement.style.setProperty(
'--s',
String((innerHeight * 0.88) / 840),
)
}
fit()
addEventListener('resize', fit)
function load(frame: HTMLIFrameElement, src: string): Promise<void> {
return new Promise(resolve => {
frame.addEventListener('load', () => resolve(), {once: true})
frame.src = src
})
}
declare global {
interface Window {
stage: typeof stage
}
}
const stage = {
async caption(
t: string,
s = '',
f = '',
opts: {logo?: boolean; instant?: boolean} = {},
) {
if (!opts.instant && title.textContent) {
text.classList.add('fade')
await sleep(250)
}
title.textContent = t
title.classList.toggle('logo', Boolean(opts.logo))
sub.textContent = s
foot.textContent = f
text.classList.remove('fade')
},
/** Was das Handy zeigt: Post im Feed, Spiel (Vollbild) oder eine eigene Aufnahme */
async show(mode: 'post' | 'game' | 'clip', src?: string) {
if (mode === 'post' && src) await load(splash, src)
if (mode === 'game' && src) await load(game, src)
if (mode === 'clip' && src) {
clip.src = src
await clip.play()
}
for (const [el, m] of [
[post, 'post'],
[game, 'game'],
[clip, 'clip'],
] as const)
el.classList.toggle('on', m === mode)
document.body.classList.toggle('post-mode', mode === 'post')
// Schriften im Spiel abwarten
await (mode === 'post' ? splash : game).contentDocument?.fonts.ready
},
clipDuration: () => clip.duration,
finger(x: number | null, y = 0) {
finger.classList.toggle('on', x !== null)
if (x !== null) {
finger.style.left = `${x}px`
finger.style.top = `${y}px`
}
},
press() {
finger.classList.remove('press')
void finger.offsetWidth
finger.classList.add('press')
},
}
window.stage = stage