Vue 3 简化演示(@iunify/vue)
使用 @iunify/vue 的 usePlayer 组合式 API 实现。位于 docs/demo/vue/components/unified.vue。
核心功能:
- 多源切换 - MP4 / HLS 等格式
- 基础控制 - 播放、暂停、全屏
- 响应式状态 - 自动更新 UI
- 事件日志 - 播放器事件
Vue 演示
usePlayer 组合式 API + 插件系统 + 事件日志
vue
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { usePlayer, PlaylistPlugin, AdPlugin, WatermarkPlugin, AnalyticsPlugin } from '@iunify/vue'
import { DEMO_PLAYLIST, DEMO_AD_CONFIG, DEMO_PLAYLIST_OPTIONS, DEMO_WATERMARK_CONFIG } from '../config/unified'
import '../typescript/components/demo.css'
const videoRef = ref<HTMLVideoElement | null>(null)
const logs = ref<string[]>([])
const log = (msg: string) => logs.value.push(`[${new Date().toLocaleTimeString()}] ${msg}`)
// 初始化插件
const playlist = new PlaylistPlugin({ playlist: DEMO_PLAYLIST, ...DEMO_PLAYLIST_OPTIONS })
const watermark = new WatermarkPlugin(DEMO_WATERMARK_CONFIG)
const analytics = new AnalyticsPlugin({ onPlay: () => log('▶ play'), onPause: () => log('⏸ pause') })
// 创建播放器
const { player, state } = usePlayer({
mediaRef: videoRef,
plugins: [playlist, watermark, analytics],
autoRegisterEngines: true
})
// 广告插件(切集时重置)
let adDispose: (() => void) | null = null
const attachAd = () => {
adDispose?.()
if (player.value) {
adDispose = player.value.use(new AdPlugin(DEMO_AD_CONFIG))
}
}
onMounted(() => {
player.value?.on('sourcechange', attachAd)
attachAd()
player.value?.setSource(DEMO_PLAYLIST[0])
})
onBeforeUnmount(() => adDispose?.())
// 计算属性
const currentItem = computed(() => playlist.getCurrentItem())
const canSkipAd = computed(() => state.value.playingAd && state.value.countdown <= 0)
</script>
<template>
<div class="demo-layout">
<div class="demo-video">
<video ref="videoRef" muted playsinline />
<div v-if="state.playingAd" class="demo-ad-overlay">
<span>广告 {{ state.countdown > 0 ? `${state.countdown}s` : '' }}</span>
<button :disabled="!canSkipAd" @click="player?.extensions.get('ad')?.skipAd()">
{{ canSkipAd ? '跳过' : '请稍候...' }}
</button>
</div>
</div>
<div class="demo-panel">
<h4>当前:{{ currentItem?.title ?? '未选择' }}</h4>
<div class="demo-playlist">
<button v-for="(item, i) in DEMO_PLAYLIST" :key="i" @click="playlist.play(i)">
{{ item.title }}
</button>
</div>
<div class="demo-actions">
<button @click="playlist.prev()">上一集</button>
<button @click="playlist.next()">下一集</button>
<button @click="player?.toggleFullscreen()">全屏</button>
</div>
<div class="demo-log">
<div v-for="(msg, i) in logs" :key="i">{{ msg }}</div>
</div>
</div>
</div>
</template>