iThome
鐵人賽
30天
今天要來打造影片標記系統的精隨之一,用YouTube Iframe API來建立一個播放器。或許有人會有疑問,為甚麼不用Iframe就好了呢?
因為為了能夠隨時抓取YouTube影片的撥放時間、打字編輯標記時也能夠控制影片撥放暫停等,所以使用YouTube Iframe API輔助來打造高控制度的撥放器。
這裡大致介紹一下功能,主要介紹一些function與event listener。
funtion:
function | 說明 |
---|---|
playVideo | 播放 |
pauseVideo | 暫停 |
stopVideo | 停止 |
seekTo | 跳到影片位置 (秒) |
mute | 靜音 |
unMute | 解除靜音 |
isMuted | 查看現在是否靜音 |
setVolume | 設定音量 |
getVolume | 取得目前音量 |
getCurrentTime | 取得現在影片撥放時間點 |
getDuration | 取得影片長度 |
事件:
事件名稱 | 說明 |
---|---|
onReady | 當影片準備好時 |
onStateChange | 當影片狀態改變時 (-1 尚未開始、0 結束、1 正在播放、2 暫停、3 緩衝、5 影片提示) |
onPlaybackQualityChange | 當影片畫質改變時 |
onPlaybackRateChange | 當影片播放速度改變時 |
onError | 當影片發生錯誤時 |
onApiChange | 當YouTube API改變時 |
以上是常用到的function與event,如果想查看更詳細的API的話可以到官方API文件去查詢喔!
第一個步驟先定義要放置player的容器 (id可以自己取名):
<div id="player"></div>
再來就是要先把Iframe的API載入進來 (當api準備好時會執行loadVideo):
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
window.onYouTubeIframeAPIReady = loadVideo; // onYouTubeIframeAPIReady will load the video after the script is loaded
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
定義loadVideo來撥放影片:
loadVideo = () => { // the Player object is created uniquely based on the id in props
const { videoid } = this.props;
player = new YT.Player(playerid, {
videoId: videoid,
playerVars: {
'playsinline': 1
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange,
'onPlaybackQualityChange': onPlaybackQualityChange,
},
})
介紹使用方法後就是來把這些步驟包成一個component來處理啦~
component第一次執行載入js:
useEffect(() => {
loadYTApi()
}, [])
const loadYTApi = () => {
if (!window.YT) { // If not, load the script asynchronously
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
window.onYouTubeIframeAPIReady = loadVideo; // onYouTubeIframeAPIReady will load the video after the script is loaded
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
} else { // If script is already there, load the video directly
this.loadVideo();
}
}
讓影片 (v) 與開始時間 (t) 由props傳入,並請使用者傳入設定player與其他監聽事件的function:
const { v, t, setPlayer, onPlayerReady, onPlayerStateChange, onPlaybackQualityChange, player, playerid } = props
const loadVideo = () => { // the Player object is created uniquely based on the id in props
setPlayer(new window.YT.Player(playerid, {
videoId: v,
playerVars: {
'start': parseFloat(t)
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange,
'onPlaybackQualityChange': onPlaybackQualityChange
},
}))
};
當傳入的v
與t
變動時,更新影片或時間點:
const { v, t, setPlayer } = props
// 當影片id變更時,載入新的影片
useEffect(() => {
player &&
player.loadVideoById({
videoId: v,
startSeconds: parseFloat(t)
})
}, [v])
// 開始時間改變時,跳轉到該時間
useEffect(() => {
player &&
player.seekTo(t)
}, [t])
把上面講述到的融合後,得到的component:
import React, { useEffect } from 'react'
export default function YouTubeIframe(props) {
const { v, t, setPlayer, onPlayerReady, onPlayerStateChange, onPlaybackQualityChange, playerid, player } = props
useEffect(() => {
loadYTApi()
}, [])
// 當影片id變更時,載入新的影片
useEffect(() => {
!!player &&
player.loadVideoById({
videoId: v,
startSeconds: parseFloat(t)
})
}, [v])
// 開始時間改變時,跳轉到該時間
useEffect(() => {
!!player &&
player.seekTo(t)
}, [t])
const loadYTApi = () => {
if (!window.YT) { // If not, load the script asynchronously
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
window.onYouTubeIframeAPIReady = loadVideo; // onYouTubeIframeAPIReady will load the video after the script is loaded
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
} else { // If script is already there, load the video directly
loadVideo();
}
}
const loadVideo = () => { // the Player object is created uniquely based on the id in props
setPlayer(new window.YT.Player(playerid, {
videoId: v,
playerVars: {
'start': parseFloat(t)
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange,
'onPlaybackQualityChange': onPlaybackQualityChange
},
}))
};
return (
<></>
)
}
明天就來試用看看並調整今天做的component吧!
看完今天Google的發表會,雖然又有些新的技術與新出來的手錶,雖然手錶真的很美,但感覺這次發表會沒有一種吸引人想買的衝動,硬體上面又感覺力不從心,只好等等看明年吧... (Pixel 6的苦主在這
附上專案:2022-iThomeIronman
對資安或Mapbox有興趣的話也可以觀看我們團隊的鐵人發文喔~