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:
2025-04-15 22:18:47 +08:00
parent ff755afd99
commit 1ea4404307
18 changed files with 1898 additions and 433 deletions

View File

@@ -1,32 +1,80 @@
<template>
<yt-live-chat-renderer class="style-scope yt-live-chat-app" style="--scrollbar-width:11px;" hide-timestamps
@mousemove="refreshCantScrollStartTime">
<ticker class="style-scope yt-live-chat-renderer" :messages.sync="paidMessages" :showGiftName="showGiftName || undefined">
</ticker>
<yt-live-chat-item-list-renderer class="style-scope yt-live-chat-renderer" allow-scroll>
<div ref="scroller" id="item-scroller" class="style-scope yt-live-chat-item-list-renderer animated"
@scroll="onScroll">
<div ref="itemOffset" id="item-offset" class="style-scope yt-live-chat-item-list-renderer">
<div ref="items" id="items" class="style-scope yt-live-chat-item-list-renderer" style="overflow: hidden"
:style="{ transform: `translateY(${Math.floor(scrollPixelsRemaining)}px)` }">
<template v-for="message in messages" :key="message.id">
<text-message v-if="message.type === MESSAGE_TYPE_TEXT"
class="style-scope yt-live-chat-item-list-renderer" :time="message.time" :avatarUrl="message.avatarUrl"
:authorName="message.authorName" :authorType="message.authorType" :privilegeType="message.privilegeType"
:richContent="getShowRichContent(message)" :repeated="message.repeated"></text-message>
<paid-message v-else-if="message.type === MESSAGE_TYPE_GIFT"
class="style-scope yt-live-chat-item-list-renderer" :time="message.time" :avatarUrl="message.avatarUrl"
:authorName="getShowAuthorName(message)" :price="message.price"
:priceText="message.price <= 0 ? getGiftShowNameAndNum(message) : ''"
:content="message.price <= 0 ? '' : getGiftShowContent(message, showGiftName)"></paid-message>
<membership-item v-else-if="message.type === MESSAGE_TYPE_MEMBER"
class="style-scope yt-live-chat-item-list-renderer" :time="message.time" :avatarUrl="message.avatarUrl"
:authorName="getShowAuthorName(message)" :privilegeType="message.privilegeType"
:title="message.title"></membership-item>
<paid-message v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
class="style-scope yt-live-chat-item-list-renderer" :time="message.time" :avatarUrl="message.avatarUrl"
:authorName="getShowAuthorName(message)" :price="message.price"
:content="getShowContent(message)"></paid-message>
<yt-live-chat-renderer
class="style-scope yt-live-chat-app"
style="--scrollbar-width:11px;"
hide-timestamps
@mousemove="refreshCantScrollStartTime"
>
<ticker
v-model:messages="paidMessages"
class="style-scope yt-live-chat-renderer"
:show-gift-name="showGiftName || undefined"
/>
<yt-live-chat-item-list-renderer
class="style-scope yt-live-chat-renderer"
allow-scroll
>
<div
id="item-scroller"
ref="scroller"
class="style-scope yt-live-chat-item-list-renderer animated"
@scroll="onScroll"
>
<div
id="item-offset"
ref="itemOffset"
class="style-scope yt-live-chat-item-list-renderer"
>
<div
id="items"
ref="items"
class="style-scope yt-live-chat-item-list-renderer"
style="overflow: hidden"
:style="{ transform: `translateY(${Math.floor(scrollPixelsRemaining)}px)` }"
>
<template
v-for="message in messages"
:key="message.id"
>
<text-message
v-if="message.type === MESSAGE_TYPE_TEXT"
class="style-scope yt-live-chat-item-list-renderer"
:time="message.time"
:avatar-url="message.avatarUrl"
:author-name="message.authorName"
:author-type="message.authorType"
:privilege-type="message.privilegeType"
:rich-content="getShowRichContent(message)"
:repeated="message.repeated"
/>
<paid-message
v-else-if="message.type === MESSAGE_TYPE_GIFT"
class="style-scope yt-live-chat-item-list-renderer"
:time="message.time"
:avatar-url="message.avatarUrl"
:author-name="getShowAuthorName(message)"
:price="message.price"
:price-text="message.price <= 0 ? getGiftShowNameAndNum(message) : ''"
:content="message.price <= 0 ? '' : getGiftShowContent(message, showGiftName)"
/>
<membership-item
v-else-if="message.type === MESSAGE_TYPE_MEMBER"
class="style-scope yt-live-chat-item-list-renderer"
:time="message.time"
:avatar-url="message.avatarUrl"
:author-name="getShowAuthorName(message)"
:privilege-type="message.privilegeType"
:title="message.title"
/>
<paid-message
v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
class="style-scope yt-live-chat-item-list-renderer"
:time="message.time"
:avatar-url="message.avatarUrl"
:author-name="getShowAuthorName(message)"
:price="message.price"
:content="getShowContent(message)"
/>
</template>
</div>
</div>
@@ -143,7 +191,7 @@ export default defineComponent({
mounted() {
this.scrollToBottom()
},
beforeDestroy() {
beforeUnmount() {
if (this.emitSmoothedMessageTimerId) {
window.clearTimeout(this.emitSmoothedMessageTimerId)
this.emitSmoothedMessageTimerId = null