React videojs 實(shí)現(xiàn)自定義組件(視頻畫質(zhì)/清晰度切換) 的操作代碼
前言
最近使用videojs作為視頻處理第三方庫,用來對接m3u8視頻類型。這里總結(jié)一下自定義組件遇到的問題及實(shí)現(xiàn),目前看了許多文章也不全,官方文檔寫的也不是很詳細(xì),自己摸索了一段時(shí)間陸陸續(xù)續(xù)完成了,這是實(shí)現(xiàn)后的效果.
樣式啥的自己檢查后覆蓋就行了,沒啥說的,重點(diǎn)看看畫質(zhì)切換這個(gè)組件如何實(shí)現(xiàn)的。最開始我是采用函數(shù)組件直接嵌入進(jìn)去,后面發(fā)現(xiàn)是報(bào)錯(cuò)的,原因是hook使用范圍有誤,找了半天也不知道是什么原因。后面采用繼承Videojs內(nèi)的menu組件來實(shí)現(xiàn)。
代碼實(shí)現(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 () => { // 組件銷毀的時(shí)候,銷毀視頻播放器的實(shí)例 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 = '超清,高清,自動(dòng)' 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 指向被點(diǎn)擊QualityTrackItem實(shí)例 handleClick(event: any) { // 先將所有選項(xiàng)的選中狀態(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ò)展基類,實(shí)現(xiàn)菜單按鈕 class QualityTrackButton extends TrackButton { constructor(player: any, options: any) { super(player, options) this.controlText('畫質(zhì)選擇') this.children()[0].el().firstElementChild.innerText = '自動(dòng)' 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)前時(shí)間是', 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)行處理,這樣的事件的地址就不會(huì)變化造成卸載失效的問題。
到此這篇關(guān)于React videojs 實(shí)現(xiàn)自定義組件(視頻畫質(zhì)/清晰度切換) 的操作代碼的文章就介紹到這了,更多相關(guān)videojs自定義組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react-native-fs實(shí)現(xiàn)文件下載、文本存儲的示例代碼
本篇文章主要介紹了react-native-fs實(shí)現(xiàn)文件下載、文本存儲的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解下2017-09-09react中useState使用:如何實(shí)現(xiàn)在當(dāng)前表格直接更改數(shù)據(jù)
這篇文章主要介紹了react中useState的使用:如何實(shí)現(xiàn)在當(dāng)前表格直接更改數(shù)據(jù),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08react-router browserHistory刷新頁面404問題解決方法
本篇文章主要介紹了react-router browserHistory刷新頁面404問題解決方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-12-12React結(jié)合Drag?API實(shí)現(xiàn)拖拽示例詳解
這篇文章主要為大家介紹了React結(jié)合Drag?API實(shí)現(xiàn)拖拽示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03