https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs
HTML
<!DOCTYPE HTML>
<html lang="en-gb">
<head>
<meta charset="utf-8">
<title>Video player example</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="player">
<video controls>
<source src="video/sintel-short.mp4" type="video/mp4">
<source src="video/sintel-short.mp4" type="video/webm">
<!-- fallback content here -->
</video>
<div class="controls">
<button class="play" data-icon="P" aria-label="play pause toggle"></button>
<button class="stop" data-icon="S" aria-label="stop"></button>
<div class="timer">
<div></div>
<span aria-label="timer">00:00</span>
</div>
<button class="rwd" data-icon="B" aria-label="rewind"></button>
<button class="fwd" data-icon="F" aria-label="fast forward"></button>
</div>
</div>
<p>Sintel © copyright Blender Foundation | <a href="http://www.sintel.org">www.sintel.org</a>.</p>
<script src="custom-player.js"></script>
</body>
</html>
- player 클래스 <div>를 이용해서 전체 비디오 플레이어를 감싸준다
(-> 나중에 필요하면 하나의 단위로 스타일 가능. 아무래도 display:flex를 줘서 비디오, control을 정렬할 때 사용할 것 같다.)
- <video> element는 두 개의 <source> elements를 포함하고 있다.
(-> browser에 따라 다른 포맷이 로드된다.)
- control 파트
- <button> 4개 - play/pause, stop, rewind, fast forward
- <button>에는 class name, data-icon(버튼 아이콘 결정하는 속성), aria-label 속성 (어떤 버튼인지 알려줌)
- timer <div>는 비디오가 플레이 될 때, 경과된 시간을 알려준다. span은 경과된 시간을 '분:초'로 알려주고, timer 속 div에는 시간이 지남에 따라 길어지는 horizontal indicator bar를 만들 것이다.
CSS
video {
border: 1px solid black;
}
p {
position: absolute;
top: 310px;
}
.player {
position: absolute;
}
- <video> element에 1px 검정색 solid border
- 가장 아래에 있는 paragraph <p> element의 position:absolute;
.controls {
background-color: black;
box-shadow: 3px 3px 5px black;
border-radius: 10px;
width: 400px;
position: absolute;
bottom: 20px;
left: 50%;
margin-left: -200px;
display: flex;
visibility: hidden;
opacity: 0.5;
transition: 1s all;
}
.player:hover .controls, player:focus .controls {
opacity: 1;
}
- control은 검정색 배경에 shadow를 주고, border-radius:10px씩. 너비는 400px;
- position:absolute;로 하면 가장 가까운 parent Element인 player <div>를 기준으로 bottom:20px, left:50%, margin-left:-200px; 배치
- visibility:hidden -> 나중에 javascript에서 visible로 바꾸고,<video> elemnt의 controls 속성을 제거 (만약 자바스크립트가 로드가 안된다 하더라도, 유저들은 native controls을 이용해서 여전히 비디오를 볼 수 있다.)
- opacity:0.5를 디폴트 값으로 줬다. -> 비디오 보는 동안 덜 방해가 되도록. (player에 hover over하거나 focus하면 opacity:1되도록)
- display:flex를 줘서 control속 버튼들이 일렬
(참고 : visibility:hidden occupies space and doesn't consume clicks.
opacity:0 occupies space and consumes clicks.)
@font-face {
font-family: 'HeydingsControlsRegular';
src: url('fonts/heydings_controls-webfont.eot');
src: url('fonts/heydings_controls-webfont.eot?#iefix') format('embedded-opentype'),
url('fonts/heydings_controls-webfont.woff') format('woff'),
url('fonts/heydings_controls-webfont.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
button:before {
font-family: HeydingsControlsRegular;
font-size: 20px;
position: relative;
content: attr(data-icon);
color: #aaa;
text-shadow: 1px 1px 0px black;
}
- @font-face는 custom web font를 import. (all the characters of the alphabet equate to common icons you might want to use in an application.) 아까 HTML에서 P는 Play 아이콘, S는 Stop 아이콘이 되는 것 같다.
- :before selector를 사용해서 각 <button>앞에 content를 배치
- We use the content property to set the content to be displayed in each case to be equal to the contents of the data-icon attribute. In the case of our play button, data-icon contains a capital "P".
- We apply the custom web font to our buttons using font-family. In this font, "P" is actually a "play" icon, so therefore the play button has a "play" icon displayed on it.
(font-face를 처음 써보는 거라 정확히 이해를 못한 상황이고 그래서 괜한 오역이 생길까 일단 MDN 설명을 그대로 따왔다.)
(Icon Fonts: Icon fonts are very cool for many reasons — cutting down on HTTP requests because you don't need to download those icons as image files, great scalability, and the fact that you can use text properties to style them — like color and text-shadow.)
.timer {
line-height: 38px;
font-size: 10px;
font-family: monospace;
text-shadow: 1px 1px 0px black;
color: white;
flex: 5;
position: relative;
}
.timer div {
position: absolute;
background-color: rgba(255,255,255,0.2);
left: 0;
top: 0;
width: 0;
height: 38px;
z-index: 2;
}
.timer span {
position: absolute;
z-index: 3;
left: 19px;
}
- .timer <div>에 flex:5를 줘서 control bar의 너비 대부분을 차지하도록 했다. 그리고 position:relative를 줘서 .timer <div> 안에 있는 elements들을 <body> 기준이 아니라 .timer <div>의 바운더리 기준으로 위치시킬 수 있게 했다.
- inner idv는 position:absolute, left:0, top:0으로 해서 .timer <div>의 가장 위, 왼쪽 가에 붙도록 했다. 그리고 initial width를 0으로 설정해서 아직은 아예 보이지 않는다. 비디오가 플레이 되면 자바스크립트를 이용해서 width가 늘어나게 할 것.
- <span>또한 .timer <div> 속에서 배치되도록 position:absolute를 주고 위치는 .timer <div> 왼쪽 19px
- inner <div>와 <span>에 z-index가 있다. timer가 top에 배치, 그 아래 innder <div>. 그렇게 해야 모든 information을 볼 수 있다. (one box is not obscuring another.
JavaScript
const media = document.querySelector('video');
const controls = document.querySelector('.controls');
const play = document.querySelector('.play');
const stop = document.querySelector('.stop');
const rwd = document.querySelector('.rwd');
const fwd = document.querySelector('.fwd');
const timerWrapper = document.querySelector('.timer');
const timer = document.querySelector('.timer span');
const timerBar = document.querySelector('.timer div');
create constants to hold references to all the objects we want to manipulate
(영어로 쓴 이유는 나중에 깃헙 커밋할 때 참고하려고 ㅋㅋ)
media.removeAttribute('controls');
controls.style.visibility = 'visible';
이 두 코드를 넣어주면 video의 default browser controls를 없애주고
아까 CSS로 만든 custom controls가 보이게 해준다.
Playing and pausing the video
play.addEventListener('click', playPauseMedia);
function playPauseMedia() {
if(media.paused) {
play.setAttribute('data-icon','u');
media.play();
} else {
play.setAttribute('data-icon','P');
media.pause();
}
플레이 버튼을 클릭한 경우 playPauseMedia 함수가 실행된다.
만약에 비디오가 paused 상태였다면? (다시 비디오를 플레이 시키고 싶다는 뜻이니까) media.play();
(If it is paused, we set the data-icon attribute value on the play button to "u", which is a "paused" icon)
아니라면, 즉 비디오가 play 상태였다면? (비디오를 일시정지 하고 싶다는 뜻이니까) media.pause();
Stopping the Video
stop.addEventListener('click', stopMedia);
media.addEventListener('ended', stopMedia);
stop버튼을 클릭하거나 media가 끝이나면 stopMedia 함수 실행
function stopMedia() {
media.pause();
media.currentTime = 0;
play.setAttribute('data-icon','P');
}
HTMLMediaElementAPI에 stop() method가 없다. 하지만 비디오를 stop시키는 방법은 간단한데
media.pause();를 해서 비디오를 정지 시키고 media.currentTime=0;으로 해서 비디오를 다시 시작 시점으로 돌려주면 stop과 같은 효과!
(currentTime에 초단위 value를 주면 비디오 그 시점으로 점프)
Seeking back and forth
rwd.addEventListener('click', mediaBackward);
fwd.addEventListener('click', mediaForward);
let intervalFwd;
let intervalRwd;
function mediaBackward() {
clearInterval(intervalFwd);
fwd.classList.remove('active');
if(rwd.classList.contains('active')) {
rwd.classList.remove('active');
clearInterval(intervalRwd);
media.play();
} else {
rwd.classList.add('active');
media.pause();
intervalRwd = setInterval(windBackward, 200);
}
}
function mediaForward() {
clearInterval(intervalRwd);
rwd.classList.remove('active');
if(fwd.classList.contains('active')) {
fwd.classList.remove('active');
clearInterval(intervalFwd);
media.play();
} else {
fwd.classList.add('active');
media.pause();
intervalFwd = setInterval(windForward, 200);
}
}
1. fast forward functionality에 의해 설정된 모든 class와 interval 값을 clear 한다.
이렇게 하는 이유는,
fast forward button을 누른 후에 rewind 버튼을 누르는 경우, fast forward functionality를 모두 cancel하고
그걸 rewind functionality로 replace 하고 싶기 때문이다.
2. if문을 이용해서 rewind 버튼에 active 클래스가 들어가 있는지 확인한다.
active 클래스가 이미 들어가 있다면 rewind가 이미 한 번 눌려졌다는 뜻이다.
3. rewind 버튼에 active가 이미 들어가있다면, 그걸 classList.remove()를 이용해서 제거하고, 처음 버튼이 눌려졌을 때 세팅된 interval 값을 clear해준다. 그리고 media.play();를 해서 rewind가 cancel되고 비디오가 다시 정상적으로 플레이 되도록 한다.
4. active 클래스가 들어가 있지 않다면, active 클래스를 rewind 버튼에 넣어준다. 그리고 비디오를 pause 시켜준다.
그런 다음 제일 위에 선언한 intervalRwd 변수에 setInterval()을 넣어준다.
intervalRwd = setInterval(windBackward, 200);
setInterval이 invoke되면, 첫번째 parameter로 주어진 함수 windBackward를 200 milliseconds마다 실행시켜준다.
setInterval을 멈추려면 clearInterval()함수를 호출해야한다.
if(rwd.classList.contains('active')) {
rwd.classList.remove('active');
clearInterval(intervalRwd);
media.play();
}else{...}
아까 if문 속 clearInterval이 이때 쓰이는 것!
5. 마지막으로 setInterval함수가 invoke하는 windBackward(), windForward() 함수를 정의해야 한다.
function windBackward() {
if(media.currentTime <= 3) {
rwd.classList.remove('active');
clearInterval(intervalRwd);
stopMedia();
} else {
media.currentTime -= 3;
}
}
function windForward() {
if(media.currentTime >= media.duration - 3) {
fwd.classList.remove('active');
clearInterval(intervalFwd);
stopMedia();
} else {
media.currentTime += 3;
}
}
if문을 이용해서 currentTime이 3초보다 적은지 확인한다. 즉, 3초 더 rewind를 했을 때 video 시작 그 전으로 가지 않는지 확인. 만약 그렇다면, stopMedia를 호출해서 비디오를 멈추고 rewind button의 active 클래스를 제거하고, intervalRwd값을 clear해서 rewind functionality가 멈추도록 한다. (이 코드를 넣어주지 않으면 비디오는 계속해서 rewind된다).
currentTime이 3초 안으로 남아있는게 아니라면, currentTime에서 3초를 빼준다. 그렇게 하면 비디오를 3초 전으로 rewind해준다(이걸 매 200milliseconds마다 해줌)
Updating the elapsed time
media.addEventListener('timeupdate', setTime);
function setTime() {
let minutes = Math.floor(media.currentTime / 60);
let seconds = Math.floor(media.currentTime - minutes * 60);
let minuteValue;
let secondValue;
if (minutes < 10) {
minuteValue = '0' + minutes;
} else {
minuteValue = minutes;
}
if (seconds < 10) {
secondValue = '0' + seconds;
} else {
secondValue = seconds;
}
let mediaTime = minuteValue + ':' + secondValue;
timer.textContent = mediaTime;
let barLength = timerWrapper.clientWidth * (media.currentTime/media.duration);
timerBar.style.width = barLength + 'px';
}
1. currentTime을 이용해서 minutes와 seconds 값 가져온다.
2. 그리고 minuteValue와 secondValue도 initialize해준다
3. number of minutes and seconds가 10보다 작다면 앞에 0을 붙여준다.
4. mediaTime이라는 변수에 'minuteValue : secondValue' 넣어서 실제 시계처럼 보이게 하고 timer textContent에 넣어준다. -> UI에 display
5. inner <div>에 넣어줄 길이 : outer div의 width를 가져와서 (media.currentTime/media.duration)나눈 값을 곱해준다.
6. timerBar.style.width 값에 barLength + 'px'을 해줘서 실제 css로 style주는 것처럼!
Fixing play and pause
rewind나 fast forward functionality가 active인 동안 play/pause 버튼이나 stop 버튼이 눌려지면 제대로 작동하지 않는다.
rwd.classList.remove('active');
fwd.classList.remove('active');
clearInterval(intervalRwd);
clearInterval(intervalFwd);
stopMedia(), playPauseMedia() 함수 안에 이 코드를 넣어준다.
(if문 앞에 넣어줘야함)
아니면 아예 분리된 함수를 만들어 줘서 필요한 곳에서 호출해도 된다.
'Mini Projects' 카테고리의 다른 글
Wealth List (1) (0) | 2020.07.16 |
---|---|
Custom Exchage Rate Calculator (fetch API, 3rd Party API) (0) | 2020.07.16 |
Movie Seat Booking (4) make Screen 3D (0) | 2020.07.10 |
Movie Seat Booking (2) - CSS colors (0) | 2020.07.09 |
Movie Seat Booking (1) HTML (0) | 2020.07.09 |