├── 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 | ![](preview.png) 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 |
63 |
64 |
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 |
    87 |
  • 0.25x
  • 88 |
  • 0.5x
  • 89 |
  • 0.75x
  • 90 |
  • 1x
  • 91 |
  • 1.25x
  • 92 |
  • 1.5x
  • 93 |
  • 1.75x
  • 94 |
  • 2x
  • 95 |
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 | --------------------------------------------------------------------------------