[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