├── preview.png
├── README.md
├── index.html
├── style.css
└── main.js
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hicodersofficial/custom-html5-video-player/HEAD/preview.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Custom HTML5 Video Player
2 |
3 | ## [See Live](https://codepen.io/hicoders/pen/ZExMmLG)
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
17 |
18 |
22 | Custom Video Controls
23 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
35 | 5
36 |
37 |
38 |
39 |
40 |
41 | 5
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
65 |
66 |
67 | 0:00
68 | /
69 | 0:00
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: "Montserrat", sans-serif;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | height: 100vh;
8 | background: #131418;
9 | }
10 |
11 | .video-container {
12 | width: 1024px;
13 | height: calc((9 / 16) * 1024px);
14 | position: relative;
15 | color: #fff;
16 | overflow: hidden;
17 | background: rgb(25, 25, 25);
18 | display: flex;
19 | align-items: center;
20 | justify-content: center;
21 | }
22 |
23 | .video-container video {
24 | max-width: 100%;
25 | max-height: 100%;
26 | }
27 |
28 | .custom-loader {
29 | position: absolute;
30 | width: 50px;
31 | height: 50px;
32 | border-radius: 50%;
33 | border-top: 5px solid transparent;
34 | border-right: 5px solid #ffffff;
35 | border-left: 5px solid #ffffff;
36 | border-bottom: 5px solid #ffffff;
37 | z-index: 9999;
38 | animation: rotation 2s infinite ease;
39 | -webkit-animation: rotation 2s infinite ease;
40 | display: none;
41 | }
42 |
43 | .player-state {
44 | display: flex;
45 | position: absolute;
46 | width: 100%;
47 | justify-content: space-around;
48 | }
49 |
50 | .state-btn {
51 | font-size: 2.3rem;
52 | width: 80px;
53 | height: 80px;
54 | cursor: pointer;
55 | border-radius: 50%;
56 | display: flex;
57 | align-items: center;
58 | justify-content: center;
59 | transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
60 | background: rgba(36, 36, 36, 0.667);
61 | z-index: 9999;
62 | -webkit-tap-highlight-color: transparent;
63 | opacity: 0;
64 | user-select: none;
65 | transform: scale(0);
66 | }
67 |
68 | .state-forward,
69 | .state-backward {
70 | font-size: 1.5rem;
71 | }
72 |
73 | .state-forward .forward-duration {
74 | margin-right: 0.5rem;
75 | }
76 | .state-backward .backward-duration {
77 | margin-left: 0.5rem;
78 | }
79 |
80 | .animate-state {
81 | animation: playPause 0.5s forwards;
82 | }
83 |
84 | .show-state {
85 | transform: scale(1);
86 | opacity: 1;
87 | }
88 |
89 | .show-controls {
90 | opacity: 1 !important;
91 | transform: translateY(0) !important;
92 | visibility: visible !important;
93 | }
94 |
95 | .controls {
96 | position: absolute;
97 | bottom: 0;
98 | left: 0;
99 | padding: 5rem 1rem 0.5rem 1rem;
100 | width: 100%;
101 | background: linear-gradient(to top, #000000b8 -100%, transparent);
102 | box-sizing: border-box;
103 | opacity: 0;
104 | transform: translateY(40px);
105 | visibility: hidden;
106 | z-index: 99;
107 | transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
108 | user-select: none;
109 | -webkit-tap-highlight-color: transparent;
110 | }
111 |
112 | .duration {
113 | position: relative;
114 | width: 100%;
115 | height: 15px;
116 | background: #62626274;
117 | cursor: pointer;
118 | transition: all 0.2s;
119 | }
120 |
121 | .duration:hover {
122 | height: 17px;
123 | }
124 |
125 | .duration .buffer {
126 | height: 100%;
127 | position: absolute;
128 | inset: 0;
129 | background-color: #ff6a0045;
130 | z-index: 9;
131 | width: 0;
132 | }
133 |
134 | .hover-time {
135 | height: 100%;
136 | position: absolute;
137 | inset: 0;
138 | background: #ffffff9a;
139 | z-index: 99;
140 | display: flex;
141 | align-items: center;
142 | width: 0;
143 | }
144 |
145 | .hover-time .hover-duration {
146 | position: absolute;
147 | right: calc((-25px / 2));
148 | top: -25px;
149 | background: #3c3c3ca7;
150 | padding: 0.2rem;
151 | border-radius: 5px;
152 |
153 | font-size: 0.7rem;
154 | visibility: hidden;
155 | font-weight: bold;
156 | opacity: 0;
157 | transform: scale(0);
158 | }
159 |
160 | .duration:hover .hover-time .hover-duration {
161 | visibility: visible;
162 | opacity: 1;
163 | transition: all 0.2s;
164 | transform: scale(1);
165 | }
166 |
167 | .duration .current-time {
168 | height: 100%;
169 | position: absolute;
170 | inset: 0;
171 | background: #ff6a00;
172 | z-index: 999;
173 | display: flex;
174 | align-items: center;
175 | width: 0;
176 | }
177 |
178 | .current-time::before {
179 | content: "";
180 | position: absolute;
181 | right: calc((-25px / 2));
182 | background: #ff6a00;
183 | width: 25px;
184 | height: 25px;
185 | border-radius: 50%;
186 | transition: all 0.2s;
187 | visibility: hidden;
188 | transform: scale(0);
189 | }
190 |
191 | .duration:hover .current-time::before {
192 | visibility: visible;
193 | transform: scale(1);
194 | }
195 |
196 | .btn-controls {
197 | padding-top: 1rem;
198 | font-size: 1.2rem;
199 | display: flex;
200 | align-items: center;
201 | justify-content: space-between;
202 | }
203 |
204 | .btn-con {
205 | display: flex;
206 | align-items: center;
207 | }
208 |
209 | .btn-con,
210 | .btn-controls > span {
211 | cursor: pointer;
212 | }
213 |
214 | .play-pause {
215 | display: flex;
216 | margin-right: 0.5rem;
217 | }
218 |
219 | .control-btn {
220 | width: 2.2rem;
221 | height: 2.2rem;
222 | border-radius: 50%;
223 | background: #33333372;
224 | display: flex;
225 | align-items: center;
226 | justify-content: center;
227 | border: 1px solid transparent;
228 | box-sizing: border-box;
229 | position: relative;
230 | margin-right: 0.5rem;
231 | }
232 |
233 | .control-btn:last-child {
234 | margin-right: 0;
235 | }
236 |
237 | .control-btn:hover {
238 | border: 1px solid #3131315c;
239 | }
240 |
241 | .control-btn::before {
242 | content: "";
243 | display: block;
244 | width: 100%;
245 | height: 100%;
246 | border-radius: 50%;
247 | background: #2424246a;
248 | position: absolute;
249 | transition: all 0.1s;
250 | transform: scale(0);
251 | }
252 |
253 | .control-btn:active::before {
254 | transform: scale(1);
255 | border: 1px solid #3131315c;
256 | }
257 |
258 | .time-container {
259 | font-size: 13px;
260 | font-weight: 500;
261 | padding-left: 0.7rem;
262 | }
263 |
264 | .volume {
265 | display: flex;
266 | align-items: center;
267 | cursor: default;
268 | }
269 |
270 | .mute-unmute {
271 | display: flex;
272 | cursor: pointer;
273 | }
274 |
275 | .max-vol {
276 | height: 3px;
277 | cursor: pointer;
278 | background: #ffffff6e;
279 | transition: all 0.1s;
280 | width: 0;
281 | visibility: hidden;
282 | transform: scaleX(0);
283 | transform-origin: left;
284 | display: flex;
285 | align-items: center;
286 | }
287 |
288 | .max-vol.show {
289 | width: 56px;
290 | visibility: visible;
291 | transform: scaleX(1);
292 | }
293 |
294 | .current-vol {
295 | position: absolute;
296 | inset: 0;
297 | width: 20%;
298 | height: 100%;
299 | background: #fff;
300 | display: flex;
301 | transition: none;
302 | align-items: center;
303 | }
304 |
305 | .current-vol::before {
306 | content: "";
307 | position: absolute;
308 | right: -5px;
309 | width: 12px;
310 | height: 12px;
311 | display: block;
312 | border-radius: 50%;
313 | background: #eee;
314 | }
315 |
316 | .setting-menu {
317 | opacity: 0;
318 | visibility: hidden;
319 | list-style: none;
320 | padding-inline-start: 0;
321 | margin-block-start: 0;
322 | margin-block-end: 0;
323 | position: absolute;
324 | bottom: 4.5rem;
325 | transition: all 0.2s;
326 | background: rgba(28, 28, 28, 0.9);
327 | transform: scaleY(0);
328 | transform-origin: bottom;
329 | border-radius: 5px;
330 | }
331 |
332 | .setting-menu li {
333 | padding: 0.3rem 2rem;
334 | margin: 0.5rem;
335 | transition: all 0.2s;
336 | border-radius: 5px;
337 | font-size: 0.9rem;
338 | font-weight: 500;
339 | }
340 |
341 | .speed-active {
342 | background: rgb(23, 23, 23);
343 | }
344 |
345 | .setting-menu li:hover {
346 | background: rgb(31, 31, 31);
347 | }
348 |
349 | .setting-btn {
350 | display: flex;
351 | }
352 |
353 | .show-setting-menu {
354 | opacity: 1;
355 | transform: scaleY(1);
356 | visibility: visible;
357 | }
358 |
359 | .theater {
360 | width: 100% !important;
361 | }
362 |
363 | .theater-btn .theater-default,
364 | .theater-btn .theater-active {
365 | display: flex;
366 | }
367 | .video-container.theater .theater-default {
368 | display: none;
369 | }
370 |
371 | .video-container:not(.theater) .theater-active {
372 | display: none;
373 | }
374 |
375 | .fullscreen {
376 | position: absolute !important;
377 | max-width: 100% !important;
378 | width: 100% !important;
379 | height: 100% !important;
380 | display: flex !important;
381 | background: #000 !important;
382 | align-items: center !important;
383 | }
384 |
385 | .right-controls {
386 | display: flex;
387 | align-items: center;
388 | }
389 |
390 | .right-controls span {
391 | cursor: pointer;
392 | }
393 |
394 | .full,
395 | .contract {
396 | display: none;
397 | }
398 |
399 | .video-container:not(.fullscreen) .full {
400 | display: flex;
401 | }
402 |
403 | .video-container.fullscreen .contract {
404 | display: flex;
405 | }
406 |
407 | @keyframes playPause {
408 | 50% {
409 | opacity: 1;
410 | transform: scale(1.1);
411 | }
412 | 100% {
413 | opacity: 0;
414 | transform: scale(1);
415 | }
416 | }
417 |
418 | @keyframes rotation {
419 | from {
420 | transform: rotate(0deg);
421 | }
422 | to {
423 | transform: rotate(360deg);
424 | }
425 | }
426 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | const video = document.querySelector("video");
2 | const fullscreen = document.querySelector(".fullscreen-btn");
3 | const playPause = document.querySelector(".play-pause");
4 | const volume = document.querySelector(".volume");
5 | const currentTime = document.querySelector(".current-time");
6 | const duration = document.querySelector(".duration");
7 | const buffer = document.querySelector(".buffer");
8 | const totalDuration = document.querySelector(".total-duration");
9 | const currentDuration = document.querySelector(".current-duration");
10 | const controls = document.querySelector(".controls");
11 | const videoContainer = document.querySelector(".video-container");
12 | const currentVol = document.querySelector(".current-vol");
13 | const totalVol = document.querySelector(".max-vol");
14 | const mainState = document.querySelector(".main-state");
15 | const muteUnmute = document.querySelector(".mute-unmute");
16 | const forward = document.querySelector(".forward");
17 | const backward = document.querySelector(".backward");
18 | const hoverTime = document.querySelector(".hover-time");
19 | const hoverDuration = document.querySelector(".hover-duration");
20 | const miniPlayer = document.querySelector(".mini-player");
21 | const settingsBtn = document.querySelector(".setting-btn");
22 | const settingMenu = document.querySelector(".setting-menu");
23 | const theaterBtn = document.querySelector(".theater-btn");
24 | const speedButtons = document.querySelectorAll(".setting-menu li");
25 | const backwardSate = document.querySelector(".state-backward");
26 | const forwardSate = document.querySelector(".state-forward");
27 | const loader = document.querySelector(".custom-loader");
28 |
29 | let isPlaying = false,
30 | mouseDownProgress = false,
31 | mouseDownVol = false,
32 | isCursorOnControls = false,
33 | muted = false,
34 | timeout,
35 | volumeVal = 1,
36 | mouseOverDuration = false,
37 | touchClientX = 0,
38 | touchPastDurationWidth = 0,
39 | touchStartTime = 0;
40 |
41 | currentVol.style.width = volumeVal * 100 + "%";
42 |
43 | // Video Event Listeners
44 | video.addEventListener("loadedmetadata", canPlayInit);
45 | video.addEventListener("play", play);
46 | video.addEventListener("pause", pause);
47 | video.addEventListener("progress", handleProgress);
48 | video.addEventListener("waiting", handleWaiting);
49 | video.addEventListener("playing", handlePlaying);
50 |
51 | document.addEventListener("keydown", handleShorthand);
52 | fullscreen.addEventListener("click", toggleFullscreen);
53 |
54 | playPause.addEventListener("click", (e) => {
55 | if (!isPlaying) {
56 | play();
57 | } else {
58 | pause();
59 | }
60 | });
61 |
62 | duration.addEventListener("click", navigate);
63 |
64 | duration.addEventListener("mousedown", (e) => {
65 | mouseDownProgress = true;
66 | navigate(e);
67 | });
68 |
69 | totalVol.addEventListener("mousedown", (e) => {
70 | mouseDownVol = true;
71 | handleVolume(e);
72 | });
73 |
74 | document.addEventListener("mouseup", (e) => {
75 | mouseDownProgress = false;
76 | mouseDownVol = false;
77 | });
78 |
79 | document.addEventListener("mousemove", handleMousemove);
80 |
81 | duration.addEventListener("mouseenter", (e) => {
82 | mouseOverDuration = true;
83 | });
84 | duration.addEventListener("mouseleave", (e) => {
85 | mouseOverDuration = false;
86 | hoverTime.style.width = 0;
87 | hoverDuration.innerHTML = "";
88 | });
89 |
90 | videoContainer.addEventListener("click", toggleMainState);
91 | videoContainer.addEventListener("fullscreenchange", () => {
92 | videoContainer.classList.toggle("fullscreen", document.fullscreenElement);
93 | });
94 | videoContainer.addEventListener("mouseleave", hideControls);
95 | videoContainer.addEventListener("mousemove", (e) => {
96 | controls.classList.add("show-controls");
97 | hideControls();
98 | });
99 | videoContainer.addEventListener("touchstart", (e) => {
100 | controls.classList.add("show-controls");
101 | touchClientX = e.changedTouches[0].clientX;
102 | const currentTimeRect = currentTime.getBoundingClientRect();
103 | touchPastDurationWidth = currentTimeRect.width;
104 | touchStartTime = e.timeStamp;
105 | });
106 | videoContainer.addEventListener("touchend", () => {
107 | hideControls();
108 | touchClientX = 0;
109 | touchPastDurationWidth = 0;
110 | touchStartTime = 0;
111 | });
112 | videoContainer.addEventListener("touchmove", handleTouchNavigate);
113 |
114 | controls.addEventListener("mouseenter", (e) => {
115 | controls.classList.add("show-controls");
116 | isCursorOnControls = true;
117 | });
118 |
119 | controls.addEventListener("mouseleave", (e) => {
120 | isCursorOnControls = false;
121 | });
122 |
123 | mainState.addEventListener("click", toggleMainState);
124 |
125 | mainState.addEventListener("animationend", handleMainSateAnimationEnd);
126 |
127 | muteUnmute.addEventListener("click", toggleMuteUnmute);
128 |
129 | muteUnmute.addEventListener("mouseenter", (e) => {
130 | if (!muted) {
131 | totalVol.classList.add("show");
132 | } else {
133 | totalVol.classList.remove("show");
134 | }
135 | });
136 |
137 | muteUnmute.addEventListener("mouseleave", (e) => {
138 | if (e.relatedTarget != volume) {
139 | totalVol.classList.remove("show");
140 | }
141 | });
142 |
143 | forward.addEventListener("click", handleForward);
144 |
145 | forwardSate.addEventListener("animationend", () => {
146 | forwardSate.classList.remove("show-state");
147 | forwardSate.classList.remove("animate-state");
148 | });
149 |
150 | backward.addEventListener("click", handleBackward);
151 |
152 | backwardSate.addEventListener("animationend", () => {
153 | backwardSate.classList.remove("show-state");
154 | backwardSate.classList.remove("animate-state");
155 | });
156 |
157 | miniPlayer.addEventListener("click", toggleMiniPlayer);
158 |
159 | theaterBtn.addEventListener("click", toggleTheater);
160 |
161 | settingsBtn.addEventListener("click", handleSettingMenu);
162 |
163 | speedButtons.forEach((btn) => {
164 | btn.addEventListener("click", handlePlaybackRate);
165 | });
166 |
167 | function canPlayInit() {
168 | totalDuration.innerHTML = showDuration(video.duration);
169 | video.volume = volumeVal;
170 | muted = video.muted;
171 | if (video.paused) {
172 | controls.classList.add("show-controls");
173 | mainState.classList.add("show-state");
174 | handleMainStateIcon(``);
175 | }
176 | }
177 |
178 | function play() {
179 | video.play();
180 | isPlaying = true;
181 | playPause.innerHTML = ``;
182 | mainState.classList.remove("show-state");
183 | handleMainStateIcon(``);
184 | // watchProgress();
185 | }
186 |
187 | // function watchProgress() {
188 | // if (isPlaying) {
189 | // requestAnimationFrame(watchProgress);
190 | // handleProgressBar();
191 | // }
192 | // }
193 |
194 | video.ontimeupdate = handleProgressBar;
195 |
196 | function handleProgressBar() {
197 | currentTime.style.width = (video.currentTime / video.duration) * 100 + "%";
198 | currentDuration.innerHTML = showDuration(video.currentTime);
199 | }
200 |
201 | function pause() {
202 | video.pause();
203 | isPlaying = false;
204 | playPause.innerHTML = ``;
205 | controls.classList.add("show-controls");
206 | mainState.classList.add("show-state");
207 | handleMainStateIcon(``);
208 | if (video.ended) {
209 | currentTime.style.width = 100 + "%";
210 | }
211 | }
212 |
213 | function handleWaiting() {
214 | loader.style.display = "unset";
215 | }
216 |
217 | function handlePlaying() {
218 | loader.style.display = "none";
219 | }
220 |
221 | function navigate(e) {
222 | const totalDurationRect = duration.getBoundingClientRect();
223 | const width = Math.min(
224 | Math.max(0, e.clientX - totalDurationRect.x),
225 | totalDurationRect.width
226 | );
227 | currentTime.style.width = (width / totalDurationRect.width) * 100 + "%";
228 | video.currentTime = (width / totalDurationRect.width) * video.duration;
229 | }
230 |
231 | function handleTouchNavigate(e) {
232 | hideControls();
233 | if (e.timeStamp - touchStartTime > 500) {
234 | const durationRect = duration.getBoundingClientRect();
235 | const clientX = e.changedTouches[0].clientX;
236 | const value = Math.min(
237 | Math.max(0, touchPastDurationWidth + (clientX - touchClientX) * 0.2),
238 | durationRect.width
239 | );
240 | currentTime.style.width = value + "px";
241 | video.currentTime = (value / durationRect.width) * video.duration;
242 | currentDuration.innerHTML = showDuration(video.currentTime);
243 | }
244 | }
245 |
246 | function showDuration(time) {
247 | const hours = Math.floor(time / 60 ** 2);
248 | const min = Math.floor((time / 60) % 60);
249 | const sec = Math.floor(time % 60);
250 | if (hours > 0) {
251 | return `${formatter(hours)}:${formatter(min)}:${formatter(sec)}`;
252 | } else {
253 | return `${formatter(min)}:${formatter(sec)}`;
254 | }
255 | }
256 |
257 | function formatter(number) {
258 | return new Intl.NumberFormat({}, { minimumIntegerDigits: 2 }).format(number);
259 | }
260 |
261 | function toggleMuteUnmute() {
262 | if (!muted) {
263 | video.volume = 0;
264 | muted = true;
265 | muteUnmute.innerHTML = ``;
266 | handleMainStateIcon(``);
267 | totalVol.classList.remove("show");
268 | } else {
269 | video.volume = volumeVal;
270 | muted = false;
271 | totalVol.classList.add("show");
272 | handleMainStateIcon(``);
273 | muteUnmute.innerHTML = ``;
274 | }
275 | }
276 |
277 | function hideControls() {
278 | if (timeout) {
279 | clearTimeout(timeout);
280 | }
281 | timeout = setTimeout(() => {
282 | if (isPlaying && !isCursorOnControls) {
283 | controls.classList.remove("show-controls");
284 | settingMenu.classList.remove("show-setting-menu");
285 | }
286 | }, 1000);
287 | }
288 |
289 | function toggleMainState(e) {
290 | e.stopPropagation();
291 | if (!e.path.includes(controls)) {
292 | if (!isPlaying) {
293 | play();
294 | } else {
295 | pause();
296 | }
297 | }
298 | }
299 |
300 | function handleVolume(e) {
301 | const totalVolRect = totalVol.getBoundingClientRect();
302 | currentVol.style.width =
303 | Math.min(Math.max(0, e.clientX - totalVolRect.x), totalVolRect.width) +
304 | "px";
305 | volumeVal = Math.min(
306 | Math.max(0, (e.clientX - totalVolRect.x) / totalVolRect.width),
307 | 1
308 | );
309 | video.volume = volumeVal;
310 | }
311 |
312 | function handleProgress() {
313 | if (!video.buffered || !video.buffered.length) {
314 | return;
315 | }
316 | const width = (video.buffered.end(0) / video.duration) * 100 + "%";
317 | buffer.style.width = width;
318 | }
319 |
320 | function toggleFullscreen() {
321 | if (!document.fullscreenElement) {
322 | videoContainer.requestFullscreen();
323 | handleMainStateIcon(``);
324 | } else {
325 | handleMainStateIcon(` `);
326 | document.exitFullscreen();
327 | }
328 | }
329 |
330 | function handleMousemove(e) {
331 | if (mouseDownProgress) {
332 | e.preventDefault();
333 | navigate(e);
334 | }
335 | if (mouseDownVol) {
336 | handleVolume(e);
337 | }
338 | if (mouseOverDuration) {
339 | const rect = duration.getBoundingClientRect();
340 | const width = Math.min(Math.max(0, e.clientX - rect.x), rect.width);
341 | const percent = (width / rect.width) * 100;
342 | hoverTime.style.width = width + "px";
343 | hoverDuration.innerHTML = showDuration((video.duration / 100) * percent);
344 | }
345 | }
346 |
347 | function handleForward() {
348 | forwardSate.classList.add("show-state");
349 | forwardSate.classList.add("animate-state");
350 | video.currentTime += 5;
351 | handleProgressBar();
352 | }
353 |
354 | function handleBackward() {
355 | backwardSate.classList.add("show-state");
356 | backwardSate.classList.add("animate-state");
357 | video.currentTime -= 5;
358 | handleProgressBar();
359 | }
360 |
361 | function handleMainStateIcon(icon) {
362 | mainState.classList.add("animate-state");
363 | mainState.innerHTML = icon;
364 | }
365 |
366 | function handleMainSateAnimationEnd() {
367 | mainState.classList.remove("animate-state");
368 | if (!isPlaying) {
369 | mainState.innerHTML = ``;
370 | }
371 | if (document.pictureInPictureElement) {
372 | mainState.innerHTML = ` `;
373 | }
374 | }
375 |
376 | function toggleTheater() {
377 | videoContainer.classList.toggle("theater");
378 | if (videoContainer.classList.contains("theater")) {
379 | handleMainStateIcon(
380 | ``
381 | );
382 | } else {
383 | handleMainStateIcon(``);
384 | }
385 | }
386 |
387 | function toggleMiniPlayer() {
388 | if (document.pictureInPictureElement) {
389 | document.exitPictureInPicture();
390 | handleMainStateIcon(``);
391 | } else {
392 | video.requestPictureInPicture();
393 | handleMainStateIcon(``);
394 | }
395 | }
396 |
397 | function handleSettingMenu() {
398 | settingMenu.classList.toggle("show-setting-menu");
399 | }
400 |
401 | function handlePlaybackRate(e) {
402 | video.playbackRate = parseFloat(e.target.dataset.value);
403 | speedButtons.forEach((btn) => {
404 | btn.classList.remove("speed-active");
405 | });
406 | e.target.classList.add("speed-active");
407 | settingMenu.classList.remove("show-setting-menu");
408 | }
409 |
410 | function handlePlaybackRateKey(type = "") {
411 | if (type === "increase" && video.playbackRate < 2) {
412 | video.playbackRate += 0.25;
413 | } else if (video.playbackRate > 0.25 && type !== "increase") {
414 | video.playbackRate -= 0.25;
415 | }
416 | handleMainStateIcon(
417 | `${video.playbackRate}x`
418 | );
419 | speedButtons.forEach((btn) => {
420 | btn.classList.remove("speed-active");
421 | if (btn.dataset.value == video.playbackRate) {
422 | btn.classList.add("speed-active");
423 | }
424 | });
425 | }
426 |
427 | function handleShorthand(e) {
428 | const tagName = document.activeElement.tagName.toLowerCase();
429 | if (tagName === "input") return;
430 | if (e.key.match(/[0-9]/gi)) {
431 | video.currentTime = (video.duration / 100) * (parseInt(e.key) * 10);
432 | currentTime.style.width = parseInt(e.key) * 10 + "%";
433 | }
434 | switch (e.key.toLowerCase()) {
435 | case " ":
436 | if (tagName === "button") return;
437 | if (isPlaying) {
438 | video.pause();
439 | } else {
440 | video.play();
441 | }
442 | break;
443 | case "f":
444 | toggleFullscreen();
445 | break;
446 | case "arrowright":
447 | handleForward();
448 | break;
449 | case "arrowleft":
450 | handleBackward();
451 | break;
452 | case "t":
453 | toggleTheater();
454 | break;
455 | case "i":
456 | toggleMiniPlayer();
457 | break;
458 | case "m":
459 | toggleMuteUnmute();
460 | break;
461 | case "+":
462 | handlePlaybackRateKey("increase");
463 | break;
464 | case "-":
465 | handlePlaybackRateKey();
466 | break;
467 |
468 | default:
469 | break;
470 | }
471 | }
472 |
--------------------------------------------------------------------------------