mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-08 11:26:56 +08:00
feat: 重构弹幕组件和工具以改进结构和性能
- 更新 `useWebFetcher.ts`:将事件监听器从 `onEvent` 更改为 `on`,并修改了断开连接处理逻辑,增加了 30 秒后自动重连的功能。 - 增强 `MessageRender.vue`:为 `paidMessages` 使用 v-model,并将生命周期钩子更新为 `beforeUnmount`。 - 引入新组件 `ClientDanmakuItem.vue`:用于渲染具有卡片和文本样式的弹幕条目。 - 创建 `BaseDanmakuItem.vue`:封装弹幕条目的通用逻辑,包括表情符号解析和显示逻辑。 - 添加 `CardStyleDanmakuItem.vue` 和 `TextStyleDanmakuItem.vue`:用于实现不同显示样式的弹幕消息。 - 开发 `danmakuUtils.ts`:提供用于弹幕条目属性和样式的工具函数。 - 改进弹幕组件的 CSS 样式:确保外观统一和响应式布局。
This commit is contained in:
188
src/client/components/danmaku/danmakuUtils.ts
Normal file
188
src/client/components/danmaku/danmakuUtils.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import { EventDataTypes, EventModel } from '@/api/api-models';
|
||||
import { DanmakuWindowSettings } from '../../store/useDanmakuWindow';
|
||||
import { computed, ComputedRef } from 'vue';
|
||||
import { GetGuardColor } from '@/Utils';
|
||||
|
||||
export interface BaseDanmakuItemProps {
|
||||
item: EventModel & { randomId: string; isNew?: boolean; disappearAt?: number; };
|
||||
setting: DanmakuWindowSettings;
|
||||
}
|
||||
|
||||
export function useDanmakuUtils(
|
||||
props: BaseDanmakuItemProps,
|
||||
emojiData: { data: { inline: { [key: string]: string }; plain: { [key: string]: string } } }
|
||||
) {
|
||||
// 计算SC弹幕的颜色类
|
||||
const scColorClass = computed(() => {
|
||||
if (props.item.type === EventDataTypes.SC) {
|
||||
const price = props.item?.price || 0;
|
||||
if (price === 0) return 'sc-0';
|
||||
if (price > 0 && price < 50) return 'sc-50';
|
||||
if (price >= 50 && price < 100) return 'sc-100';
|
||||
if (price >= 100 && price < 500) return 'sc-500';
|
||||
if (price >= 500 && price < 1000) return 'sc-1000';
|
||||
if (price >= 1000 && price < 2000) return 'sc-2000';
|
||||
if (price >= 2000) return 'sc-max';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 根据类型计算样式
|
||||
const typeClass = computed(() => {
|
||||
switch (props.item.type) {
|
||||
case EventDataTypes.Message: return 'message-item';
|
||||
case EventDataTypes.Gift: return 'gift-item';
|
||||
case EventDataTypes.SC: return `sc-item ${scColorClass.value}`;
|
||||
case EventDataTypes.Guard: return 'guard-item';
|
||||
case EventDataTypes.Enter: return 'enter-item';
|
||||
default: return '';
|
||||
}
|
||||
});
|
||||
|
||||
// 获取舰长颜色
|
||||
const guardColor = computed(() => GetGuardColor(props.item.guard_level));
|
||||
|
||||
// 舰长样式类
|
||||
const guardLevelClass = computed(() => {
|
||||
if (props.item.type === EventDataTypes.Guard) {
|
||||
return `guard-level-${props.item.guard_level || 0}`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 检查是否需要显示头像
|
||||
const showAvatar = computed(() => props.setting.showAvatar);
|
||||
|
||||
// 解析包含内联表情的消息
|
||||
const parsedMessage = computed<{ type: 'text' | 'emoji'; content?: string; url?: string; name?: string; }[]>(() => {
|
||||
// 仅处理非纯表情的普通消息
|
||||
if (props.item.type !== EventDataTypes.Message || props.item.emoji || !props.item.msg) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const segments: { type: 'text' | 'emoji'; content?: string; url?: string; name?: string; }[] = [];
|
||||
let lastIndex = 0;
|
||||
const regex = /\[([^\]]+)\]/g; // 匹配 [表情名]
|
||||
let match;
|
||||
|
||||
try {
|
||||
const availableEmojis = emojiData.data || {}; // 确保 emojiData 已加载
|
||||
|
||||
while ((match = regex.exec(props.item.msg)) !== null) {
|
||||
// 添加表情前的文本部分
|
||||
if (match.index > lastIndex) {
|
||||
segments.push({ type: 'text', content: props.item.msg.substring(lastIndex, match.index) });
|
||||
}
|
||||
|
||||
const emojiFullName = match[0]; // 完整匹配,例如 "[哈哈]"
|
||||
const emojiInfo = availableEmojis.inline[emojiFullName] || availableEmojis.plain[emojiFullName];
|
||||
|
||||
if (emojiInfo) {
|
||||
// 找到了表情
|
||||
segments.push({ type: 'emoji', url: emojiInfo, name: emojiFullName });
|
||||
} else {
|
||||
// 未找到表情,当作普通文本处理
|
||||
segments.push({ type: 'text', content: emojiFullName });
|
||||
}
|
||||
|
||||
lastIndex = regex.lastIndex;
|
||||
}
|
||||
|
||||
// 添加最后一个表情后的文本部分
|
||||
if (lastIndex < props.item.msg.length) {
|
||||
segments.push({ type: 'text', content: props.item.msg.substring(lastIndex) });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing message for emojis:", error);
|
||||
// 解析出错时,返回原始文本
|
||||
return [{ type: 'text', content: props.item.msg }];
|
||||
}
|
||||
|
||||
// 如果解析后为空(例如,消息只包含无法识别的[]),则返回原始文本
|
||||
if (segments.length === 0 && props.item.msg) {
|
||||
return [{ type: 'text', content: props.item.msg }];
|
||||
}
|
||||
|
||||
return segments;
|
||||
});
|
||||
|
||||
// 获取不同类型消息的显示标签
|
||||
const typeLabel = computed(() => {
|
||||
switch (props.item.type) {
|
||||
case EventDataTypes.Message: return ''; // 普通消息不需要标签
|
||||
case EventDataTypes.Gift: return '【礼物】';
|
||||
case EventDataTypes.SC: return '【SC】';
|
||||
case EventDataTypes.Guard: return '【舰长】';
|
||||
case EventDataTypes.Enter: return '【进场】';
|
||||
default: return '';
|
||||
}
|
||||
});
|
||||
|
||||
// 获取礼物或SC的价格文本
|
||||
const priceText = computed(() => {
|
||||
if (props.item.type === EventDataTypes.SC ||
|
||||
(props.item.type === EventDataTypes.Gift && props.item.price > 0)) {
|
||||
return `¥${props.item.price || 0}`;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 获取用户名显示
|
||||
const displayName = computed(() => {
|
||||
return props.item.name || '匿名用户';
|
||||
});
|
||||
|
||||
// 获取消息显示内容
|
||||
const displayContent = computed(() => {
|
||||
switch (props.item.type) {
|
||||
case EventDataTypes.Message:
|
||||
return props.item.msg || '';
|
||||
case EventDataTypes.Gift:
|
||||
return `${props.item.num || 1} × ${props.item.msg}`;
|
||||
case EventDataTypes.SC:
|
||||
return props.item.msg || '';
|
||||
case EventDataTypes.Guard:
|
||||
return props.item.msg || '开通了舰长';
|
||||
case EventDataTypes.Enter:
|
||||
return '进入了直播间';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
// 根据风格及类型获取文本颜色
|
||||
const textModeColor = computed(() => {
|
||||
if (props.item.type === EventDataTypes.SC) {
|
||||
return '#FFD700'; // SC消息金色
|
||||
} else if (props.item.type === EventDataTypes.Gift) {
|
||||
return '#FF69B4'; // 礼物消息粉色
|
||||
} else if (props.item.type === EventDataTypes.Guard) {
|
||||
return guardColor.value; // 舰长消息使用舰长颜色
|
||||
} else if (props.item.type === EventDataTypes.Enter) {
|
||||
return '#67C23A'; // 入场消息绿色
|
||||
}
|
||||
return undefined; // 普通消息使用默认颜色
|
||||
});
|
||||
|
||||
return {
|
||||
scColorClass,
|
||||
typeClass,
|
||||
guardColor,
|
||||
guardLevelClass,
|
||||
showAvatar,
|
||||
parsedMessage,
|
||||
typeLabel,
|
||||
priceText,
|
||||
displayName,
|
||||
displayContent,
|
||||
textModeColor
|
||||
};
|
||||
}
|
||||
|
||||
// 返回类型定义,便于TypeScript类型推断
|
||||
export type DanmakuUtils = ReturnType<typeof useDanmakuUtils>;
|
||||
|
||||
// 类型别名,用于清晰表达每个计算属性的类型
|
||||
export type ComputedDanmakuUtils = {
|
||||
[K in keyof DanmakuUtils]: ComputedRef<any>
|
||||
};
|
||||
Reference in New Issue
Block a user