feat: add time ruler with tick marks above timeline
Display adaptive tick marks and time labels above the timeline to indicate intervals. Tick spacing automatically adjusts based on zoom level: - < 10 px/sec: 30s intervals - < 20 px/sec: 10s intervals - < 40 px/sec: 5s intervals - < 80 px/sec: 1s intervals - >= 80 px/sec: 0.5s intervals Labels show minutes (m), seconds (s), or deciseconds (·) depending on interval. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
52c700b843
commit
d93b92f4a5
2 changed files with 41 additions and 0 deletions
|
|
@ -320,6 +320,10 @@ body { overflow: hidden; background: var(--desktop); background-size: 2px 2px;
|
|||
html.dark .timeline-head {
|
||||
background: rgba(0, 0, 0, .58);
|
||||
}
|
||||
.time-ruler {
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
.timeline-body-scroll {
|
||||
flex: 1; min-height: 0; display: flex; align-items: flex-start;
|
||||
overflow-y: auto; overflow-x: hidden;
|
||||
|
|
|
|||
|
|
@ -303,6 +303,34 @@
|
|||
(rf/dispatch [::events/draft-click-seg (:mark seg) cf])))}
|
||||
(str (clip-label scene segs (:mark seg)) " " cf "f")])]))
|
||||
|
||||
;; --- timeline ruler -------------------------------------------------------
|
||||
|
||||
(defn- time-ruler [len fps zoom]
|
||||
"Generate tick marks and labels for the timeline ruler.
|
||||
Returns a vector of {:frame :px :label} with major/minor ticks."
|
||||
(let [px-per-sec (* fps zoom)
|
||||
;; choose tick interval in frames based on zoom level
|
||||
tick-frames (cond
|
||||
(< px-per-sec 10) (* 30 fps) ; 30 sec intervals
|
||||
(< px-per-sec 20) (* 10 fps) ; 10 sec intervals
|
||||
(< px-per-sec 40) (* 5 fps) ; 5 sec intervals
|
||||
(< px-per-sec 80) fps ; 1 sec intervals
|
||||
:else (/ fps 2)) ; 0.5 sec intervals
|
||||
sec-per-tick (/ tick-frames fps)]
|
||||
(loop [frame 0 tick-num 0 ticks []]
|
||||
(if (>= frame len)
|
||||
ticks
|
||||
(let [px (* (/ frame fps) zoom)
|
||||
total-sec (* tick-num sec-per-tick)
|
||||
label (cond
|
||||
(>= sec-per-tick 10) (let [m (int (/ total-sec 60))
|
||||
s (int (mod total-sec 60))]
|
||||
(if (> s 0) (str m ":" (if (< s 10) "0" "") s) (str m "m")))
|
||||
(>= sec-per-tick 1) (str (int total-sec) "s")
|
||||
:else (str (js/Math.round (* total-sec 10)) "·"))]
|
||||
(recur (+ frame tick-frames) (inc tick-num)
|
||||
(conj ticks {:frame frame :px px :label label})))))))
|
||||
|
||||
;; --- timeline -------------------------------------------------------------
|
||||
|
||||
(defn- scrub! [content fps zoom ev]
|
||||
|
|
@ -373,6 +401,15 @@
|
|||
:on-click #(swap! tracks-open? not)}
|
||||
(if @tracks-open? "<- HIDE" "->")]]
|
||||
[:div.lanes-hscroll {:ref (fn [n] (reset! lanes-scroll n))}
|
||||
[:div.time-ruler {:style {:height 24 :width width :position "relative" :border-bottom "1px solid var(--ink)"}}
|
||||
(let [ticks (time-ruler len fps zoom)]
|
||||
(for [{:keys [frame px label]} ticks]
|
||||
^{:key frame}
|
||||
[:div {:style {:position "absolute" :left (str px "px") :top 0 :height "100%"
|
||||
:display "flex" :flex-direction "column" :align-items "center"}}
|
||||
[:div {:style {:height 8 :width 1 :background "var(--ink)" :margin-top 6}}]
|
||||
[:div {:style {:font-size 10 :margin-top 2 :white-space "nowrap" :color "var(--mute)"}}
|
||||
label]]))]
|
||||
[:div.ann-lanes {:style {:height lane-h :width width}
|
||||
:on-mouse-down #(scrub! @content fps zoom %)}
|
||||
;; annotation bars — solid for saved, dashed for the in-progress draft
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue