Skip to content

Commit f810e60

Browse files
committed
Make the playback-control demo visually more compelling
1 parent 8fc2701 commit f810e60

1 file changed

Lines changed: 181 additions & 28 deletions

File tree

demo/playback-control/demo.html

Lines changed: 181 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
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 () => {
@@ -31,14 +34,14 @@
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(() => {
@@ -47,8 +50,11 @@
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

@@ -63,7 +69,7 @@
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
@@ -76,12 +82,14 @@
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>
@@ -95,7 +103,7 @@
95103
pointer-events: none;
96104
}
97105
::view-transition-group(*) {
98-
animation-duration: 5s;
106+
animation-duration: 3s;
99107
}
100108

101109
* {
@@ -106,41 +114,186 @@
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

Comments
 (0)