This commit is contained in:
Andrés Villagrán 2026-05-19 10:40:55 +07:00 committed by GitHub
commit f60315ee63
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 4 deletions

View File

@ -224,12 +224,26 @@ export class PlaybackManager {
const maxTime = this.editor.timeline.getTotalDuration();
if (newTime >= maxTime) {
const shouldLoop =
this.editor.project.getActive()?.settings.loop === true &&
maxTime > ZERO_MEDIA_TIME;
if (shouldLoop) {
this.playbackStartWallTime = performance.now();
this.playbackStartTime = ZERO_MEDIA_TIME;
this.currentTime = ZERO_MEDIA_TIME;
this.notifySeek(ZERO_MEDIA_TIME);
this.dispatchSeekEvent(ZERO_MEDIA_TIME);
this.playbackTimer = requestAnimationFrame(this.updateTime);
return;
}
this.pause();
this.currentTime = maxTime;
this.notify();
this.notifySeek(maxTime);
this.dispatchSeekEvent(maxTime);
return;
this.notifySeek(maxTime);
this.dispatchSeekEvent(maxTime);
return;
}
this.currentTime = newTime;

View File

@ -11,7 +11,10 @@ import {
FullScreenIcon,
PauseIcon,
PlayIcon,
RepeatIcon,
RepeatOffIcon,
} from "@hugeicons/core-free-icons";
import { UpdateProjectSettingsCommand } from "@/commands/project";
import { HugeiconsIcon } from "@hugeicons/react";
import { Separator } from "@/components/ui/separator";
import {
@ -40,7 +43,10 @@ export function PreviewToolbar({
return (
<div className="grid grid-cols-[1fr_auto_1fr] items-center pb-3 pt-5 px-5">
<TimecodeDisplay />
<PlayPauseButton />
<div className="justify-self-center flex items-center gap-1">
<PlayPauseButton />
<LoopToggleButton />
</div>
<div className="justify-self-end flex items-center gap-2.5">
<ZoomSelect />
<Separator orientation="vertical" className="h-4" />
@ -160,3 +166,23 @@ function PlayPauseButton() {
</Tooltip>
);
}
function LoopToggleButton() {
const loop = useEditor((e) => e.project.getActive()?.settings.loop === true);
return (
<Button
type="button"
variant={loop ? "secondary" : "text"}
size="icon"
aria-pressed={loop}
aria-label={loop ? "Disable loop playback" : "Enable loop playback"}
title={loop ? "Loop is on" : "Loop is off"}
onClick={() => {
new UpdateProjectSettingsCommand({ loop: !loop }).execute();
}}
>
<HugeiconsIcon icon={loop ? RepeatIcon : RepeatOffIcon} />
</Button>
);
}

View File

@ -33,6 +33,11 @@ export interface TProjectSettings {
lastCustomCanvasSize?: TCanvasSize | null;
originalCanvasSize?: TCanvasSize | null;
background: TBackground;
/**
* When true, preview playback wraps back to the start of the timeline
* instead of pausing once the playhead reaches the end. Defaults to false.
*/
loop?: boolean;
}
export interface TTimelineViewState {