mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
refactor: 移除弹幕项的消失动画逻辑,简化弹幕处理逻辑
This commit is contained in:
@@ -8,12 +8,12 @@ const props = defineProps<BaseDanmakuItemProps>();
|
||||
|
||||
// 使用工具函数获取基础计算属性
|
||||
const emojiData = useDanmakuWindow().emojiData;
|
||||
const { isDisappearing, typeClass } = useDanmakuUtils(props, emojiData);
|
||||
const { typeClass } = useDanmakuUtils(props, emojiData);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="['danmaku-item-content', typeClass, { 'disappearing': isDisappearing }]"
|
||||
:class="['danmaku-item-content', typeClass]"
|
||||
:data-disappear="item.disappearAt"
|
||||
>
|
||||
<!-- 根据设置选择显示风格 -->
|
||||
@@ -47,22 +47,4 @@ const { isDisappearing, typeClass } = useDanmakuUtils(props, emojiData);
|
||||
font-size: var(--dw-font-size);
|
||||
color: var(--dw-text-color);
|
||||
}
|
||||
|
||||
.danmaku-item-content.disappearing {
|
||||
animation: danmaku-out var(--dw-animation-duration, 300ms) ease forwards;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 动画相关 */
|
||||
@keyframes danmaku-out {
|
||||
from {
|
||||
opacity: var(--dw-opacity);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
type TempDanmakuType = EventModel & {
|
||||
randomId: string;
|
||||
isNew?: boolean; // 添加:标记是否为新弹幕
|
||||
disappearAt?: number; // 消失时间戳
|
||||
timestamp?: number; // 添加:记录插入时间戳
|
||||
};
|
||||
@@ -71,24 +70,11 @@
|
||||
disappearAt = Date.now() + setting.value.autoDisappearTime * 1000;
|
||||
}
|
||||
|
||||
// 判断短时间内是否有大量弹幕插入
|
||||
const isRapidInsertion = danmakuList.value.filter(item =>
|
||||
item.isNew && Date.now() - (item.timestamp || 0) < 500).length > 5;
|
||||
|
||||
if (isRapidInsertion && !isInBatchUpdate.value) {
|
||||
isInBatchUpdate.value = true;
|
||||
// 在大量插入时简化动画,300ms后恢复
|
||||
setTimeout(() => {
|
||||
isInBatchUpdate.value = false;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// 为传入的弹幕对象添加一个随机ID和isNew标记
|
||||
const dataWithId = {
|
||||
...data,
|
||||
randomId: nanoid(), // 生成一个随机ID
|
||||
disappearAt, // 添加消失时间
|
||||
isNew: true, // 标记为新弹幕,用于动画
|
||||
timestamp: Date.now(), // 添加时间戳记录插入时间
|
||||
};
|
||||
|
||||
@@ -96,35 +82,8 @@
|
||||
|
||||
// 优化超出长度的弹幕处理 - 改为标记并动画方式移除
|
||||
if (danmakuList.value.length > maxItems.value) {
|
||||
// 找到要移除的项目
|
||||
const itemsToRemove = danmakuList.value.slice(maxItems.value);
|
||||
itemsToRemove.forEach(item => {
|
||||
if (!pendingRemovalItems.value.includes(item.randomId)) {
|
||||
pendingRemovalItems.value.push(item.randomId);
|
||||
danmakuList.value.splice(maxItems.value, danmakuList.value.length - maxItems.value);
|
||||
}
|
||||
});
|
||||
|
||||
// 延迟移除,给动画足够时间
|
||||
setTimeout(() => {
|
||||
danmakuList.value = danmakuList.value.filter(item =>
|
||||
!pendingRemovalItems.value.includes(item.randomId) ||
|
||||
item.timestamp && Date.now() - item.timestamp < setting.value!.animationDuration
|
||||
);
|
||||
|
||||
// 更新待移除列表
|
||||
pendingRemovalItems.value = pendingRemovalItems.value.filter(id =>
|
||||
danmakuList.value.some(item => item.randomId === id)
|
||||
);
|
||||
}, setting.value.animationDuration || 300);
|
||||
}
|
||||
|
||||
// 设置一个定时器,在动画完成后移除isNew标记
|
||||
setTimeout(() => {
|
||||
const index = danmakuList.value.findIndex(item => item.randomId === dataWithId.randomId);
|
||||
if (index !== -1) {
|
||||
danmakuList.value[index].isNew = false;
|
||||
}
|
||||
}, setting.value.animationDuration || 300);
|
||||
|
||||
//console.log('[DanmakuWindow] 添加弹幕:', dataWithId);
|
||||
}
|
||||
@@ -139,14 +98,7 @@
|
||||
// 先标记将要消失的弹幕
|
||||
danmakuList.value.forEach(item => {
|
||||
if (item.disappearAt && item.disappearAt <= now && !pendingRemovalItems.value.includes(item.randomId)) {
|
||||
// 标记为待移除,但还不实际移除
|
||||
pendingRemovalItems.value.push(item.randomId);
|
||||
|
||||
// 延迟删除,让动画有时间完成
|
||||
setTimeout(() => {
|
||||
danmakuList.value = danmakuList.value.filter(d => d.randomId !== item.randomId);
|
||||
pendingRemovalItems.value = pendingRemovalItems.value.filter(id => id !== item.randomId);
|
||||
}, animationDuration);
|
||||
danmakuList.value.splice(danmakuList.value.indexOf(item), 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -232,27 +184,21 @@
|
||||
tag="div"
|
||||
class="danmaku-list-container"
|
||||
>
|
||||
<div
|
||||
<ClientDanmakuItem
|
||||
v-for="item in danmakuList"
|
||||
:key="item.randomId"
|
||||
:data-type="item.type"
|
||||
class="danmaku-item"
|
||||
:class="{
|
||||
'danmaku-item-leaving': pendingRemovalItems.includes(item.randomId),
|
||||
'batch-item': isInBatchUpdate
|
||||
}"
|
||||
>
|
||||
<ClientDanmakuItem
|
||||
:item="item"
|
||||
:setting="setting"
|
||||
:data-type="item.type"
|
||||
class="danmaku-item"
|
||||
/>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
html,
|
||||
body {
|
||||
background: transparent;
|
||||
@@ -279,7 +225,8 @@
|
||||
.danmaku-window {
|
||||
-webkit-app-region: drag;
|
||||
overflow: hidden;
|
||||
background-color: transparent; /* 完全透明背景 */
|
||||
background-color: transparent;
|
||||
/* 完全透明背景 */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--dw-text-color);
|
||||
@@ -300,7 +247,8 @@
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: var(--dw-direction);
|
||||
box-sizing: border-box; /* 确保padding不会增加元素的实际尺寸 */
|
||||
box-sizing: border-box;
|
||||
/* 确保padding不会增加元素的实际尺寸 */
|
||||
}
|
||||
|
||||
.danmaku-list-container {
|
||||
@@ -308,9 +256,8 @@
|
||||
display: flex;
|
||||
flex-direction: inherit;
|
||||
gap: var(--dw-item-spacing);
|
||||
padding-bottom: 8px; /* 添加底部内边距以防止项目溢出 */
|
||||
box-sizing: border-box; /* 确保padding不会增加元素的实际尺寸 */
|
||||
position: relative; /* 为TransitionGroup添加相对定位 */
|
||||
position: relative;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.danmaku-list.reverse {
|
||||
@@ -330,24 +277,6 @@
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 弹幕项样式 */
|
||||
.danmaku-item {
|
||||
transform-origin: center left;
|
||||
transition: all var(--dw-animation-duration) ease;
|
||||
/* 添加硬件加速,防止文字模糊 */
|
||||
transform: translateZ(0);
|
||||
will-change: transform, opacity;
|
||||
backface-visibility: hidden;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
}
|
||||
|
||||
/* 正在离开的弹幕项样式 */
|
||||
.danmaku-item-leaving {
|
||||
animation: danmaku-leave var(--dw-animation-duration) cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
opacity: 0.8; /* 轻微降低不透明度,提高视觉层次感 */
|
||||
z-index: -1; /* 确保离开的项在其他项下方 */
|
||||
}
|
||||
|
||||
/* 批量更新模式下的优化 */
|
||||
.batch-update .danmaku-list-move {
|
||||
transition-duration: 100ms !important;
|
||||
@@ -358,73 +287,42 @@
|
||||
transition-duration: 100ms !important;
|
||||
}
|
||||
|
||||
/* TransitionGroup动画效果 */
|
||||
/* 1. declare transition */
|
||||
.danmaku-list-move,
|
||||
.danmaku-list-enter-active,
|
||||
.danmaku-list-leave-active,
|
||||
.danmaku-list-move {
|
||||
transition: all var(--dw-animation-duration) cubic-bezier(0.55, 0, 0.1, 1);
|
||||
/* 确保动画过程中文字不模糊 */
|
||||
transform: translateZ(0);
|
||||
will-change: transform, opacity;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.danmaku-list-leave-active {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.danmaku-list-enter-from {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.5) translateX(-30px) translateZ(0);
|
||||
}
|
||||
|
||||
.danmaku-list-enter-to {
|
||||
opacity: 1;
|
||||
transform: scaleY(1) translateX(0) translateZ(0);
|
||||
transition: all var(--dw-animation-duration) cubic-bezier(0.55, 0, 0.1, 1);
|
||||
}
|
||||
|
||||
.danmaku-list-enter-from,
|
||||
.danmaku-list-leave-to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.5) translateX(30px) translateZ(0);
|
||||
transform: scaleY(0.01) translate(3000px, 0);
|
||||
}
|
||||
|
||||
/* 处理已有弹幕的移动动画 */
|
||||
.danmaku-list-move {
|
||||
transition: transform var(--dw-animation-duration) cubic-bezier(0.55, 0, 0.1, 1);
|
||||
/* 确保移动动画时文字不模糊 */
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0);
|
||||
/* 3. ensure leaving items are taken out of layout flow so that moving
|
||||
animations can be calculated correctly. */
|
||||
.danmaku-list-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* 根据弹幕类型提供不同的动画特性 */
|
||||
[data-type="3"] { /* 普通弹幕 */
|
||||
[data-type="3"] {
|
||||
/* 普通弹幕 */
|
||||
--transition-delay: 0.02s;
|
||||
}
|
||||
|
||||
[data-type="2"] { /* 礼物 */
|
||||
[data-type="2"] {
|
||||
/* 礼物 */
|
||||
--transition-delay: 0.04s;
|
||||
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); /* 小弹跳效果 */
|
||||
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
/* 小弹跳效果 */
|
||||
}
|
||||
|
||||
[data-type="1"] { /* SC */
|
||||
[data-type="1"] {
|
||||
/* SC */
|
||||
--transition-delay: 0.05s;
|
||||
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); /* 特殊强调效果 */
|
||||
}
|
||||
|
||||
/* 添加弹幕消失动画 */
|
||||
@keyframes danmaku-leave {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateX(0) translateZ(0);
|
||||
filter: blur(0px);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(30px) translateZ(0);
|
||||
filter: blur(1px);
|
||||
}
|
||||
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
/* 特殊强调效果 */
|
||||
}
|
||||
</style>
|
||||
@@ -191,18 +191,3 @@ defineExpose({
|
||||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 公共基础动画样式 */
|
||||
@keyframes danmaku-out {
|
||||
from {
|
||||
opacity: var(--dw-opacity);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user