2025-08-19 19:04:11 +08:00

503 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="dplayer-test">
<div class="container">
<h1>DPlayer 测试页面</h1>
<p>测试 DPlayer 视频播放器功能</p>
<div class="video-section">
<div ref="dplayerContainer" class="dplayer-container"></div>
</div>
<div class="controls">
<div class="control-group">
<h3>基础控制</h3>
<button @click="play">播放</button>
<button @click="pause">暂停</button>
<button @click="seek(30)">跳转30秒</button>
<button @click="setVolume(50)">音量50%</button>
</div>
<div class="control-group">
<h3>快进控制</h3>
<button @click="seekBackward(10)">后退10秒</button>
<button @click="seekForward(10)">前进10秒</button>
<button @click="seekBackward(30)">后退30秒</button>
<button @click="seekForward(30)">前进30秒</button>
</div>
<div class="control-group">
<h3>倍速控制</h3>
<button @click="setPlaybackRate(0.5)">0.5x</button>
<button @click="setPlaybackRate(0.75)">0.75x</button>
<button @click="setPlaybackRate(1)">1x</button>
<button @click="setPlaybackRate(1.25)">1.25x</button>
<button @click="setPlaybackRate(1.5)">1.5x</button>
<button @click="setPlaybackRate(2)">2x</button>
</div>
<div class="control-group">
<h3>清晰度切换</h3>
<button @click="switchQuality(0)">1080P</button>
<button @click="switchQuality(1)">720P</button>
<button @click="switchQuality(2)">480P</button>
<button @click="switchQuality(3)">360P</button>
</div>
<div class="control-group">
<h3>字幕控制</h3>
<button @click="toggleSubtitle">切换字幕</button>
<button @click="showSubtitle">显示字幕</button>
<button @click="hideSubtitle">隐藏字幕</button>
</div>
<div class="control-group">
<h3>弹幕控制 (模拟)</h3>
<button @click="toggleDanmaku">切换弹幕</button>
<button @click="showDanmaku">显示弹幕</button>
<button @click="hideDanmaku">隐藏弹幕</button>
<button @click="sendDanmaku('测试弹幕', '#fff', 0)">发送白色弹幕</button>
<button @click="sendDanmaku('红色弹幕', '#e54256', 0)">发送红色弹幕</button>
<button @click="sendDanmaku('顶部弹幕', '#ffe133', 1)">发送顶部弹幕</button>
<button @click="sendDanmaku('底部弹幕', '#64DD17', 2)">发送底部弹幕</button>
</div>
<div class="control-group">
<h3>弹幕设置 (模拟)</h3>
<button @click="setDanmakuOpacity(0.5)">透明度50%</button>
<button @click="setDanmakuOpacity(0.8)">透明度80%</button>
<button @click="setDanmakuOpacity(1)">透明度100%</button>
<button @click="setDanmakuFontSize(20)">字体20px</button>
<button @click="setDanmakuFontSize(24)">字体24px</button>
<button @click="setDanmakuFontSize(28)">字体28px</button>
</div>
</div>
<div class="status">
<h3>播放状态</h3>
<p>播放状态: {{ isPlaying ? '播放中' : '已暂停' }}</p>
<p>当前倍速: {{ currentPlaybackRate }}x</p>
<p>当前清晰度: {{ currentQuality }}</p>
<p>字幕状态: {{ subtitleVisible ? '显示' : '隐藏' }}</p>
<p>弹幕状态: {{ danmakuVisible ? '显示' : '隐藏' }} (模拟)</p>
<p>错误信息: {{ errorMessage || '无' }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
const dplayerContainer = ref<HTMLDivElement>()
let player: any = null
const isPlaying = ref(false)
const errorMessage = ref('')
const currentPlaybackRate = ref(1)
const currentQuality = ref('1080P')
const subtitleVisible = ref(false)
const danmakuVisible = ref(true)
// 视频质量配置
const videoQualities = [
{ name: '1080P', url: '/video/first.mp4' },
{ name: '720P', url: '/video/first.mp4' }, // 实际项目中应该是不同的视频文件
{ name: '480P', url: '/video/first.mp4' },
{ name: '360P', url: '/video/first.mp4' }
]
// 字幕配置
const subtitleConfig = {
url: '/subtitle/sample.vtt', // 示例字幕文件
type: 'webvtt',
fontSize: '20px',
bottom: '10%',
color: '#fff'
}
// 加载 DPlayer
const loadDPlayer = (): Promise<void> => {
return new Promise((resolve, reject) => {
if ((window as any).DPlayer) {
resolve()
return
}
// 加载 CSS
const cssLink = document.createElement('link')
cssLink.rel = 'stylesheet'
cssLink.href = 'https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.css'
document.head.appendChild(cssLink)
// 加载 JS
const script = document.createElement('script')
script.src = 'https://cdn.jsdelivr.net/npm/dplayer@1.27.1/dist/DPlayer.min.js'
script.onload = () => resolve()
script.onerror = () => reject(new Error('Failed to load DPlayer'))
document.head.appendChild(script)
})
}
// 初始化播放器
const initPlayer = async () => {
try {
await loadDPlayer()
if (!dplayerContainer.value) return
const DPlayer = (window as any).DPlayer
player = new DPlayer({
container: dplayerContainer.value,
video: {
url: '/video/first.mp4',
type: 'auto'
},
subtitle: subtitleConfig,
// 暂时禁用弹幕 API避免网络错误
// danmaku: {
// id: 'dplayer-danmaku',
// api: 'https://dplayer-mate.vercel.app/api/dplayer',
// token: 'tokendemo',
// maximum: 1000,
// user: 'DIYGod',
// bottom: '15%',
// unlimited: true
// },
autoplay: false,
theme: '#007bff',
lang: 'zh-cn',
hotkey: true,
preload: 'auto',
volume: 0.8,
playbackSpeed: [0.5, 0.75, 1, 1.25, 1.5, 2],
loop: false,
showDanmaku: true,
danmakuUnlimited: true,
contextmenu: [
{
text: '关于 DPlayer',
link: 'https://github.com/DIYGod/DPlayer'
}
]
})
// 事件监听
player.on('play', () => {
isPlaying.value = true
console.log('播放开始')
})
player.on('pause', () => {
isPlaying.value = false
console.log('播放暂停')
})
player.on('ended', () => {
isPlaying.value = false
console.log('播放结束')
})
player.on('error', () => {
errorMessage.value = '播放出错'
console.error('播放错误')
})
player.on('ratechange', () => {
currentPlaybackRate.value = player.speed()
console.log('倍速改变:', currentPlaybackRate.value)
})
// 移除可能导致错误的弹幕相关事件监听
// player.on('quality_start', () => {
// console.log('清晰度切换开始')
// })
// player.on('quality_end', () => {
// const quality = player.video.currentQuality
// currentQuality.value = videoQualities[quality].name
// console.log('清晰度切换完成:', currentQuality.value)
// })
} catch (err) {
console.error('初始化 DPlayer 失败:', err)
errorMessage.value = '初始化失败'
}
}
// 基础控制方法
const play = () => {
if (player) {
player.play()
}
}
const pause = () => {
if (player) {
player.pause()
}
}
const seek = (time: number) => {
if (player) {
player.seek(time)
}
}
const setVolume = (volume: number) => {
if (player) {
player.volume(volume / 100)
}
}
// 快进控制方法
const seekBackward = (seconds: number) => {
if (player) {
const currentTime = player.video.currentTime
const newTime = Math.max(0, currentTime - seconds)
player.seek(newTime)
}
}
const seekForward = (seconds: number) => {
if (player) {
const currentTime = player.video.currentTime
const duration = player.video.duration
const newTime = Math.min(duration, currentTime + seconds)
player.seek(newTime)
}
}
// 倍速控制方法
const setPlaybackRate = (rate: number) => {
if (player) {
player.speed(rate)
currentPlaybackRate.value = rate
}
}
// 清晰度切换方法
const switchQuality = (qualityIndex: number) => {
if (player && player.video) {
// 由于现在使用单一视频源,这里只是模拟切换
const quality = videoQualities[qualityIndex]
currentQuality.value = quality.name
console.log(`切换到清晰度: ${quality.name}`)
alert(`模拟切换到清晰度: ${quality.name}`)
}
}
// 字幕控制方法
const toggleSubtitle = () => {
if (player) {
if (subtitleVisible.value) {
player.hideSubtitle()
subtitleVisible.value = false
} else {
player.showSubtitle()
subtitleVisible.value = true
}
}
}
const showSubtitle = () => {
if (player) {
player.showSubtitle()
subtitleVisible.value = true
}
}
const hideSubtitle = () => {
if (player) {
player.hideSubtitle()
subtitleVisible.value = false
}
}
// 弹幕控制方法
const sendDanmaku = (text: string, color: string = '#fff', type: number = 0) => {
if (player && player.danmaku) {
player.danmaku.send({
text: text,
color: color,
type: type
})
} else {
console.log('弹幕功能暂不可用,请检查网络连接')
alert(`模拟发送弹幕: ${text} (颜色: ${color}, 类型: ${type})`)
}
}
const showDanmaku = () => {
if (player && player.danmaku) {
player.danmaku.show()
danmakuVisible.value = true
} else {
danmakuVisible.value = true
console.log('弹幕显示')
}
}
const hideDanmaku = () => {
if (player && player.danmaku) {
player.danmaku.hide()
danmakuVisible.value = false
} else {
danmakuVisible.value = false
console.log('弹幕隐藏')
}
}
const toggleDanmaku = () => {
if (player && player.danmaku) {
if (player.danmaku.visible) {
player.danmaku.hide()
danmakuVisible.value = false
} else {
player.danmaku.show()
danmakuVisible.value = true
}
} else {
danmakuVisible.value = !danmakuVisible.value
console.log(`弹幕${danmakuVisible.value ? '显示' : '隐藏'}`)
}
}
const setDanmakuOpacity = (opacity: number) => {
if (player && player.danmaku) {
player.danmaku.opacity(opacity)
} else {
console.log(`设置弹幕透明度: ${opacity}`)
}
}
const setDanmakuFontSize = (size: number) => {
if (player && player.danmaku) {
player.danmaku.fontSize(size)
} else {
console.log(`设置弹幕字体大小: ${size}px`)
}
}
onMounted(() => {
initPlayer()
})
onUnmounted(() => {
if (player) {
player.destroy()
player = null
}
})
</script>
<style scoped>
.dplayer-test {
min-height: 100vh;
background: #f5f5f5;
padding: 20px 0;
}
.container {
max-width: 1000px;
margin: 0 auto;
padding: 0 20px;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 10px;
}
p {
text-align: center;
color: #666;
margin-bottom: 30px;
}
.video-section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.dplayer-container {
width: 100%;
aspect-ratio: 16/9;
background: #000;
border-radius: 8px;
overflow: hidden;
}
.controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.control-group {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.control-group h3 {
margin: 0 0 15px 0;
color: #333;
font-size: 16px;
text-align: center;
}
.control-group button {
display: block;
width: 100%;
padding: 10px 15px;
margin: 5px 0;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.control-group button:hover {
background: #0056b3;
}
.status {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.status h3 {
margin: 0 0 15px 0;
color: #333;
font-size: 18px;
}
.status p {
margin: 10px 0;
text-align: left;
color: #333;
font-size: 14px;
}
@media (max-width: 768px) {
.controls {
grid-template-columns: 1fr;
}
.control-group button {
font-size: 12px;
padding: 8px 12px;
}
}
</style>