瀏覽器中視頻播放器實現(xiàn)的基本思路與代碼
前言
自定義個播放器,組件都是用的原生的,所以有點丑,重點關(guān)注業(yè)務邏輯吧。
界面大概長下面這個樣子。
大家可以看著界面,在腦海中想一下自己會如何實現(xiàn)這個視頻播放器。可以問自己以下幾個問題:
- 這個組件會接受那些props
- 如何獲取視頻的基本信息,包括時長,分辨率等
- 暫停、播放如何實現(xiàn)
- 拖動進度條的邏輯如何實現(xiàn)
- 視頻初始加載顯示loading如何處理
- 視頻播放過程中卡頓顯示loading如何處理
瀏覽器中的音視頻知識總結(jié)
視頻編碼
視頻,其實就是一系列連續(xù)播放的圖片,如果1s鐘播放24張圖片,視頻的幀率就是24。
如果視頻的的尺寸是1920*1080,即一張圖片的尺寸是1920*1080*3 bytes,乘3是因為一個像素點3個比特,分別存放rbg,那么一個30分鐘的視頻所需要的存儲空間如下:
//1s視頻需要的存儲空間為: 1920*1080*3*24 bytes //30min視頻需要的存儲空間: 1920*1080*3*24 * 60*30=250.28GB
可以看到,非常大,所以視頻需要壓縮,于是就有了編碼(codec)的概念。視頻的編碼格式可以理解為壓縮格式,不同的編碼格式壓縮率不同,常見的編碼格式有 h264,mpeg4,vp8等。
此外,需要注意的一點是,因為編碼格式是有版權(quán)問題的,所以不同的瀏覽器支持的編碼格式不同,所以就會出現(xiàn)有些編碼格式的視頻在某些瀏覽器播放不了,或者只有聲音沒有畫面的情況。
我們前端開發(fā)只需要記住一點,主流瀏覽器支持的視頻編碼格式是h264。
封裝格式
一個視頻文件內(nèi)會包含視頻流和音頻流,還有一些元數(shù)據(jù),例如分辨率信息,標題等,這個文件的格式我們稱為封裝格式,可以理解為打包格式,常見的mp4,webp,mov,mpeg等都是封裝格式。
封裝格式往往是與視頻編碼無關(guān)的,一個mp4文件,里面的視頻流編碼可以是h264,也可以是mpeg,所以就會出現(xiàn),同樣都是mp4文件,有的瀏覽器可以放,有的瀏覽器就放不了的問題。
音視頻標簽
<video controls poster="1.jpg" src="1.mp4" loop muted></video> <audio controls src="1.mp3"></audio>
src指定資源地址,poster為視頻指定一張封面圖,controls表示瀏覽器應該顯示UI控件(每個瀏覽器樣式不同)
常用屬性
下面是video和audio的通用屬性

常用事件
video和audio通用事件

常用方法
- play() 控制視頻開始播放
- pause() 控制視頻暫停播放
有了上述的屬性、事件及方法,我們就可以做很多事了,比如自定義播放器,使用播放器本地預覽視頻等。
整體思路如下
- 該組件接收一個視頻的src作為參數(shù)
- 監(jiān)聽onLoadedMetadata事件,獲取視頻時長(duration),真實寬高(videoWidth,videoHeight)
- 點擊播放/暫停時,調(diào)用視頻元素的play/pause方法
- 播放時,監(jiān)聽onTimeUpdate,獲取當前播放時間(currentTime),計算出進度條的進度
- 拖動進度條,設置視頻當前播放時間,與步驟4相反
- 視頻初始加載時,顯示loading。即組件初次渲染時,loading屬性默認為true,即顯示加載效果,當視頻元數(shù)據(jù)加載時,取消loading
- 視頻卡頓時,顯示loading。該功能的實現(xiàn)是監(jiān)聽的onWaiting事件,不卡頓時,取消loading,監(jiān)聽的是onCanPlay事件
代碼實現(xiàn)
代碼是用React實現(xiàn)的,用Vue也是一樣的,關(guān)注業(yè)務邏輯即可。如果大家有需求,我可以再更新下這篇文章,添加上Vue的代碼。
注意:dom原生事件在react中需要前面加個on,然后寫成駝峰的形式
function formatDuration(duration) {
var sec_num = parseInt(duration, 10); // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (hours < 10) {hours = "0"+hours;}
if (minutes < 10) {minutes = "0"+minutes;}
if (seconds < 10) {seconds = "0"+seconds;}
return hours+':'+minutes+':'+seconds;
}
import React, { createRef, useState } from 'react'
import './VideoPlayer.css'
function VideoPlayer({src}){
const videoDom=createRef()
// 視頻當前播放時間
const [curTime,setCurTime]=useState(0)
// 視頻時長
const [duration,setDuration]=useState(0)
// 視頻狀態(tài),是否暫停
const [isPause,setPause]=useState(true)
// 視頻真是尺寸
const [size,setSize]=useState({width:1920,height:1080})
// 視頻加載中
const [waiting,setWaiting]=useState(true)
// 視頻元數(shù)據(jù)加載成功
const onLoad=(e)=>{
const {duration,videoWidth,videoHeight}=e.target
setDuration(duration)
setSize({width:videoWidth,height:videoHeight})
setWaiting(false)
}
// 控制播放暫停
const handlePlay=(play)=>{
const v=videoDom.current
if(play){
setPause(false)
v.play()
}else{
setPause(true)
v.pause()
}
}
// 拖動slider時改變視頻currentTime
const onSliderChange=(e)=>{
setCurTime(e.target.value)
videoDom.current.currentTime=e.target.value
}
// 監(jiān)聽video timeupdate
const onTimeUpdate=()=>{
const v=videoDom.current
setCurTime(v.currentTime)
if(v.ended){
handlePlay(false)
v.currentTime=0
}
}
// 卡頓時,顯示加載中提示
const onWaiting=()=>{
setWaiting(true)
}
// 可以播放時,隱藏加載中提示
const onCanPlay=()=>{
setWaiting(false)
}
return <div className="video-wrapper">
<video ref={videoDom} src={src} onLoadedMetadata={onLoad} onTimeUpdate={onTimeUpdate} onWaiting={onWaiting} onCanPlay={onCanPlay} ></video>
{/* 視頻加載時顯示loading */}
{waiting && <div className="waiting">loading...</div>}
<div className="video-controls">
{/* 播放按鈕 */}
{isPause? <button onClick={()=>{handlePlay(true)}}>播放</button>: <button onClick={()=>{handlePlay(false)}}>暫停</button>}
{/* 進度條 */}
<input type="range" min="0" max={duration} value={curTime} onChange={onSliderChange}/>
{/* 時間信息和分辨率信息 */}
<span>{formatDuration(curTime)}/{formatDuration(duration)}</span>
<span>分辨率:{size.width}x{size.height}</span>
</div>
</div>
}
export default VideoPlayer
樣式寫了一點點,VideoPlayer.css
.video-wrapper{
width:800px;
}
.video-wrapper>video{
width: 100%;
}
.video-controls{
margin-top: 20px;
}
本文的重點不在于React,React只是一個載體,同樣的邏輯可以很容易地用Vue實現(xiàn),重點在于自定義一個視頻播放器的邏輯。
總結(jié)
到此這篇關(guān)于瀏覽器中視頻播放器實現(xiàn)的基本思路與代碼的文章就介紹到這了,更多相關(guān)瀏覽器中視頻播放器實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React?數(shù)據(jù)共享useContext的實現(xiàn)
本文主要介紹了React?數(shù)據(jù)共享useContext的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-04-04
react axios配置代理(proxy),如何解決本地開發(fā)時的跨域問題
這篇文章主要介紹了react axios配置代理(proxy),如何解決本地開發(fā)時的跨域問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
react數(shù)據(jù)管理機制React.Context源碼解析
這篇文章主要為大家介紹了react數(shù)據(jù)管理機制React.Context源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
解決React報錯`value` prop on `input` should&
這篇文章主要為大家介紹了React報錯`value` prop on `input` should not be null解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
字節(jié)封裝React組件手機號自動校驗格式FormItem
這篇文章主要為大家介紹了字節(jié)封裝React組件手機號自動校驗格式FormItem,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08

