前言
JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks、No Compilers、No Libraries、No Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
本日目標
靈活運用 video 元素的相關屬性、方法,實作出一個擁有快進快退、播放速度倍率、控制音量大小、拖拉時間軸功能的簡易影片播放器。

解析程式碼
HTML 部分
.player 代表整個影片播放器,包含影片(.player__video)、播放控制列(.player__controls)兩部分。
播放控制列內部又可細分為四個部分:
1. 影片播放的時間軸 : .progress、.progress__filled
2. 播放/暫停鈕 : .toggle
3. 音量/播放速度倍率 : .player__slider
4. 快進/快退 : .player__button
| 1 | <div class="player"> | 
JS 部分
首先,取得所有要用到 HTML 標籤並放到對應宣告的常數中。
| 1 | /*get element we need*/ | 
下面我們將各個播放器的功能一個個拆出來做 :
1. 影片的播放/暫停
功能目的 : 我們希望在點擊影片或播放/暫停鈕時,播放或暫停影片。
在video(影片)、toggle(播放/暫停鈕)上都註冊click 事件監聽器,觸發事件後用togglePlay()進行處理。
在togglePlay()裡,我們宣告一個常數method,經過條件判斷,當video.paused回傳 true 則 method = play;當video.paused回傳 false 則method = pause。
這邊有一個特殊的寫法video[method]();。舉例來說,當method = play則實際效果相當於video.play();。
| 1 | function togglePlay(){ | 
2. 更新播放/暫停圖示
功能目的 : 我們希望在影片播放/暫停的狀態下,同步更新圖示。
在video(影片)上註冊play 事件和pause 事件兩個監聽器並都以updateButton()進行事件處理。
在updateButton()裡,我們宣告一個常數icon指定當影片處於暫停狀態則icon = '►',接著利用toggle.textContent = icon修改按鈕的圖示,當影片處於播放狀態的處理也是用一樣的方式。
| 1 | function updateButton(){ | 
3. 影片的快進/快退
功能目的 : 我們希望在點擊快進/快退按鈕時,同步調整影片的時間軸。
在skipButtons(快進、快退按鈕)裡的所有button都註冊click 事件並以skip()進行事件處理。
在skip()裡,我們將video.currentTime(影片現在播放的時間點)加上我們要快進或快退的秒數。
由於video.currentTime本身是 float 型別,因此需要將this.dataset.skip(快進/快退的秒數)用parseFloat()轉換成float型別之後再進行運算。
| 1 | function skip(){ | 
4. 調整影片的播放速度(倍率)、音量大小
功能目的 : 我們希望在滑鼠在倍率或音量條上移動改變數值時,同步反映到video(影片)上。
在速度倍率和音量條上都註冊change 事件和mousemove 事件,分別在數值改變和滑鼠拖曳時觸發事件,之後用handleRangeUpdate()進行事件處理。
在handleRangeUpdate()裡,我們使用和之前一樣的特殊語法video[this.name] = this.value;對video的屬性值進行調整。舉例來說,如果this.name = volume、this.value = 0,則video[this.name] = this.value;的效果和video.volume = 0;一樣。
| 1 | function handleRangeUpdate(){ | 
5. 更新時間軸
功能目的 : 我們希望在影片播放的過程中,不斷地更新時間軸。
在video(影片)上註冊timeupdate 事件的監聽器,當影片的播放時間(currentTime)有變動就觸發事件,之後用handleProgress()進行事件處理。
在handleProgress()裡,我們宣告常數percent並放入video.currentTime(影片現在時間)除以video.duration(影片的總長度)再乘以100得到的比例值。
接著用progressBar.style.flexBasis = `${percent}%`;,用percent指定時間軸的長度佔比。
| 1 | function handleProgress(){ | 
6. 用拖拉的方式移動時間軸
功能目的 : 我們希望按住滑鼠拖或點擊時間軸的同時,更新video(影片)現在播放的時間。
宣告mousedown作為 flag 判斷現在是否有按住滑鼠。
我們在progress註冊click 事件、mousemove 事件、mousedown 事件、mouseup 事件監聽器。
觸發click 事件時,我們可以直接就以scrub(e)進行事件的處理。
但在觸發mousemove 事件時,我們需要先判斷是否有按住滑鼠,所以要借助mousedown 事件和mouseup 事件的幫忙,在mousedown 事件觸發地當下將 flag(mousedown) 設為 true,反之觸發mouseup 事件則將 flag(mousedown) 設為 false。最後用mousedown && scrub(e)判斷是否執行scrub(e),只有當flag(mousedown) 是 true 的時候,才接著執行scrub(e)完成事件處理。
在scrub(e)裡,我們宣告常數scrubTime放入將滑鼠在元素內部的X座標(e.offsetX)除以時間軸的長度(progress.offsetWidth)再乘以影片長度(video.duration)所得到要前往的時間點。最後將影片現在的時間(video.currentTime)指定為要前往的時間點(scrubTime)。
| 1 | function scrub(e){ | 
補充說明:
HTMLVideoElement繼承自HTMLMediaElement所以一些video元素的屬性都可以到HTMLMediaElement查詢。
使用HTMLElement.offsetWidth所取得的元素(element)寬度包括透過 CSS 設定的width、border、padding等等…。
補充資料:
JS一秒區分clientX,offsetX,screenX,pageX之間關係