11<script setup lang="ts">
2+ import { ref , onMounted , onUnmounted } from " vue" ;
3+ import { usePlayer } from " @vue-motion/core" ;
4+
5+ const player = usePlayer ();
6+ const isDragging = ref (false );
7+ const currentTime = ref (0 );
8+
29const { widget } = defineProps <{
310 widget: {
411 duration: number ;
512 };
613}>();
14+
15+ function handleMouseDown(e : MouseEvent ) {
16+ isDragging .value = true ;
17+ updateTimeFromMouseEvent (e );
18+
19+ // Prevent text selection while dragging
20+ e .preventDefault ();
21+ }
22+
23+ function handleMouseMove(e : MouseEvent ) {
24+ if (! isDragging .value ) return ;
25+
26+ // Find the SVG element and its container
27+ const svg = document .querySelector (" svg" );
28+ const container = document .querySelector (" .overflow-auto" );
29+ if (! svg || ! container ) return ;
30+
31+ const containerRect = container .getBoundingClientRect ();
32+ const scrollLeft = container .scrollLeft ;
33+
34+ // Calculate x position considering scroll and container offset
35+ // Allow negative values for smooth dragging experience
36+ const rawX = e .clientX - containerRect .left - 40 + scrollLeft ;
37+ const x = Math .max (0 , Math .min (widget .duration * 100 , rawX )); // Clamp to timeline width
38+ const totalWidth = widget .duration * 100 ; // 100px per second
39+
40+ // Auto scroll when dragging near edges or outside
41+ const scrollSpeed = 10 ;
42+
43+ if (e .clientX <= containerRect .left ) {
44+ // Mouse is left of the container - scroll left faster based on distance
45+ const distance = containerRect .left - e .clientX ;
46+ container .scrollLeft = Math .max (
47+ 0 ,
48+ scrollLeft - scrollSpeed * (1 + distance / 50 ),
49+ );
50+ } else if (e .clientX >= containerRect .right ) {
51+ // Mouse is right of the container - scroll right faster based on distance
52+ const distance = e .clientX - containerRect .right ;
53+ container .scrollLeft = scrollLeft + scrollSpeed * (1 + distance / 50 );
54+ }
55+
56+ // Calculate time in seconds (clamp between 0 and duration)
57+ const newTime = Math .max (
58+ 0 ,
59+ Math .min (widget .duration , (x / totalWidth ) * widget .duration ),
60+ );
61+ currentTime .value = newTime ;
62+ player .seek (newTime );
63+ }
64+
65+ function handleMouseUp() {
66+ isDragging .value = false ;
67+ }
68+
69+ function updateTimeFromMouseEvent(e : MouseEvent ) {
70+ const svg = e .currentTarget as SVGElement ;
71+ const container = svg .closest (" .overflow-auto" );
72+ if (! container ) return ;
73+
74+ const containerRect = container .getBoundingClientRect ();
75+ const scrollLeft = container .scrollLeft ;
76+
77+ const rawX = e .clientX - containerRect .left - 40 + scrollLeft ;
78+ const x = Math .max (0 , Math .min (widget .duration * 100 , rawX ));
79+ const totalWidth = widget .duration * 100 ;
80+
81+ const newTime = Math .max (
82+ 0 ,
83+ Math .min (widget .duration , (x / totalWidth ) * widget .duration ),
84+ );
85+ currentTime .value = newTime ;
86+ player .seek (newTime );
87+ }
88+
89+ // Add global mouse event listeners
90+ onMounted (() => {
91+ window .addEventListener (" mousemove" , handleMouseMove );
92+ window .addEventListener (" mouseup" , handleMouseUp );
93+ });
94+
95+ onUnmounted (() => {
96+ window .removeEventListener (" mousemove" , handleMouseMove );
97+ window .removeEventListener (" mouseup" , handleMouseUp );
98+ });
799 </script >
8100
9101<template >
10- <div class =" w-full h-full overflow-scroll " >
11- <div class =" mx-10" >
102+ <div class =" w-full h-full overflow-auto " >
103+ <div class =" mx-10 relative " >
12104 <svg
105+ @mousedown =" handleMouseDown"
13106 :style =" {
14- width: `${widget.duration * 10 }px`,
15- textAlign : 'center ',
107+ width: `${widget.duration * 100 }px`,
108+ height : '60px ',
16109 }"
110+ class =" cursor-col-resize"
17111 >
112+ <!-- Timeline marks - 10 marks per second -->
18113 <line :x1 =" 0" :y1 =" 0" :x2 =" 0" :y2 =" 35" stroke =" grey" stroke-width =" 1" />
19114 <line
20- v-for =" i in widget.duration * 10 - 10 "
115+ v-for =" i in widget.duration * 10"
21116 :key =" i"
22117 :x1 =" i * 10"
23118 :y1 =" 0"
@@ -26,11 +121,30 @@ const { widget } = defineProps<{
26121 stroke =" grey"
27122 stroke-width =" 1"
28123 />
124+
125+ <!-- Time labels - one per second -->
29126 <text :x =" 0" :y =" 50" >0</text >
30- <text v-for =" i in widget.duration - 1 " :key =" i" :x =" i * 100" :y =" 50" >
127+ <text v-for =" i in widget.duration" :key =" i" :x =" i * 100 - 10 " :y =" 50" >
31128 {{ i }}
32129 </text >
130+
131+ <!-- Current time indicator -->
132+ <line
133+ :x1 =" currentTime * 100"
134+ :y1 =" 0"
135+ :x2 =" currentTime * 100"
136+ :y2 =" 50"
137+ stroke =" #2196f3"
138+ stroke-width =" 2"
139+ />
140+ <circle :cx =" currentTime * 100" :cy =" 0" r =" 4" fill =" #2196f3" />
33141 </svg >
34142 </div >
35143 </div >
36144</template >
145+
146+ <style scoped>
147+ .cursor-col-resize {
148+ cursor : col-resize ;
149+ }
150+ </style >
0 commit comments