<template>
<div class="container">
<!-- 弹幕容器 -->
<Danmaku :messages="danmakuList">
<!-- 背景内容,如视频 -->
<video src="your-video-url.mp4" controls></video>
</Danmaku>
<!-- 弹幕发送器 -->
<DanmakuSender @send="handleSendDanmaku" />
</div>
</template>
<script setup>
import Danmaku from '@/components/Danmaku.vue';
import DanmakuSender from '@/components/DanmakuSender.vue';
import { ref } from 'vue';
const danmakuList = ref([]);
const handleSendDanmaku = (message) => {
// 添加弹幕到列表
danmakuList.value.push({
...message,
timestamp: Date.now()
});
};
</script>
interface DanmakuMessage {
text: string; // 弹幕文本
color?: string; // 弹幕颜色,默认白色
fontSize?: number; // 字体大小,默认18px
speed?: number; // 弹幕速度,默认10
opacity?: number; // 不透明度,默认1
timestamp?: number; // 时间戳
}
<Danmaku
:messages="danmakuList"
:paused="isDanmakuPaused"
/>
<button @click="toggleDanmaku">
{{ isDanmakuPaused ? '显示弹幕' : '隐藏弹幕' }}
</button>
<script setup>
const isDanmakuPaused = ref(false);
const toggleDanmaku = () => {
isDanmakuPaused.value = !isDanmakuPaused.value;
};
</script>
<DanmakuSender
@send="handleSendDanmaku"
:font-size="danmakuSize"
:speed="danmakuSpeed"
/>
<div class="controls">
<label>弹幕大小: {{ danmakuSize }}px</label>
<input type="range" v-model.number="danmakuSize" min="12" max="36">
<label>弹幕速度: {{ danmakuSpeed }}</label>
<input type="range" v-model.number="danmakuSpeed" min="5" max="20">
</div>
<Danmaku
:messages="danmakuList"
@send="handleSendDanmaku"
@click="handleDanmakuClick"
/>
<script setup>
const handleDanmakuClick = (danmaku) => {
console.log('点击了弹幕:', danmaku);
// 可以实现点击弹幕高亮、回复等功能
};
</script>
<template>
<div class="danmaku-container" ref="container">
<slot /> <!-- 背景内容 -->
<div class="danmaku-area" ref="danmakuArea"></div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
const props = defineProps({
messages: {
type: Array,
default: () => []
},
trackCount: {
type: Number,
default: 10
},
paused: {
type: Boolean,
default: false
}
});
const emits = defineEmits(['send']);
// 组件实现代码...
</script>
<template>
<div class="danmaku-sender">
<div class="input-area">
<input
v-model="message"
placeholder="发送弹幕..."
@keyup.enter="sendDanmaku"
/>
<button @click="sendDanmaku">发送</button>
</div>
<div class="options">
<div class="color-options">
<label>颜色:</label>
<input type="radio" id="white" value="white" v-model="color">
<label for="white">白色</label>
<input type="radio" id="red" value="red" v-model="color">
<label for="red">红色</label>
<input type="radio" id="yellow" value="yellow" v-model="color">
<label for="yellow">黄色</label>
</div>
<div class="controls">
<button @click="toggleDanmaku">
{{ isDanmakuVisible ? '隐藏弹幕' : '显示弹幕' }}
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
placeholder: {
type: String,
default: '发送弹幕...'
}
});
const emits = defineEmits(['send', 'toggleVisibility']);
// 组件实现代码...
</script>
<template>
<div class="video-danmaku">
<div class="video-container">
<video ref="videoRef" :src="videoSrc" controls></video>
<Danmaku
:messages="danmakuList"
:paused="!isPlaying"
/>
</div>
<DanmakuSender @send="handleSendDanmaku" />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Danmaku from './Danmaku.vue';
import DanmakuSender from './DanmakuSender.vue';
const props = defineProps({
videoSrc: {
type: String,
required: true
}
});
// 组件实现代码...
</script>
<template>
<div class="live-danmaku">
<div class="live-player">
<!-- 直播播放器 -->
<LivePlayer :stream-url="streamUrl" />
<!-- 弹幕层 -->
<Danmaku
:messages="danmakuList"
:paused="!isLivePlaying"
/>
</div>
<!-- 弹幕发送器 -->
<DanmakuSender @send="handleSendDanmaku" />
<!-- 观众列表 -->
<div class="viewers">
<h3>在线观众 ({{ viewerCount }})</h3>
<ul>
<li v-for="viewer in viewers" :key="viewer.id">{{ viewer.name }}</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import Danmaku from './Danmaku.vue';
import DanmakuSender from './DanmakuSender.vue';
import LivePlayer from './LivePlayer.vue';
const props = defineProps({
streamUrl: {
type: String,
required: true
}
});
// 组件实现代码...
</script>
// plugins/danmaku.ts
import { App } from 'vue';
import Danmaku from '@/components/Danmaku.vue';
import DanmakuSender from '@/components/DanmakuSender.vue';
export const DanmakuPlugin = {
install(app: App) {
// 注册组件
app.component('Danmaku', Danmaku);
app.component('DanmakuSender', DanmakuSender);
// 添加全局方法
app.config.globalProperties.$danmaku = {
// 全局发送弹幕方法
send(text: string, options = {}) {
// 这里可以实现全局发送弹幕的逻辑
console.log('全局发送弹幕:', text, options);
}
};
}
};
在main.ts中使用插件:
import { createApp } from 'vue';
import App from './App.vue';
import { DanmakuPlugin } from './plugins/danmaku';
const app = createApp(App);
// 使用弹幕插件
app.use(DanmakuPlugin);
app.mount('#app');
// 在Danmaku组件中实现对象池
const danmakuPool = ref([]);
const createDanmakuElement = () => {
if (danmakuPool.value.length > 0) {
return danmakuPool.value.pop();
}
const element = document.createElement('div');
element.className = 'danmaku-item';
return element;
};
const releaseDanmakuElement = (element) => {
// 重置元素属性
element.style.cssText = '';
element.textContent = '';
// 放入对象池
danmakuPool.value.push(element);
};
// 使用requestAnimationFrame更新弹幕位置
const updateDanmakuPositions = () => {
if (isPaused.value) return;
activeDanmakus.value.forEach(danmaku => {
// 更新弹幕位置
const progress = (Date.now() - danmaku.startTime) / danmaku.duration;
if (progress < 1) {
const x = containerWidth.value - progress * (containerWidth.value + danmaku.width);
danmaku.element.style.transform = `translateX(${x}px)`;
} else {
// 弹幕播放完毕
removeDanmaku(danmaku);
}
});
requestAnimationFrame(updateDanmakuPositions);
};
// 启动动画循环
onMounted(() => {
updateDanmakuPositions();
});
// 支持顶部固定弹幕
enum DanmakuType {
Scroll = 'scroll', // 滚动弹幕
Top = 'top', // 顶部固定弹幕
Bottom = 'bottom' // 底部固定弹幕
}
// 在Danmaku组件中处理不同类型弹幕
const createDanmaku = (message) => {
const type = message.type || DanmakuType.Scroll;
if (type === DanmakuType.Scroll) {
// 创建滚动弹幕
createScrollDanmaku(message);
} else if (type === DanmakuType.Top) {
// 创建顶部固定弹幕
createFixedDanmaku(message, 'top');
} else if (type === DanmakuType.Bottom) {
// 创建底部固定弹幕
createFixedDanmaku(message, 'bottom');
}
};
// 在Danmaku组件中添加过滤功能
const isDanmakuAllowed = (message) => {
// 检查是否包含敏感词
if (containsSensitiveWords(message.text)) {
return false;
}
// 检查是否被用户屏蔽
if (isUserBlocked(message.userId)) {
return false;
}
// 其他过滤条件...
return true;
};
// 在添加弹幕前进行过滤
const addDanmaku = (message) => {
if (!isDanmakuAllowed(message)) return;
// 正常添加弹幕逻辑
};
下面是一个完整的弹幕应用示例:
<template>
<div class="app-container">
<header class="bg-primary text-white p-4">
<h1 class="text-2xl font-bold">弹幕演示应用</h1>
</header>
<main class="container mx-auto p-6">
<div class="flex flex-col lg:flex-row gap-6">
<!-- 视频区域 -->
<div class="lg:w-2/3">
<div class="bg-black rounded-lg overflow-hidden aspect-video relative">
<video
ref="videoRef"
class="w-full h-full object-cover"
controls
poster="https://picsum.photos/1200/675?random=3"
>
<source src="https://example.com/sample-video.mp4" type="video/mp4">
您的浏览器不支持视频播放
</source>
</video>
<!-- 弹幕层 -->
<Danmaku
ref="danmakuRef"
:messages="danmakuList"
:paused="!isPlaying"
/>
</div>
<!-- 弹幕发送器 -->
<DanmakuSender
@send="handleSendDanmaku"
:danmaku-count="danmakuList.length"
/>
</div>
<!-- 弹幕列表 -->
<div class="lg:w-1/3">
<div class="bg-white rounded-lg shadow-md p-4 h-full">
<h2 class="text-xl font-bold mb-4">弹幕列表</h2>
<div class="mb-4">
<div class="flex justify-between items-center mb-2">
<h3 class="font-medium">弹幕设置</h3>
</div>
<div class="space-y-3">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">字体大小</label>
<input
type="range"
min="12"
max="36"
v-model.number="fontSize"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary"
>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>小</span>
<span>{{ fontSize }}px</span>
<span>大</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">弹幕速度</label>
<input
type="range"
min="5"
max="20"
v-model.number="speed"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary"
>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>慢</span>
<span>{{ speed }}</span>
<span>快</span>
</div>
</div>
<div class="flex items-center">
<input type="checkbox" id="show-danmaku" v-model="showDanmaku" class="h-4 w-4 text-primary focus:ring-primary border-gray-300 rounded">
<label for="show-danmaku" class="ml-2 text-sm text-gray-700">显示弹幕</label>
</div>
</div>
</div>
<div class="border-t border-gray-100 pt-4">
<div class="flex justify-between items-center mb-2">
<h3 class="font-medium">最近弹幕</h3>
<span class="text-sm text-gray-500">{{ danmakuList.length }}条</span>
</div>
<div class="space-y-2 max-h-[400px] overflow-y-auto">
<div
v-for="(danmaku, index) in danmakuList.slice(-20).reverse()"
:key="index"
class="p-2 rounded bg-gray-50"
>
<div class="flex justify-between">
<span :style="{ color: danmaku.color || '#FFFFFF' }">{{ danmaku.text }}</span>
<span class="text-xs text-gray-400">{{ formatTime(danmaku.timestamp) }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<footer class="bg-gray-800 text-white p-6 mt-12">
<div class="container mx-auto">
<div class="text-center">
<p>© 2025 弹幕演示应用</p>
</div>
</div>
</footer>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue';
import Danmaku from '@/components/Danmaku.vue';
import DanmakuSender from '@/components/DanmakuSender.vue';
const videoRef = ref(null);
const danmakuRef = ref(null);
const danmakuList = ref([]);
const isPlaying = ref(false);
const fontSize = ref(18);
const speed = ref(10);
const showDanmaku = ref(true);
onMounted(() => {
if (videoRef.value) {
videoRef.value.addEventListener('play', () => {
isPlaying.value = true;
});
videoRef.value.addEventListener('pause', () => {
isPlaying.value = false;
});
}
// 添加示例弹幕
addSampleDanmakus();
});
const addSampleDanmakus = () => {
const sampleMessages = [
{ text: '欢迎来到直播间!', color: 'white' },
{ text: '666666', color: 'yellow' },
{ text: '主播好厉害!', color: 'white' },
{ text: '前方高能', color: 'red' },
{ text: '这个场景太赞了', color: 'white' },
{ text: '第一次看这个视频', color: 'white' },
{ text: '打卡打卡', color: 'yellow' },
{ text: '哈哈哈', color: 'white' }
];
// 定时添加示例弹幕
sampleMessages.forEach((message, index) => {
setTimeout(() => {
handleSendDanmaku(message);
}, index * 2000);
});
};
const handleSendDanmaku = (message) => {
const newMessage = {
...message,
timestamp: Date.now(),
fontSize: fontSize.value,
speed: speed.value
};
danmakuList.value.push(newMessage);
// 实际项目中可以发送到服务器
console.log('发送弹幕:', newMessage);
};
const formatTime = (timestamp) => {
const date = new Date(timestamp);
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
};
</script>
通过以上使用方法和组件封装,您可以在Vue项目中实现一个功能完整、性能优良的弹幕系统,支持视频、直播等多种场景,并且可以根据需求进行灵活扩展和定制。
【夸克网盘】点击查看
关注我获取更多内容