1616 import { getAnimations , ViewTransitionPart } from '../js/animations.js' ;
1717
1818 // The View Transition and one of its animations to track
19- let t , a ;
19+ let t , a , rAFHandle ;
2020
2121 const syncProgressToSlider = ( ) => {
22- document . getElementById ( "scrub" ) . value = a . effect . getComputedTiming ( ) . progress . toFixed ( 2 ) ;
23- requestAnimationFrame ( syncProgressToSlider ) ;
24- }
22+ const val = a . effect . getComputedTiming ( ) . progress . toFixed ( 2 ) ;
23+ document . getElementById ( "scrub" ) . value = val ;
24+ const valEl = document . getElementById ( "slider-val" ) ;
25+ if ( valEl ) valEl . innerText = val ;
26+ rAFHandle = requestAnimationFrame ( syncProgressToSlider ) ;
27+ } ;
2528
2629 // When clicking the start button, start a View Transition
2730 document . getElementById ( "start" ) . addEventListener ( "click" , async ( ) => {
3134 document . getElementById ( "scrub" ) . disabled = true ;
3235
3336 t = document . startViewTransition ( ( ) => {
34- document . querySelector ( '.box ' ) . classList . toggle ( 'supergreen' ) ;
37+ document . querySelector ( '.track ' ) . classList . toggle ( 'supergreen' ) ;
3538 } ) ;
3639
3740 t . ready . then ( ( ) => {
3841 a = getAnimations ( t , 'box' , ViewTransitionPart . Group ) [ 0 ] ;
3942
4043 // Start a rAF with syncProgressToSlider
41- requestAnimationFrame ( syncProgressToSlider ) ;
44+ rAFHandle = requestAnimationFrame ( syncProgressToSlider ) ;
4245 } ) ;
4346
4447 t . finished . then ( ( ) => {
4750 document . getElementById ( "resume" ) . disabled = true ;
4851 document . getElementById ( "scrub" ) . disabled = true ;
4952
53+ const valEl = document . getElementById ( "slider-val" ) ;
54+ if ( valEl ) valEl . innerText = "0.00" ;
55+
5056 // Stop the rAF
51- cancelAnimationFrame ( syncProgressToSlider ) ;
57+ cancelAnimationFrame ( rAFHandle ) ;
5258 } ) ;
5359 } ) ;
5460
6369 pause ( t ) ;
6470
6571 // Stop the rAF
66- cancelAnimationFrame ( syncProgressToSlider ) ;
72+ cancelAnimationFrame ( rAFHandle ) ;
6773 } ) ;
6874
6975 // When clicking the resume button, unpause the View Transition
7682 resume ( t ) ;
7783
7884 // Start a rAF with syncProgressToSlider
79- requestAnimationFrame ( syncProgressToSlider ) ;
85+ rAFHandle = requestAnimationFrame ( syncProgressToSlider ) ;
8086 } ) ;
8187
8288 // When moving the slider, scrub the View Transition
8389 document . getElementById ( "scrub" ) . addEventListener ( "input" , ( e ) => {
8490 scrub ( t , e . target . value ) ;
91+ const valEl = document . getElementById ( "slider-val" ) ;
92+ if ( valEl ) valEl . innerText = Number ( e . target . value ) . toFixed ( 2 ) ;
8593 } ) ;
8694 </ script >
8795 < style >
95103 pointer-events : none;
96104 }
97105 ::view-transition-group (* ) {
98- animation-duration : 5 s ;
106+ animation-duration : 3 s ;
99107 }
100108
101109 * {
106114
107115 html {
108116 font-family : system-ui, "Segoe UI" , Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji" , "Segoe UI Emoji" , "Segoe UI Symbol" ;
117+ background-color : # f8fafc ;
118+ height : 100% ;
109119 }
110120
111- select , input {
112- font-family : inherit;
121+ body {
122+ height : 100% ;
123+ display : grid;
124+ place-content : safe center;
125+ padding : 5vw 0 ;
113126 }
114127
115- html , body {
116- height : 100% ;
128+ .container {
129+ background : white;
130+ border : 1px solid rgba (148 , 163 , 184 , 0.2 );
131+ border-radius : 24px ;
132+ padding : 3rem ;
133+ box-shadow : 0 20px 25px -5px rgba (0 , 0 , 0 , 0.05 ), 0 10px 10px -5px rgba (0 , 0 , 0 , 0.02 );
134+ display : flex;
135+ flex-direction : column;
136+ align-items : center;
137+ gap : 2.5rem ;
138+ max-width : 450px ;
139+ width : 90vw ;;
117140 }
118141
119- body {
142+ .track {
143+ width : 100% ;
144+ aspect-ratio : 1 ;
145+ background : # f1f5f9 ;
146+ border-radius : 16px ;
120147 display : grid;
121- place-content : center;
148+ place-content : start;
149+ padding : 1.5rem ;
150+ border : 1px solid rgba (0 , 0 , 0 , 0.05 );
151+ }
152+
153+ .track .supergreen {
154+ place-content : end;
122155 }
123156
124157 .box {
125- height : 2em ;
126- width : 2em ;
127- background : blue;
158+ height : 80px ;
159+ width : 80px ;
160+ background : cornflowerblue;
161+ color : white;
162+ display : flex;
163+ align-items : center;
164+ justify-content : center;
165+ font-weight : 700 ;
166+ font-size : 1rem ;
167+ }
128168
129- & .supergreen {
130- background : green;
131- margin-left : auto;
169+ .controls-grid {
170+ display : grid;
171+ grid-template-columns : repeat (3 , 1fr );
172+ gap : 1rem ;
173+ width : 100% ;
174+ }
175+
176+ @media (max-width : 599px ) {
177+ .controls-grid {
178+ grid-template-columns : 1fr ;
132179 }
133180 }
181+
182+ button {
183+ padding : 0.75rem 1rem ;
184+ border : 1px solid rgba (148 , 163 , 184 , 0.2 );
185+ border-radius : 12px ;
186+ background : white;
187+ color : # 334155 ;
188+ font-weight : 600 ;
189+ cursor : pointer;
190+ transition : all 0.2s ease;
191+ display : flex;
192+ align-items : center;
193+ justify-content : center;
194+ gap : 0.5rem ;
195+ font-family : inherit;
196+ font-size : 0.9rem ;
197+ }
198+
199+ button : hover : not (: disabled ) {
200+ background : # f8fafc ;
201+ border-color : # 38bdf8 ;
202+ color : # 0284c7 ;
203+ transform : translateY (-2px );
204+ box-shadow : 0 4px 6px -1px rgba (0 , 0 , 0 , 0.1 );
205+ }
206+
207+ button : active : not (: disabled ) {
208+ transform : translateY (0 );
209+ }
210+
211+ button : disabled {
212+ opacity : 0.5 ;
213+ cursor : not-allowed;
214+ background : # f1f5f9 ;
215+ color : # 94a3b8 ;
216+ }
217+
218+ button # start {
219+ background : # 0f172a ;
220+ color : white;
221+ border : none;
222+ }
223+
224+ button # start : hover : not (: disabled ) {
225+ background : # 1e293b ;
226+ box-shadow : 0 4px 12px rgba (15 , 23 , 42 , 0.3 );
227+ }
228+
229+ .slider-container {
230+ width : 100% ;
231+ display : flex;
232+ flex-direction : column;
233+ gap : 0.75rem ;
234+ }
235+
236+ .slider-header {
237+ display : flex;
238+ justify-content : space-between;
239+ align-items : center;
240+ color : # 64748b ;
241+ font-size : 0.85rem ;
242+ font-weight : 500 ;
243+ }
244+
245+ input [type = "range" ] {
246+ -webkit-appearance : none;
247+ appearance : none;
248+ width : 100% ;
249+ height : 6px ;
250+ background : # e2e8f0 ;
251+ border-radius : 3px ;
252+ outline : none;
253+ }
254+
255+ input [type = "range" ]::-webkit-slider-thumb {
256+ -webkit-appearance : none;
257+ appearance : none;
258+ width : 18px ;
259+ height : 18px ;
260+ background : # 38bdf8 ;
261+ border-radius : 50% ;
262+ cursor : pointer;
263+ transition : all 0.2s ease;
264+ box-shadow : 0 4px 6px rgba (56 , 189 , 248 , 0.3 );
265+ }
266+
267+ input [type = "range" ]::-webkit-slider-thumb : hover {
268+ transform : scale (1.2 );
269+ }
270+
271+ input [type = "range" ]: disabled ::-webkit-slider-thumb {
272+ background : # 94a3b8 ;
273+ cursor : not-allowed;
274+ box-shadow : none;
275+ }
134276 </ style >
135277</ head >
136278< body >
137- < div >
138- < div class ="box "> </ div >
279+ < div class ="container ">
280+ < div class ="track ">
281+ < div class ="box "> BOX</ div >
282+ </ div >
283+
284+ < div class ="controls-grid ">
285+ < button id ="start "> Start</ button >
286+ < button id ="pause " disabled > Pause</ button >
287+ < button id ="resume " disabled > Resume</ button >
288+ </ div >
139289
140- < button id ="start "> Start VT</ button >
141- < button id ="pause " disabled > Pause VT</ button >
142- < button id ="resume " disabled > Resume VT</ button >
143- < input id ="scrub " type ="range " min ="0 " max ="1 " step ="0.01 " value ="0 " disabled >
290+ < div class ="slider-container ">
291+ < div class ="slider-header ">
292+ < span > Scrub Progress</ span >
293+ < span id ="slider-val "> 0.00</ span >
294+ </ div >
295+ < input id ="scrub " type ="range " min ="0 " max ="1 " step ="0.01 " value ="0 " disabled >
296+ </ div >
144297 </ div >
145298</ body >
146299</ html >
0 commit comments