JAVASCRIPT小型專案練習13 Custom HTML5 Video Player(JS30-11)

JS30又來啦~!!要趕在新學期開始前把一些之前落後的東西補上~!
學習低潮這種東西真是可怕~還好我逃出來了 之後再寫分文章分享那段時間的點滴:P
進入正題吧!這是要處理的是客製化video player,使用者可以操作暫停、快進等常見的影片功能,算是蠻有意思的主題,內容同時也較多,需要多一點時間吸收。

HTML

<body>
   
</div> <button class="player__button toggle" title="Toggle Play">►</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="25" class="player__button">25s »</button> </div> </div> </body>

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 .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

// ---------------選取元素---------------

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 skipButtons = player.querySelectorAll('[data-skip]');
const ranges = player.querySelectorAll('.player__slider');

// ---------------建立功能---------------

// 播放功能,注意video僅有paused屬性而沒有play屬性,所以用paused來操作按鈕
function toggle_play() {
  return video.paused ? video.play() : video.pause()
}

// 更新按鈕,撥放時將按鈕變更為暫停鍵,反之則恢復為撥放紐
function update_button() {
  toggle.textContent = video.paused ? '►' : '❚ ❚';
}

// skip按鈕,利用data-attribute去操作元素 比用value來得優秀一些
function skip() {
  video.currentTime += parseFloat(this.dataset.skip)
}

// 音量、播放速度控制
function handleRangeBar() {
  video[this.name] = this.value;
}

// 進度條控制-隨時間更新
function handleProgressBar() {
  const percent = (video.currentTime / video.duration) * 100
  progressBar.style.flexBasis = `${percent}%`
}

// 進度條控制-滑鼠拖曳
function scrub(e) {
  console.log(e.offsetX);
  const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
  video.currentTime = scrubTime
}

// ---------------設定監聽器---------------

//播放
toggle.addEventListener('click', toggle_play)
video.addEventListener('play', update_button)
video.addEventListener('pause', update_button)
video.addEventListener('timeupdate', handleProgressBar)

//快進、快退
skipButtons.forEach(buttons => {
  buttons.addEventListener('click', skip)
})

//音量、播放速度控制
ranges.forEach(ranges => {
  ranges.addEventListener('change', handleRangeBar)
  ranges.addEventListener('mousemove', handleRangeBar)
})

//進度條控制
let mouseDown = false
progress.addEventListener('click', scrub)
progress.addEventListener('mousedown', () => mouseDown = true)
progress.addEventListener('mouseup', () => mouseDown = false)
progress.addEventListener('mousemove', (e) => mouseDown && scrub(e)) 

學習重點

  • 複習change & mousemove事件的配合,兩者配合使用才能即時顯示變更
  • 複習mousedown,mouseup事件的用法,建立一個flag會好用很多
  • 了解如何運用event.offsetX/Y 以及 offsetWidth
  • 了解video的常用屬性與方法(currentTime,paused,pause,play,duration等),特別注意video僅具有paused屬性
  • 了解 && 的進階用法 ,若運算子左方為true則回傳右方 反之回傳左方 ||運算子則相反

上方學習重點補充說明
&&運算子舉例
console.log(1 && 2) // 2
console.log(0 && 4) // 4
利用這樣的方法可以將函式簡寫

before

progress.addEventListener('mousemove',()=>{
  if (mouseDown) {
    scrub(e)
  }
})
after progress.addEventListener('mousemove', (e) => mouseDown && scrub(e))

最終成品

發表留言