feat: prev/next frame buttons flanking play
Transport row is now |◀ ▶/⏸ ▶|. step-frame! pauses, rounds the video clock to the nearest frame and seeks ±1; the rAF clock reports the new position back into :playhead, so the red line follows (same single seek-only path as jump/scrub). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ea78a9ee08
commit
ec236e93cb
2 changed files with 21 additions and 5 deletions
|
|
@ -199,11 +199,14 @@ body { overflow: hidden; }
|
||||||
flex-shrink: 0; }
|
flex-shrink: 0; }
|
||||||
.slider { display: flex; align-items: center; gap: 6px; font-size: 11px; color: #9ab; }
|
.slider { display: flex; align-items: center; gap: 6px; font-size: 11px; color: #9ab; }
|
||||||
.slider input { width: 150px; touch-action: none; }
|
.slider input { width: 150px; touch-action: none; }
|
||||||
.play-btn {
|
.transport { display: flex; align-items: center; gap: 4px; }
|
||||||
|
.play-btn, .step-btn {
|
||||||
background: #1c2a38; color: #cfe3f5; border: 1px solid #3a6ea5;
|
background: #1c2a38; color: #cfe3f5; border: 1px solid #3a6ea5;
|
||||||
border-radius: 4px; padding: 2px 12px; cursor: pointer; font-size: 14px; line-height: 1.4;
|
border-radius: 4px; cursor: pointer; line-height: 1.4;
|
||||||
}
|
}
|
||||||
.play-btn:hover { background: #24384e; }
|
.play-btn { padding: 2px 12px; font-size: 14px; }
|
||||||
|
.step-btn { padding: 2px 7px; font-size: 11px; letter-spacing: -1px; }
|
||||||
|
.play-btn:hover, .step-btn:hover { background: #24384e; }
|
||||||
|
|
||||||
.timeline-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0; }
|
.timeline-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0; }
|
||||||
.timeline { display: flex; align-items: flex-start; background: #111; }
|
.timeline { display: flex; align-items: flex-start; background: #111; }
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,15 @@
|
||||||
(when-let [v @video-el]
|
(when-let [v @video-el]
|
||||||
(if (.-paused v) (.play v) (.pause v)))) ; play/pause events drive :playing? + rAF
|
(if (.-paused v) (.play v) (.pause v)))) ; play/pause events drive :playing? + rAF
|
||||||
|
|
||||||
|
(defn- step-frame!
|
||||||
|
"Pause and nudge the playhead one frame in `dir` (-1 / +1). Seeking is the
|
||||||
|
only move — the rAF clock reports the new position back into :playhead."
|
||||||
|
[fps dir]
|
||||||
|
(when-let [v @video-el]
|
||||||
|
(when-not (.-paused v) (.pause v))
|
||||||
|
(let [frame (js/Math.round (* (.-currentTime v) fps))]
|
||||||
|
(set! (.-currentTime v) (secs (max 0 (+ frame dir)) fps)))))
|
||||||
|
|
||||||
(defn- editable-target?
|
(defn- editable-target?
|
||||||
"True for fields where Space should type a space, not toggle play."
|
"True for fields where Space should type a space, not toggle play."
|
||||||
[el]
|
[el]
|
||||||
|
|
@ -486,10 +495,14 @@
|
||||||
(defn zoom-controls []
|
(defn zoom-controls []
|
||||||
(let [zoom @(rf/subscribe [::subs/zoom])
|
(let [zoom @(rf/subscribe [::subs/zoom])
|
||||||
row-h @(rf/subscribe [::subs/row-h])
|
row-h @(rf/subscribe [::subs/row-h])
|
||||||
|
fps (or @(rf/subscribe [::subs/fps]) (/ 24000 1001))
|
||||||
playing? @(rf/subscribe [::subs/playing?])]
|
playing? @(rf/subscribe [::subs/playing?])]
|
||||||
[:div.toolbar
|
[:div.toolbar
|
||||||
|
[:div.transport
|
||||||
|
[:button.step-btn {:on-click #(step-frame! fps -1) :title "Previous frame"} "|◀"]
|
||||||
[:button.play-btn {:on-click toggle-play! :title "Play / pause (Space)"}
|
[:button.play-btn {:on-click toggle-play! :title "Play / pause (Space)"}
|
||||||
(if playing? "⏸" "▶")]
|
(if playing? "⏸" "▶")]
|
||||||
|
[:button.step-btn {:on-click #(step-frame! fps 1) :title "Next frame"} "▶|"]]
|
||||||
[slider "Zoom X" zoom 2 400 zoom-x!]
|
[slider "Zoom X" zoom 2 400 zoom-x!]
|
||||||
[slider "Zoom Y" row-h 8 120 #(rf/dispatch [::events/set-row-h %])]]))
|
[slider "Zoom Y" row-h 8 120 #(rf/dispatch [::events/set-row-h %])]]))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue