-
Notifications
You must be signed in to change notification settings - Fork 2
[혜원] 3-CustomVideoPlayer #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # 목차 | ||
| - `<video>` 요소 | ||
| - loadedmetadata | ||
| - Event listener 중첩 | ||
|
|
||
| ## `<video>` 요소 | ||
| - - - | ||
| - 비디오 플레이백을 지원하는 미디어 플레이어를 문서에 삽입한다. | ||
|
|
||
| ### Properties | ||
| - - - | ||
| <br /> | Description | ||
| |--|--| | ||
| src | 삽입할 동영상의 주소 | ||
| poster | 동영상 재생/탐색하기 전까지 출력되는 포스터 프레임 주소 | ||
| currentTime | 현재 재생 위치 | ||
| duration | video 전체 길이 | ||
|
|
||
| ### Methods | ||
| - - - | ||
| <br /> | Description | ||
| |--|--| | ||
| play() | 재생 시작 | ||
| pause() | 일시정지 | ||
|
|
||
| ### Events | ||
| - - - | ||
| <br /> | Description | ||
| |--|--| | ||
| ended | 현재 video 가 끝났을 때 | ||
| loadedmetadata | 브라우저가 video 에 대한 메타 데이터를 로드했을 때 | ||
| pause | video 가 일시정지 되었을 때 | ||
| play | video 가 시작되었거나 더이상 일시정지 되지 않았을 때 | ||
| timeupdate | 현재 재생위치가 변경되었을 때 | ||
|
|
||
| ### 내 코드 | ||
| ```javascript | ||
| if($video.paused){ | ||
| $video.play() | ||
| $playButton.classList.replace('fa-play', 'fa-pause') | ||
| return | ||
| } | ||
| ``` | ||
| ```javascript | ||
| $video.currentTime = Math.trunc(currentPosition) | ||
| ``` | ||
|
|
||
| ## loadedmetadata | ||
| - - - | ||
| - video 의 메타 데이터가 로드되면 해당 이벤트가 발생한다. | ||
| - video 의 전체 길이를 가져오는 duration 속성은, **video 에 대한 로드가 끝나지 않은 시점에서 호출하면 NaN 값이 출력된다.** | ||
| - 따라서 로드가 끝난 시점에서 duration 값을 호출하고 싶다면, **'lodaedmetadata'** 이벤트 리스너를 등록해야 한다. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 duration NaN 떴었는데 비디오 정보를 불러오기전에 접근했어서 그랬던거였어요. 굳입니다!! |
||
|
|
||
| ### 내 코드 | ||
| - - - | ||
| ```javascript | ||
| $video.addEventListener('loadedmetadata', function() { | ||
| const duration = $video.duration | ||
| ``` | ||
|
|
||
| ## Event listener 중첩 | ||
| - - - | ||
| ### 내 코드 | ||
| ```javascript | ||
| $video.addEventListener('loadedmetadata', function() { | ||
| const duration = $video.duration | ||
| const unit = ((parseFloat($progressBar.max) - parseFloat($progressBar.min)) / duration) / 4 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unit이 정확히 어떤 역할을 하는지 알기 힘든거 같아요. 어떤 역할을 하나요? |
||
|
|
||
| $video.addEventListener('timeupdate', e => { | ||
| $progressBar.value = parseFloat($progressBar.value) + unit | ||
| // 생략 | ||
| }) | ||
|
|
||
| $progressBar.addEventListener('change', e => { | ||
| const currentPosition = duration * $progressBar.value / (parseFloat($progressBar.max) | ||
| // 생략 | ||
| }) | ||
| }) | ||
| ``` | ||
| loadedmetadata 항목의 설명에서도 언급했듯이, duration 은 메타데이터가 로드된 이후에 호출하는 것이 안전하다. 그럼 duration 과 관련된 변수를 사용하는 event listener 들은 loadedmetadata 로 duration 을 가져온 이후에 실행됨이 보장되어야 할 것 같은데, 그렇게 할 만한 방법이 중첩밖에 생각나지 않았다... 이게 효율적인 방법인지는 잘 모르겠다... (nested event listener 이런 식으로 서치해 봤는데 좋은 정보를 얻지 못했다.) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제 생각인데 문제가 되는게 메타데이터 불러오기 전에 비디오 데이터를 접근하려해서인거 같은데, 이벤트 리스너는 결국 동영상이 재생될때 작동하고 동영상을 재생할때는 메타데이터가 다 불러와졌으니 loadedonmetadata를 사용하지 않아도 괜찮지 않을까요...? 제 개인적인 생각이여서 다같이 고민해보면 좋을거 같아요. |
||
|
|
||
| ## 참조 | ||
| - - - | ||
| - `<video>` 요소 <br /> | ||
| https://developer.mozilla.org/ko/docs/Web/HTML/Element/Video <br /> | ||
| https://html.spec.whatwg.org/#the-video-element <br /> | ||
| https://www.w3schools.com/tags/ref_av_dom.asp <br /> | ||
| - loadedmetadata <br /> | ||
| https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ivory82&logNo=220096880567 <br /> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| /* SOURCE: https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/ */ | ||
|
|
||
| input[type='range'] { | ||
| -webkit-appearance: none; /* Hides the slider so that custom slider can be made */ | ||
| width: 100%; /* Specific width is required for Firefox. */ | ||
| background: transparent; /* Otherwise white in Chrome */ | ||
| } | ||
|
|
||
| input[type='range']::-webkit-slider-thumb { | ||
| -webkit-appearance: none; | ||
| } | ||
|
|
||
| input[type='range']:focus { | ||
| outline: none; /* Removes the blue border. You should probably do some kind of focus styling for accessibility reasons though. */ | ||
| } | ||
|
|
||
| input[type='range']::-ms-track { | ||
| width: 100%; | ||
| cursor: pointer; | ||
|
|
||
| /* Hides the slider so custom styles can be added */ | ||
| background: transparent; | ||
| border-color: transparent; | ||
| color: transparent; | ||
| } | ||
|
|
||
| /* Special styling for WebKit/Blink */ | ||
| input[type='range']::-webkit-slider-thumb { | ||
| -webkit-appearance: none; | ||
| border: 1px solid #000000; | ||
| height: 36px; | ||
| width: 16px; | ||
| border-radius: 3px; | ||
| background: #ffffff; | ||
| cursor: pointer; | ||
| margin-top: -14px; /* You need to specify a margin in Chrome, but in Firefox and IE it is automatic */ | ||
| box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; /* Add cool effects to your sliders! */ | ||
| } | ||
|
|
||
| /* All the same stuff for Firefox */ | ||
| input[type='range']::-moz-range-thumb { | ||
| box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||
| border: 1px solid #000000; | ||
| height: 36px; | ||
| width: 16px; | ||
| border-radius: 3px; | ||
| background: #ffffff; | ||
| cursor: pointer; | ||
| } | ||
|
|
||
| /* All the same stuff for IE */ | ||
| input[type='range']::-ms-thumb { | ||
| box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||
| border: 1px solid #000000; | ||
| height: 36px; | ||
| width: 16px; | ||
| border-radius: 3px; | ||
| background: #ffffff; | ||
| cursor: pointer; | ||
| } | ||
|
|
||
| input[type='range']::-webkit-slider-runnable-track { | ||
| width: 100%; | ||
| height: 8.4px; | ||
| cursor: pointer; | ||
| box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||
| background: #3071a9; | ||
| border-radius: 1.3px; | ||
| border: 0.2px solid #010101; | ||
| } | ||
|
|
||
| input[type='range']:focus::-webkit-slider-runnable-track { | ||
| background: #367ebd; | ||
| } | ||
|
|
||
| input[type='range']::-moz-range-track { | ||
| width: 100%; | ||
| height: 8.4px; | ||
| cursor: pointer; | ||
| box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||
| background: #3071a9; | ||
| border-radius: 1.3px; | ||
| border: 0.2px solid #010101; | ||
| } | ||
|
|
||
| input[type='range']::-ms-track { | ||
| width: 100%; | ||
| height: 8.4px; | ||
| cursor: pointer; | ||
| background: transparent; | ||
| border-color: transparent; | ||
| border-width: 16px 0; | ||
| color: transparent; | ||
| } | ||
| input[type='range']::-ms-fill-lower { | ||
| background: #2a6495; | ||
| border: 0.2px solid #010101; | ||
| border-radius: 2.6px; | ||
| box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||
| } | ||
| input[type='range']:focus::-ms-fill-lower { | ||
| background: #3071a9; | ||
| } | ||
| input[type='range']::-ms-fill-upper { | ||
| background: #3071a9; | ||
| border: 0.2px solid #010101; | ||
| border-radius: 2.6px; | ||
| box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||
| } | ||
| input[type='range']:focus::-ms-fill-upper { | ||
| background: #367ebd; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| @import url('https://fonts.googleapis.com/css?family=Questrial&display=swap'); | ||
|
|
||
| * { | ||
| box-sizing: border-box; | ||
| } | ||
|
|
||
| body { | ||
| font-family: 'Questrial', sans-serif; | ||
| background-color: #666; | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| justify-content: center; | ||
| max-height: 100vh; | ||
| margin: 0; | ||
| } | ||
|
|
||
| h1 { | ||
| color: #fff; | ||
| } | ||
|
|
||
| .screen { | ||
| cursor: pointer; | ||
| width: 60%; | ||
| background-color: #000 !important; | ||
| border-top-left-radius: 10px; | ||
| border-top-right-radius: 10px; | ||
| } | ||
|
|
||
| .controls { | ||
| background: #333; | ||
| color: #fff; | ||
| width: 60%; | ||
| border-bottom-left-radius: 10px; | ||
| border-bottom-right-radius: 10px; | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| padding: 10px; | ||
| } | ||
|
|
||
| .controls .btn { | ||
| border: 0; | ||
| background: transparent; | ||
| cursor: pointer; | ||
| } | ||
|
|
||
| .controls .fa-play { | ||
| color: #28a745; | ||
| } | ||
|
|
||
| .controls .fa-stop { | ||
| color: #dc3545; | ||
| } | ||
|
|
||
| .controls .fa-pause { | ||
| color: #fff; | ||
| } | ||
|
|
||
| .controls .timestamp { | ||
| color: #fff; | ||
| font-weight: bold; | ||
| margin-left: 10px; | ||
| } | ||
|
|
||
| .btn:focus { | ||
| outline: 0; | ||
| } | ||
|
|
||
| @media (max-width: 800px) { | ||
| .screen, | ||
| .controls { | ||
| width: 90%; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <meta http-equiv="X-UA-Compatible" content="ie=edge" /> | ||
| <title>Custom Video Player</title> | ||
| <link | ||
| href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" | ||
| rel="stylesheet" | ||
| integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" | ||
| crossorigin="anonymous" | ||
| /> | ||
| <link rel="stylesheet" href="css/progress.css" /> | ||
| <link rel="stylesheet" href="css/style.css" /> | ||
| </head> | ||
| <body> | ||
| <h1>Custom Video Player</h1> | ||
| <video | ||
| src="videos/gone.mp4" | ||
| id="video" | ||
| class="screen" | ||
| poster="img/poster.png" | ||
| onclick="videoPlay()" | ||
| ></video> | ||
| <div class="controls"> | ||
| <button class="btn" id="play" onclick="videoPlay()"> | ||
| <i class="fa fa-play fa-2x"></i> | ||
| </button> | ||
| <button class="btn" id="stop" onclick="stop()"> | ||
| <i class="fa fa-stop fa-2x"></i> | ||
| </button> | ||
| <input | ||
| type="range" | ||
| id="progress" | ||
| class="progress" | ||
| min="0" | ||
| max="100" | ||
| step="0.1" | ||
| value="0" | ||
| /> | ||
| <span class="timestamp" id="timestamp">00:00</span> | ||
| </div> | ||
|
|
||
| <script src="script.js"></script> | ||
| </body> | ||
| </html> | ||
|
|
||
| <!-- https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs --> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| const $video = document.getElementById('video') | ||
| const $playButton = document.querySelector('.controls .btn#play .fa') | ||
| const $progressBar = document.getElementById('progress') | ||
| const $timestamp = document.getElementById('timestamp') | ||
|
|
||
| function videoPlay() { | ||
| if($video.paused){ | ||
| $video.play() | ||
| $playButton.classList.replace('fa-play', 'fa-pause') | ||
| return | ||
| } | ||
| if($video.played) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if를 두번 쓸필요 없이 위에서 early return 해줬으니 paused가 아닌경우는 if 없이 해줘도 될거 같아요 아마도...?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 그렇게 생각합니다! |
||
| $video.pause() | ||
| $playButton.classList.replace('fa-pause', 'fa-play') | ||
| return | ||
| } | ||
| } | ||
|
|
||
| function stop() { | ||
| $video.currentTime = 0 | ||
| $video.pause() | ||
| if($playButton.classList.contains('fa-pause')){ | ||
| $playButton.classList.replace('fa-pause', 'fa-play') | ||
| } | ||
| $progressBar.value = 0 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. video.curretTime = 0 해주면 value도 같이 0 되지않나요? 잘 모르겠어서...
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해보니까 value도 같이 0 되는거 같아요! |
||
| } | ||
|
|
||
| $video.addEventListener('loadedmetadata', function() { | ||
| const duration = $video.duration | ||
| const unit = ((parseFloat($progressBar.max) - parseFloat($progressBar.min)) / duration) / 4 | ||
|
|
||
| $video.addEventListener('timeupdate', e => { | ||
| $progressBar.value = parseFloat($progressBar.value) + unit | ||
| const min = Math.trunc(Math.trunc($video.currentTime) / 60) | ||
| const sec = Math.trunc(Math.trunc($video.currentTime) % 60) | ||
| const time = ((min >= 10 ) ? `${min}` : ("0" + `${min}`)) + ((sec >= 10) ? `:${sec}` : (":0" + `${sec}`)) | ||
| $timestamp.textContent = time | ||
| if($video.ended) { | ||
| $playButton.classList.replace('fa-pause', 'fa-play') | ||
| } | ||
| }) | ||
|
|
||
| $progressBar.addEventListener('change', e => { | ||
| const currentPosition = duration * $progressBar.value / (parseFloat($progressBar.max) - parseFloat($progressBar.min)) | ||
| $video.currentTime = Math.trunc(currentPosition) | ||
| }) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Math.trunc는 처음 알았네요. 소수점을 다 떼주는 역할을 하네요. Math.floor 와 같은 역할을 하는거 맞겠죠?