Phase 6 WIP working

This commit is contained in:
Dejvino 2026-08-05 12:20:55 +02:00
parent 968101c532
commit d0f93e5c55
3 changed files with 37 additions and 9 deletions

View File

@ -8,7 +8,7 @@
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"lint:scenes": "node tools/lint-scenes.js", "lint:scenes": "node tools/lint-scenes.js",
"test": "node --test test/" "test": "node --test test/*.test.js"
}, },
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {

View File

@ -95,18 +95,26 @@ document.addEventListener('drop', (e) => {
// ---------------------------------------------------------------- transport // ---------------------------------------------------------------- transport
function seekTo(frame) { function seekTo(frame, options = {}) {
if (!state.show.ready) return; if (!state.show.ready) return;
const clamped = Math.max(0, Math.min(state.show.frameCount - 1, Math.round(frame))); const clamped = Math.max(0, Math.min(state.show.frameCount - 1, Math.round(frame)));
dom.audio.currentTime = clamped / state.show.fps; dom.audio.currentTime = clamped / state.show.fps;
state.show.seek(clamped); state.show.seek(clamped, options);
strip.setFrame(clamped); strip.setFrame(clamped);
} }
function togglePlay() { function togglePlay() {
if (!state.show.ready) return; if (!state.show.ready) return;
state.playing = !state.playing; state.playing = !state.playing;
if (state.playing) dom.audio.play(); else dom.audio.pause(); if (state.playing) {
dom.audio.play().catch((err) => {
console.warn('[flow-state] playback failed:', err);
state.playing = false;
dom.play.textContent = '▶';
});
} else {
dom.audio.pause();
}
dom.play.textContent = state.playing ? '❚❚' : '▶'; dom.play.textContent = state.playing ? '❚❚' : '▶';
} }
@ -376,6 +384,7 @@ function resize() {
window.addEventListener('resize', resize); window.addEventListener('resize', resize);
let lastPanelSection = -1; let lastPanelSection = -1;
let lastRenderedFrame = -1;
function frame(now) { function frame(now) {
requestAnimationFrame(frame); requestAnimationFrame(frame);
@ -393,7 +402,10 @@ function frame(now) {
if (dom.audio.ended) { state.playing = false; dom.play.textContent = '▶'; } if (dom.audio.ended) { state.playing = false; dom.play.textContent = '▶'; }
} }
show.present(show.renderFrame(show.timeline.frame)); if (state.playing || show.timeline.frame !== lastRenderedFrame) {
show.present(show.renderFrame(show.timeline.frame));
lastRenderedFrame = show.timeline.frame;
}
strip.setFrame(show.timeline.frame); strip.setFrame(show.timeline.frame);
strip.draw(); strip.draw();

View File

@ -23,14 +23,30 @@ export class TimelineStrip {
this.frame = 0; this.frame = 0;
this.hoverFrame = -1; this.hoverFrame = -1;
this.loopSection = -1; this.loopSection = -1;
this.isDragging = false;
canvas.addEventListener('pointerdown', (e) => this._seekFromEvent(e)); canvas.addEventListener('pointerdown', (e) => {
this.isDragging = true;
this._seekFromEvent(e, { warmup: false });
});
canvas.addEventListener('pointermove', (e) => { canvas.addEventListener('pointermove', (e) => {
const rect = canvas.getBoundingClientRect(); const rect = canvas.getBoundingClientRect();
this.hoverFrame = this._frameAt(e.clientX - rect.left, rect.width); this.hoverFrame = this._frameAt(e.clientX - rect.left, rect.width);
if (e.buttons & 1) this._seekFromEvent(e); if (this.isDragging || (e.buttons & 1)) {
this.isDragging = true;
this._seekFromEvent(e, { warmup: false });
}
}); });
canvas.addEventListener('pointerleave', () => { this.hoverFrame = -1; }); canvas.addEventListener('pointerleave', () => { this.hoverFrame = -1; });
window.addEventListener('pointerup', () => {
if (this.isDragging) {
this.isDragging = false;
if (this.onSeek && this.show && this.show.ready) {
this.onSeek(this.frame, { warmup: true });
}
}
});
} }
setShow(show) { this.show = show; } setShow(show) { this.show = show; }
@ -42,10 +58,10 @@ export class TimelineStrip {
return Math.round((x / Math.max(1, width)) * (this.show.frameCount - 1)); return Math.round((x / Math.max(1, width)) * (this.show.frameCount - 1));
} }
_seekFromEvent(event) { _seekFromEvent(event, options = {}) {
if (!this.onSeek || !this.show || !this.show.ready) return; if (!this.onSeek || !this.show || !this.show.ready) return;
const rect = this.canvas.getBoundingClientRect(); const rect = this.canvas.getBoundingClientRect();
this.onSeek(this._frameAt(event.clientX - rect.left, rect.width)); this.onSeek(this._frameAt(event.clientX - rect.left, rect.width), options);
} }
resize() { resize() {