[JS] ๐๏ธJavaScript 30 - Day 11
๐Day 11 - Custom HTML5 Video Player
JavaScript 30์ Day 11 ํ๋ก์ ํธ๋ HTML, CSS, JavaScript๋ง์ ์ด์ฉํด์ ๋๋ง์ ๋น๋์ค ํ๋ ์ด์ด๋ฅผ ๋ง๋ค์ด๋ณด๋ ํ๋ก์ ํธ์ด๋ค.
๐ค๐์ฝ๋ ๋ชจ์๋ณด๊ธฐ
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>HTML Video Player</title> <link rel="stylesheet" href="style.css" /> </head> <body> <div class="player"> <video class="player__video viewer" src="652333414.mp4"></video> <div class="player__controls"> <div class="progress"> <div class="progress__filled"></div> </div> <button class="player__button toggle" title="Toggle Play">โบ</button> <button class="player__button volume" title="Toggle Volume">๐</button> <input type="range" name="volume" class="player__slider" min="0" max="1" step="0.05" value="1" /> <input type="range" name="playbackRate" class="player__slider" min="0.5" max="2" step="0.1" value="1" /> <button data-skip="-10" class="player__button">ยซ 10s</button> <button data-skip="10" class="player__button">10s ยป</button> <button class="player__button full-screen">๐ณ</button> </div> </div> <script src="main.js"></script> </body> </html>
CSS
html { box-sizing: border-box; } *, *:before, *:after { box-sizing: inherit; } body { margin: 0; padding: 0; display: flex; background: #7a419b; min-height: 100vh; background: linear-gradient(135deg, #7c1599 0%, #921099 48%, #7e4ae8 100%); background-size: cover; align-items: center; justify-content: center; } .player { max-width: 750px; border: 5px solid rgba(0, 0, 0, 0.2); box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); position: relative; font-size: 0; overflow: hidden; } /* This css is only applied when fullscreen is active. */ .player:fullscreen { max-width: none; width: 100%; } .player:-webkit-full-screen { max-width: none; width: 100%; } .player__video { width: 100%; } .player__button { background: none; border: 0; line-height: 1; color: white; text-align: center; outline: 0; padding: 0; cursor: pointer; max-width: 50px; } .player__button:focus { border-color: #ffc600; } .player__slider { width: 10px; height: 30px; } .player__controls { display: flex; position: absolute; bottom: 0; width: 100%; transform: translateY(100%) translateY(-5px); transition: all 0.3s; flex-wrap: wrap; background: rgba(0, 0, 0, 0.1); } .player:hover .player__controls { transform: translateY(0); } .player:hover .progress { height: 15px; } .player__controls > * { flex: 1; } .progress { flex: 10; position: relative; display: flex; flex-basis: 100%; height: 5px; transition: height 0.3s; background: rgba(0, 0, 0, 0.5); cursor: ew-resize; } .progress__filled { width: 50%; background: #ffc600; flex: 0; flex-basis: 50%; } /* unholy css to style input type="range" */ input[type="range"] { -webkit-appearance: none; background: transparent; width: 100%; margin: 0 5px; } input[type="range"]:focus { outline: none; } input[type="range"]::-webkit-slider-runnable-track { width: 100%; height: 8.4px; cursor: pointer; box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0); background: rgba(255, 255, 255, 0.8); border-radius: 1.3px; border: 0.2px solid rgba(1, 1, 1, 0); } input[type="range"]::-webkit-slider-thumb { height: 15px; width: 15px; border-radius: 50px; background: #ffc600; cursor: pointer; -webkit-appearance: none; margin-top: -3.5px; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2); } input[type="range"]:focus::-webkit-slider-runnable-track { background: #bada55; } input[type="range"]::-moz-range-track { width: 100%; height: 8.4px; cursor: pointer; box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0); background: #ffffff; border-radius: 1.3px; border: 0.2px solid rgba(1, 1, 1, 0); } input[type="range"]::-moz-range-thumb { box-shadow: 0 0 0 rgba(0, 0, 0, 0), 0 0 0 rgba(13, 13, 13, 0); height: 15px; width: 15px; border-radius: 50px; background: #ffc600; cursor: pointer; }
JavaScript
/* Get our Elements */ const player = document.querySelector(".player"); const video = player.querySelector(".viewer"); const progress = player.querySelector(".progress"); const progressBar = player.querySelector(".progress__filled"); const toggle = player.querySelector(".toggle"); const volume = player.querySelector(".volume"); const skipButtons = player.querySelectorAll("[data-skip]"); const ranges = player.querySelectorAll(".player__slider"); const screen = player.querySelector(".full-screen"); /* Build out functions */ function togglePlay() { const method = video.paused ? "play" : "pause"; video[method](); } function toggleVolume() { const mute = video.muted ? "๐" : "๐"; video.muted = !video.muted; volume.textContent = mute; } function updateToggle() { const icon = this.paused ? "โถ๏ธ" : "โโ"; toggle.textContent = icon; } function skip(e) { video.currentTime += parseInt(e.dataset.skip); } function handleKeyboard(e) { if (e.key === "ArrowRight") { skip(skipButtons[1]); } else if (e.key === "ArrowLeft") { skip(skipButtons[0]); } else if (e.key === " ") { togglePlay(); } else if (e.key === "m") { toggleVolume(); } } function handleRangeUpdate() { video[this.name] = this.value; } function handleProgress() { const percent = (video.currentTime / video.duration) * 100; progressBar.style.flexBasis = `${percent}%`; } function scrub(e) { const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration; video.currentTime = scrubTime; } function handleScreen() { video.requestFullscreen(); } /* Hook up the event listners */ video.addEventListener("click", togglePlay); video.addEventListener("play", updateToggle); video.addEventListener("pause", updateToggle); video.addEventListener("timeupdate", handleProgress); toggle.addEventListener("click", togglePlay); volume.addEventListener("click", toggleVolume); window.addEventListener("keydown", handleKeyboard); skipButtons.forEach((button) => button.addEventListener("click", skip)); ranges.forEach((range) => range.addEventListener("change", handleRangeUpdate)); ranges.forEach((range) => range.addEventListener("mousemove", handleRangeUpdate) ); let mousedown = false; progress.addEventListener("click", scrub); progress.addEventListener("mousedown", (e) => mousedown && scrub(e)); progress.addEventListener("mousedown", () => (mousedown = true)); progress.addEventListener("mouseup", () => (mousedown = false)); screen.addEventListener("click", handleScreen);
๐์ฝ๋ ์ค๋ช
- ๊ฐ์ฅ ๋จผ์ ,
querySelector
๋ฅผ ์ด์ฉํด์ ํ์ํ element๋ค์ ์ป์ ์ ์๊ฒ ์ธํ ํด์ค๋ค. ์ด์ ํ์ํ ๊ธฐ๋ฅ๋ค์ ํ๋์ฉ ๊ตฌํํ ์ฐจ๋ก๋ค. ์ฒซ๋ฒ์งธ๋ก, ์ฌ์ ๋ฐ ์ผ์์ ์ง ๊ธฐ๋ฅ์ด๋ค. togglePlay๋ผ๋ ํจ์๋ฅผ ๋ง๋ค๊ณ<video>
์paused
์์ฑ์ ์ด์ฉํด ํ์ฌ ์ผ์์ ์ง ๋์ด์๋ค๋ฉด 'play' ์ฌ์์ค์ด๋ผ๋ฉด 'pause'๋ฅผ method๋ผ๋ ๋ณ์์ ๋ฃ์ด์ค๋ค. ์ดํ์play()
ํน์pause()
๋ฉ์๋๋ก ์์์ ์ฌ์ / ์ผ์์ ์ง ์์ผ์ค๋ค. ์๊ฐ์ ํจ๊ณผ๋ฅผ ์ํด ์ฐ๋ฆฌ๊ฐ ์๋ ์ฌ์ ๋ฐ ์ผ์์ ์ง๋ฒํผ์ผ๋ก ์ ๋ฐ์ดํธ ํด์ฃผ๋ updateToggle ์ด๋ผ๋ ํจ์๋ ๊ฐ์ด ๋ง๋ค์ด์ฃผ๋๋ฐvideo
์play
,pause
์ด๋ฒคํธ์ ๋ฐ๋ผ ๋ฒํผ์ ์ ๋ฐ์ดํธ ํด์ค๋ค. - ๋๋ฒ์งธ๋ก, ์์์ ์คํต ๊ธฐ๋ฅ์ด๋ค. ์์์ ๋๊ธฐ๊ธฐ ์ํด์
currentTime
์ด๋ผ๋ ์์ฑ์ ์ด์ฉํ๋๋ฐskipButtons
์์ forEach๋ฌธ์ ์ด์ฉํดclick
์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด skip์ด๋ผ๋ ํจ์๋ฅผ ์คํ์ํจ๋ค. skip์ด๋ผ๋ ํจ์๋video.currentTime
์ ํด๋น ์ด๋ฒคํธ๊ฐ ๋ฒ์ด์ง ๋ฒํผ์ ๋ฐ์ดํฐ ์์ฑ์ ์ด์ฉํ๋ค. ์์ ์ ์์๋ดค๋ฏ์ด JavaScript์์ ์ ๊ทผํ ๋๋data-*
ํํ์์ ๋ท๋ถ๋ถ์ธ * ๋ง์ ์ด์ฉํด ์ ๊ทผํ๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ์ด๋ฒคํธ ๊ฐ์ฒดe
์ dataset์์ฑ์ ํตํด ๋ฌธ์์ด์ ๊ฐ์ ธ์ค๊ณ parseInt๋ก type์ ๋ฐ๊ฟ์ค๋ค. - ์ธ๋ฒ์งธ๋ก, ๋ณผ๋ฅจ๊ณผ ๋ฐฐ์์ ์กฐ์ ํ๋ ๊ธฐ๋ฅ์ด๋ค. ranges๋ผ๋ ๋ณ์์ player__slider๋ผ๋ ํด๋์ค์ ์์๋ค์ ๋ฐ์์คฌ๊ธฐ ๋๋ฌธ์ ๊ทธ๊ฑธ ์ด์ฉํ๋ค.
change
์mousemove
์ด๋ฒคํธ์ handleRangeUpdate๋ผ๋ ํจ์๋ฅผ ์คํ์ํค๋๋ฐ ์ด ํจ์๋ html์name
element๋ฅผ ์ด์ฉํด value๋ฅผ ์์ ํ๋ค. - ๋ง์ง๋ง์ผ๋ก, ๊ฐ์ฅ ๊น๋ค๋ก์ด ์ฌ์ ์งํ ๋ฐ ๊ธฐ๋ฅ์ด๋ค. ํ์ฌ ์ฌ์ ์งํ๋ฅ ์ ๋ฐ๋ผ ์ฌ์ ๋ฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ handleProgress ํจ์์ ํน์ ์ง์ ์ ๋ง์ฐ์ค๋ฅผ ๋์์ ๋ ์์์ ํน์ ์๊ฐ์ผ๋ก ์ด๋์ํค๋ scrubํจ์๋ฅผ ๊ตฌํํ๋ค. ๋จผ์ , handleProgress ํจ์๋
<video>
์currentTime
๊ณผduration
์ด๋ผ๋ ๋ฉ์๋๋ฅผ ์ด์ฉํ๋ค.๋น๋์ค์ ํ์ฌ ์ฌ์ ์๊ฐ / ์ ์ฒด ์์ ์๊ฐ
์ ํผ์ผํธ๋ฅผ percent๋ผ๋ ๋ณ์์ ๋ฃ์ด์ฃผ๊ณ felx-basis๋ฅผ percent๋งํผ ๋ฐ๊ฟ์ฃผ๋ฉด ์ํ๋ ๊ธฐ๋ฅ์ด ์คํ๋๋ค. scrubํจ์๋ progress์ ์ ์ฒด ๊ธธ์ด(progress.offsetWidth
)์์ ์ด๋ฒคํธ ๊ฐ์ฒด e์ x์ขํ(e.offsetX
)๊ฐ ์ด๋ ์๊ฐ์ ๋ํ๋ด๋์ง ๊ตฌํ๋ฉด ๋๋ค. ์๋ฅผ ๋ค๋ฉด, 1๋ถ ์ง๋ฆฌ ์์์์ ์ ๊ฐ์ด๋ฐ(50%)๋ฅผ ํด๋ฆญํ๋ฉด ์์์ ํ์ฌ ์ฌ์์๊ฐ์ด 30์ด๋ก ๋ฐ๋๋ฉด ๋๋ค. ์ด๋ฅผ ์ํด์ ํน์ ์์ ์ x์ขํ์์ ์ ์ฒด ๊ธธ์ด๋ฅผ ๋๋ ๊ฐ์ ์ ์ฒด ์ฌ์์๊ฐ์ ๊ณฑํด์ฃผ๋ฉด ์ํ๋ ์๊ฐ์ ์ป์ ์ ์๋ค. ๊ทธ๋ ๊ฒ ์ป์ ์๊ฐ์video.currentTime
์ ๋ฃ์ด์ฃผ๋ฉด ๋์ด๋ค. - ์ถ๊ฐ๋ก, ์์์ ์ ์ฒด ํ๋ฉด์ผ๋ก ์ฌ์ํ๋ ๊ธฐ๋ฅ๊ณผ ํค๋ณด๋์ ์คํ์ด์ค ๋ฐ๋ฅผ ํตํ ์ฌ์ ๋ฐ ์ผ์์ ์ง๊ธฐ๋ฅ, ๋ฐฉํฅํค๋ก ์คํตํ ์ ์๋ ๊ธฐ๋ฅ, m์ ๋๋ฅด๋ฉด ์์๊ฑฐ๊ฐ ๊บผ์ก๋ค ์ผ์ง๋ ๊ธฐ๋ฅ๋ฑ์ ์ถ๊ฐ๋ก ๊ตฌํํด๋ณด์๋ค. ์ ์ฒดํ๋ฉด์ผ๋ก ์ ํํด์ฃผ๋ ๊ฒ์ ๊ตฌ๊ธ๋ง์ ํด๋ณด๋ ๋ธ๋ผ์ฐ์ ๋ง๋ค ๋ฉ์๋๋ช ์ด ๋ฌ๋๋๋ฐ ๋๋ ํฌ๋กฌ์ ์ด์ฉํ๊ธฐ ๋๋ฌธ์ ํฌ๋กฌ์์๋ง ๊ตฌํ๋ ์ ์๋๋ก ํ๋ค. ํค๋ณด๋๋ฅผ ์ด์ฉํ๋ ๊ธฐ๋ฅ์ handleKeyboard๋ผ๋ ํจ์๋ฅผ ๋ง๋ค์ด ํด๋น ํค๊ฐ ์ ๋ ฅ๋ ๊ฒฝ์ฐ ํ์ํ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋๋ก ํด๋ณด์๋ค.
๐TIL(Today I Learned)
- ๋์ค์ ๋ณด๋ฉด ์ ๋ง ์์ข์ ์ฝ๋์ผ ์ ์์ง๋ง, ์ง๊ธ๊น์ง ๋ด๊ฐ ์๋ ๋ฐฉ๋ฒ + ๊ตฌ๊ธ๋ง์ ํตํด ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ฉด์ customizing ํด๋ดค๋๋ฐ, ์์ผ๋ก ์์ ํ๋ก์ ํธ๋ ์ด๋ฌํ ์์ ์ ํตํด ์๋ฐ์คํฌ๋ฆฝํธ์ ๋ ๊ฐ๊น์์ ธ์ผ๊ฒ ๋ค.
dataset
,offsetX
,offsetWidth
,video(HTML)
'Language > JavaScript' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JS] ๐๏ธJavaScript 30 - Day 12 (0) | 2021.11.29 |
---|---|
[JS] ๐๏ธJavaScript 30 - Day 10 (0) | 2021.11.26 |
[JS] ๐๏ธJavaScript 30 - Day 9 (0) | 2021.11.23 |
[JS] ๐๏ธJavaScript 30 - Day 8 (0) | 2021.11.22 |
[JS] ๐๏ธJavaScript 30 - Day 7 (0) | 2021.11.21 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[JS] ๐๏ธJavaScript 30 - Day 12
[JS] ๐๏ธJavaScript 30 - Day 12
2021.11.29 -
[JS] ๐๏ธJavaScript 30 - Day 10
[JS] ๐๏ธJavaScript 30 - Day 10
2021.11.26 -
[JS] ๐๏ธJavaScript 30 - Day 9
[JS] ๐๏ธJavaScript 30 - Day 9
2021.11.23 -
[JS] ๐๏ธJavaScript 30 - Day 8
[JS] ๐๏ธJavaScript 30 - Day 8
2021.11.22
๋๊ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.