React 简化演示(@iunify/react)
使用 @iunify/react 的 usePlayer Hook 实现。位于 docs/demo/react/components/unified.tsx。
核心功能:
- 多源切换 - MP4 / HLS 等格式
- 基础控制 - 播放、暂停、全屏
- 响应式状态 - 自动更新 UI
- 事件日志 - 播放器事件
React 演示
usePlayer Hook + 插件系统 + 事件日志
tsx
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { usePlayer, PlaylistPlugin, AdPlugin, WatermarkPlugin, AnalyticsPlugin } from '@iunify/react'
import { DEMO_PLAYLIST, DEMO_AD_CONFIG, DEMO_PLAYLIST_OPTIONS, DEMO_WATERMARK_CONFIG } from '../config/unified'
import '../typescript/components/demo.css'
export default function ReactUnifiedDemo() {
const videoRef = useRef<HTMLVideoElement>(null)
// 初始化插件(memoized)
const playlist = useMemo(() => new PlaylistPlugin({ playlist: DEMO_PLAYLIST, ...DEMO_PLAYLIST_OPTIONS }), [])
const watermark = useMemo(() => new WatermarkPlugin(DEMO_WATERMARK_CONFIG), [])
const analytics = useMemo(() => new AnalyticsPlugin({ onPlay: () => log('▶ play'), onPause: () => log('⏸ pause') }), [])
const plugins = useMemo(() => [playlist, watermark, analytics], [playlist, watermark, analytics])
// 创建播放器
const { player, state } = usePlayer({
mediaRef: videoRef,
plugins,
autoRegisterEngines: true
})
// 广告插件(切集时重置)
useEffect(() => {
if (!player) return
let dispose: (() => void) | null = null
const attach = () => {
dispose?.()
dispose = player.use(new AdPlugin(DEMO_AD_CONFIG))
}
const off = player.on('sourcechange', attach)
attach()
return () => { off(); dispose?.() }
}, [player])
// 设置初始源
useEffect(() => {
player?.setSource(DEMO_PLAYLIST[0])
}, [player])
// 计算属性
const currentItem = playlist.getCurrentItem()
const canSkipAd = state.playingAd && state.countdown <= 0
return (
<div className="demo-layout">
<div className="demo-video">
<video ref={videoRef} muted playsInline />
{state.playingAd && (
<div className="demo-ad-overlay">
<span>广告 {state.countdown > 0 ? `${state.countdown}s` : ''}</span>
<button disabled={!canSkipAd} onClick={() => player?.extensions.get('ad')?.skipAd()}>
{canSkipAd ? '跳过' : '请稍候...'}
</button>
</div>
)}
</div>
<div className="demo-panel">
<h4>当前:{currentItem?.title ?? '未选择'}</h4>
<div className="demo-playlist">
{DEMO_PLAYLIST.map((item, i) => (
<button key={i} onClick={() => playlist.play(i)}>{item.title}</button>
))}
</div>
<div className="demo-actions">
<button onClick={() => playlist.prev()}>上一集</button>
<button onClick={() => playlist.next()}>下一集</button>
<button onClick={() => player?.toggleFullscreen()}>全屏</button>
</div>
</div>
</div>
)
}