前言
最近使用videojs作為視頻處理第三方庫,用來對接m3u8視頻類型。這里總結(jié)一下自定義組件遇到的問題及實現(xiàn),目前看了許多文章也不全,官方文檔寫的也不是很詳細(xì),自己摸索了一段時間陸陸續(xù)續(xù)完成了,這是實現(xiàn)后的效果.
樣式啥的自己檢查后覆蓋就行了,沒啥說的,重點看看畫質(zhì)切換這個組件如何實現(xiàn)的。最開始我是采用函數(shù)組件直接嵌入進(jìn)去,后面發(fā)現(xiàn)是報錯的,原因是hook使用范圍有誤,找了半天也不知道是什么原因。后面采用繼承Videojs內(nèi)的menu組件來實現(xiàn)。
代碼實現(xiàn)
option配置如下
const options: any = {
controls: true,
preload: 'auto',
language: 'zh-CN',
width: 854,
height: 480,
playbackRates: [0.5, 0.75, 1, 1.5, 2], // 倍速數(shù)組
controlBar: {
children: {
PlayToggle: true,
CurrentTimeDisplay: true,
DurationDisplay: true,
ProgressControl: true,
Quality: true,
PlaybackRateMenuButton: true,
volumePanel: {
inline: false,
},
PictureInPictureToggle: true,
FullscreenToggle: true,
},
},
}
video組件
import { ForwardedRef, forwardRef, useEffect, useImperativeHandle, useRef } from 'react'
import videojs from 'video.js'
import Quality from './quality'
import './index.less'
interface videoComProps {
videoOptions: any
onReady: (player: any) => void
src?: string
}
const VideoWrapper = (props: videoComProps, ref: ForwardedRef<any>) => {
const { videoOptions, onReady, src } = props
const videoRef = useRef<any>(null)
const playerRef = useRef<any>(null)
function toggleTv(obj: any) {
const player = playerRef?.current
if (!player) return
player.src(obj.src)
player.load(obj.load)
player.play()
}
useEffect(() => {
if (!playerRef?.current && videoRef.current) {
// 注冊組件 一定要在使用之前注冊哦
videojs.registerComponent('Quality', Quality as any)
// 初始化video
const player = (playerRef.current = videojs(videoRef.current, videoOptions, () => {
onReady(player)
}))
}
}, [videoRef])
useEffect(() => {
const player = playerRef.current
return () => {
// 組件銷毀的時候,銷毀視頻播放器的實例
if (player && !player.isDisposed()) {
player.dispose()
playerRef.current = null
}
}
}, [playerRef])
// ref拋出變量
useImperativeHandle(ref, () => ({
toggleTv,
}))
return (
<div className="video-wrapper">
<video
ref={videoRef}
className="video-js vjs-big-play-centered"
>
<source src={src} />
{/* <span>視頻走丟了,請稍后再試</span> */}
</video>
</div>
)
}
export default forwardRef(VideoWrapper)
自定義組件
// 切換視頻清晰度代碼
import videoJs from 'video.js'
import { createRoot } from 'react-dom/client'
// 初始化清晰度按鈕
const TextTrackMenuItem: any = videoJs.getComponent('TextTrackMenuItem')
const TrackButton: any = videoJs.getComponent('TrackButton')
const videoQuality = '超清,高清,自動'
class QualityTrackItem extends TextTrackMenuItem {
constructor(player: any, options: any) {
super(player, options)
this.mount = this.mount.bind(this)
player.ready(() => {
this.mount()
})
this.on('dispose', () => {
this.root.unmount()
})
if (options.index === 2) {
this.addClass('vjs-selected')
}
}
// 切換高清播放源,this 指向被點擊QualityTrackItem實例
handleClick(event: any) {
// 先將所有選項的選中狀態(tài)設(shè)為未選中
this.parentComponent_.children_.forEach((c: any) => {
c.selected(false)
})
// 選中當(dāng)前
this.selected(true)
// 選中后修改按鈕文本
const btn = document.querySelector('.vjs-menu-button .vjs-icon-placeholder')
if (!btn) return
btn.innerHTML = this.track.label
// 其他邏輯 通知修改視頻源地址進(jìn)行切換
console.log('切換視頻源')
}
mount() {
this.root = createRoot(this.el()).render(<div>{this.track.label}</div>)
}
}
// 擴(kuò)展基類,實現(xiàn)菜單按鈕
class QualityTrackButton extends TrackButton {
constructor(player: any, options: any) {
super(player, options)
this.controlText('畫質(zhì)選擇')
this.children()[0].el().firstElementChild.innerText = '自動'
this.addClass('vjs-quality')
}
createItems() {
const qualityKeyArray = videoQuality.split(',')
if (qualityKeyArray.length > 0) {
const result: any = []
qualityKeyArray.forEach((key, index: number) => {
result.push(
new QualityTrackItem(this.player_, {
track: {
label: key,
value: key,
},
selectable: true,
index,
})
)
})
return result
} else {
return []
}
}
}
export default QualityTrackButton
可能遇到的問題
1.卸載不了對應(yīng)事件
const handleUpdate = useCallback(() => {
const player = playerRef.current
//window.document.fullscreenElement檢測視頻是否正在全屏
// console.log('播放中,當(dāng)前時間是', player.currentTime())
if (player.currentTime() > 10) {
if (window.document.fullscreenElement) {
// 如果是全屏 退出全屏
window.document.exitFullscreen()
}
player.currentTime(10)
setOverlay(true)
player.pause()
}
}, [])
useEffect(() => {
if (!playerRef?.current && videoRef.current) {
// 注冊組件 一定要在使用之前注冊哦
videojs.registerComponent('Quality', Quality as any)
// 初始化video
const player = (playerRef.current = videojs(videoRef.current, videoOptions, () => {
onReady(player)
}))
playFlag && player.on('timeupdate', handleUpdate)
}
}, [videoRef])
// 加入學(xué)習(xí)
const handelAddLearn = () => {
const player = playerRef.current
player.off('timeupdate', handleUpdate)
setPlayFlag(false)
setOverlay(false)
player.play()
}
把對應(yīng)需要卸載的事件采用useCallback進(jìn)行處理,這樣的事件的地址就不會變化造成卸載失效的問題文章來源:http://www.zghlxwxcb.cn/news/detail-677645.html
END
希望能幫到正在開發(fā)的伙伴們文章來源地址http://www.zghlxwxcb.cn/news/detail-677645.html
到了這里,關(guān)于videojs 實現(xiàn)自定義組件(視頻畫質(zhì)/清晰度切換) React的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!