perf: stop toolbar/share-menu re-rendering every frame in playback

Both derefed the raw ::playhead in their render bodies, so the whole toolbar
(zoom/row sliders) and the share menu (even while closed) reconciled 60fps
during playback. toolbar now subscribes coarse ::at-start?/::at-end? edges
(flip only at the timeline ends) and reads the exact playhead at click time;
share-menu reads the playhead at click time too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-07-01 20:25:14 -04:00
parent 05514f3522
commit 0c5456d6df
2 changed files with 21 additions and 8 deletions

View file

@ -71,6 +71,13 @@
::playhead
(fn [db _] (scene/playhead (:view db) (peek (get-in db [:view :stack])))))
;; coarse boolean edges of the playhead — subscribing these (instead of the raw
;; playhead) keeps a component from re-rendering every frame during playback: they
;; only flip at the timeline ends. Read ::playhead itself at click time for the
;; actual value.
(rf/reg-sub ::at-start? :<- [::playhead] (fn [ph _] (<= ph 0)))
(rf/reg-sub ::at-end? :<- [::playhead] :<- [::length] (fn [[ph len] _] (>= ph len)))
;; breadcrumb trail of the stack
(rf/reg-sub
::breadcrumbs

View file

@ -1167,19 +1167,24 @@
:on-click #(rf/dispatch [::events/pop-to id])} name]]))])))
(defn toolbar []
;; NB: subscribe the coarse ::at-start?/::at-end? edges, not the raw playhead —
;; else the whole toolbar (sliders included) re-renders every frame in playback.
;; The step buttons read the exact playhead at click time.
(let [zoom @(rf/subscribe [::subs/zoom]) row-h @(rf/subscribe [::subs/row-h])
playing? @(rf/subscribe [::subs/playing?])
ph (js/Math.round @(rf/subscribe [::subs/playhead]))
start? @(rf/subscribe [::subs/at-start?])
end? @(rf/subscribe [::subs/at-end?])
len @(rf/subscribe [::subs/length])
authed? @(rf/subscribe [::subs/authed?])
authoring? (some? @(rf/subscribe [::subs/draft-group]))]
authoring? (some? @(rf/subscribe [::subs/draft-group]))
ph-now #(js/Math.round @(rf/subscribe [::subs/playhead]))]
[:div.toolbar
[:div.transport
[:button.step-btn {:title "Previous frame" :disabled (<= ph 0)
:on-click #(goto! (max 0 (dec ph)))} "◁"]
[:button.step-btn {:title "Previous frame" :disabled start?
:on-click #(goto! (max 0 (dec (ph-now))))} "◁"]
[:button.play-btn {:on-click toggle-play! :title "Play / pause"} (if playing? "❚❚" "▶")]
[:button.step-btn {:title "Next frame" :disabled (>= ph len)
:on-click #(goto! (min len (inc ph)))} "▷"]]
[:button.step-btn {:title "Next frame" :disabled end?
:on-click #(goto! (min len (inc (ph-now))))} "▷"]]
(when (and authed? (not authoring?))
[:button.add-annotation {:title "Add annotation"
:on-click #(rf/dispatch [::events/open-draft])}
@ -1540,7 +1545,6 @@
(fn []
(let [proj @(rf/subscribe [::subs/project])
stack @(rf/subscribe [::subs/stack])
playhead @(rf/subscribe [::subs/playhead])
id (:id proj)
grab (fn [rel which]
(copy-text! (abs-url rel))
@ -1552,7 +1556,9 @@
[:<>
[:div.menu-backdrop {:on-click #(reset! open? false)}]
[:div.menu-pop
[:button {:on-click #(grab (routes/project-url id stack playhead) :moment)}
;; read the playhead at click time, not in render, so this menu
;; doesn't re-render every frame during playback
[:button {:on-click #(grab (routes/project-url id stack @(rf/subscribe [::subs/playhead])) :moment)}
(if (= @copied :moment) "✓ Link copied" "Copy link to this moment")]
[:button {:on-click #(grab (routes/project-url id [:root] nil) :project)}
(if (= @copied :project) "✓ Link copied" "Copy link to the whole project")]]])]))))