hlassiege/darylpublic Fork
⑂ fdce48a
Commits
⬇ Clone ▾
git clone https://git.rickub.com/hlassiege/daryl.git
git clone ssh://git@rickub.com/hlassiege/daryl.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

feat(web): snap the playhead to cuts while scrubbing the ruler

It snaps to clip and item edges and markers; Alt scrubs freely.
hlassiege committed 2026-10-12T09:22:49+02:00 Browse files
fdce48a parent: fe37089
modified web/src/project/steps/montage/Timeline.vue +8 -5
@@ -328,7 +328,9 @@ function applyGhost(d: NonNullable<typeof ghostDrag>, t: number) {
328328
329329 let drag: { track: Track; id: string; mode: Mode; t0: number; origin: Timeline; pts: number[]; moved: boolean; fx?: FxEnd } | null = null;
330330 let rangeDrag: { a: number } | null = null;
331-let scrub = false;
331+// la tête de lecture tirée sur la règle s'aimante aux coupes et aux bords des éléments (Alt : sans aimant)
332+let scrub: number[] | null = null;
333+const scrubTo = (e: PointerEvent) => emit("seek", e.altKey ? timeAt(e) : snap(timeAt(e), scrub!));
332334
333335 function down(e: PointerEvent, track: Track, id: string, mode: Mode = "move") {
334336 if (e.button !== 0) return;
@@ -465,7 +467,7 @@ function dragTo(e: PointerEvent) {
465467 const a = Math.min(rangeDrag.a, t), b = Math.max(rangeDrag.a, t);
466468 sel.value = b - a > 0.05 ? { range: [round(a), round(b)] } : null;
467469 } else if (scrub) {
468- emit("seek", timeAt(e));
470+ scrubTo(e);
469471 }
470472 }
471473
@@ -479,7 +481,7 @@ function up() {
479481 }
480482 drag = null;
481483 rangeDrag = null;
482- scrub = false;
484+ scrub = null;
483485 lastMove = null;
484486 cancelAnimationFrame(edgeRaf);
485487 edgeRaf = 0;
@@ -505,8 +507,9 @@ function downRuler(e: PointerEvent) {
505507 if (e.shiftKey) {
506508 rangeDrag = { a: timeAt(e) };
507509 } else {
508- scrub = true;
509- emit("seek", timeAt(e));
510+ // sans la tête elle-même, qui sinon resterait collée à sa position de départ
511+ scrub = snapPoints("").filter((p) => p !== props.t);
512+ scrubTo(e);
510513 }
511514 (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
512515 }
@@ -328,7 +328,9 @@ function applyGhost(d: NonNullable<typeof ghostDrag>, t: number) {
328 328
329 let drag: { track: Track; id: string; mode: Mode; t0: number; origin: Timeline; pts: number[]; moved: boolean; fx?: FxEnd } | null = null;329 let drag: { track: Track; id: string; mode: Mode; t0: number; origin: Timeline; pts: number[]; moved: boolean; fx?: FxEnd } | null = null;
330 let rangeDrag: { a: number } | null = null;330 let rangeDrag: { a: number } | null = null;
331-let scrub = false;331+// la tête de lecture tirée sur la règle s'aimante aux coupes et aux bords des éléments (Alt : sans aimant)
332+let scrub: number[] | null = null;
333+const scrubTo = (e: PointerEvent) => emit("seek", e.altKey ? timeAt(e) : snap(timeAt(e), scrub!));
332 334
333 function down(e: PointerEvent, track: Track, id: string, mode: Mode = "move") {335 function down(e: PointerEvent, track: Track, id: string, mode: Mode = "move") {
334 if (e.button !== 0) return;336 if (e.button !== 0) return;
@@ -465,7 +467,7 @@ function dragTo(e: PointerEvent) {
465 const a = Math.min(rangeDrag.a, t), b = Math.max(rangeDrag.a, t);467 const a = Math.min(rangeDrag.a, t), b = Math.max(rangeDrag.a, t);
466 sel.value = b - a > 0.05 ? { range: [round(a), round(b)] } : null;468 sel.value = b - a > 0.05 ? { range: [round(a), round(b)] } : null;
467 } else if (scrub) {469 } else if (scrub) {
468- emit("seek", timeAt(e));470+ scrubTo(e);
469 }471 }
470 }472 }
471 473
@@ -479,7 +481,7 @@ function up() {
479 }481 }
480 drag = null;482 drag = null;
481 rangeDrag = null;483 rangeDrag = null;
482- scrub = false;484+ scrub = null;
483 lastMove = null;485 lastMove = null;
484 cancelAnimationFrame(edgeRaf);486 cancelAnimationFrame(edgeRaf);
485 edgeRaf = 0;487 edgeRaf = 0;
@@ -505,8 +507,9 @@ function downRuler(e: PointerEvent) {
505 if (e.shiftKey) {507 if (e.shiftKey) {
506 rangeDrag = { a: timeAt(e) };508 rangeDrag = { a: timeAt(e) };
507 } else {509 } else {
508- scrub = true;510+ // sans la tête elle-même, qui sinon resterait collée à sa position de départ
509- emit("seek", timeAt(e));511+ scrub = snapPoints("").filter((p) => p !== props.t);
512+ scrubTo(e);
510 }513 }
511 (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);514 (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
512 }515 }