Ping-pong turns on the last key, and stepped rows hold the key they last passed (#112)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-17 07:08:44 +02:00
1 parent f68941fa3a
commit cc14988338
7 files changed
+72 -22

No files matched your search

+4 -1
View File
@@ -33,7 +33,10 @@ export class AnimationEditor extends AssetTabEditor<AnimationAsset, TimelineCanv
this.playButton = new Button('Play', { width: 64, onClick: () => this.togglePlay() });
this.centreButton = new Button('Centre', { onClick: () => this.preview.fit() });
const timeline = new TimelineCanvas(state);
this.preview = new PreviewCanvas(state, () => timeline.playhead);
this.preview = new PreviewCanvas(state, () => ({
frame: timeline.playhead,
reverse: timeline.playheadReverse,
}));
timeline.onPlayhead = () => this.preview.redraw();
this.mount(timeline, [this.rootPicker, this.addRowButton, this.playButton, this.centreButton]);
}
+4 -3
View File
@@ -31,8 +31,8 @@ export class PreviewCanvas extends WorldViewport {
constructor(
state: EditorState,
/** Where the timeline's playhead stands, in frames. */
private readonly playhead: () => number,
/** Where the timeline's playhead stands, in frames, and whether it runs ping-pong's way back. */
private readonly playhead: () => { frame: number; reverse: boolean },
) {
super(state);
this.on('pointerdown', (e: FederatedPointerEvent) => this.onPointerDown(e));
@@ -95,7 +95,8 @@ export class PreviewCanvas extends WorldViewport {
protected override collectFrame(): Frame {
const asset = this.assetId ? findAssetOf(this.state.project, this.assetId, 'animation') : undefined;
const holder = asset && previewHolder(this.state.project, asset, this.playhead());
const head = this.playhead();
const holder = asset && previewHolder(this.state.project, asset, head.frame, head.reverse);
return collectHolderItems(this.state.project, holder, Theme.colors.viewport);
}
+5
View File
@@ -79,6 +79,8 @@ export class TimelineCanvas extends Widget {
private assetId = '';
/** Where the playhead stands, in frames – fractional while playing. */
playhead = 0;
/** Whether the playhead runs ping-pong's way back – a jumping value then holds the key above. */
playheadReverse = false;
/** Seconds since play began; the playhead is a function of it (see `animationPlayheadAt`). */
private time = 0;
private playingNow = false;
@@ -160,6 +162,7 @@ export class TimelineCanvas extends Widget {
stop(): void {
this.playingNow = false;
this.playhead = 0;
this.playheadReverse = false;
this.time = 0;
this.onPlayhead();
}
@@ -171,6 +174,7 @@ export class TimelineCanvas extends Widget {
this.time += dt;
const at = animationPlayheadAt(asset, this.time);
this.playhead = at.frame;
this.playheadReverse = at.reverse;
if (at.finished) this.playingNow = false;
this.redraw();
this.onPlayhead();
@@ -179,6 +183,7 @@ export class TimelineCanvas extends Widget {
/** Puts the playhead on a frame – what the ruler does, and what a check does without a pointer. */
scrub(frame: number): void {
this.playhead = Math.max(0, frame);
this.playheadReverse = false;
const asset = this.asset();
if (asset) this.time = this.timeOf(asset, this.playhead);
this.redraw();