refactor: 移除弹幕项的消失动画逻辑,简化弹幕处理逻辑

This commit is contained in:
2025-04-18 03:40:09 +08:00
parent 5891f20f86
commit 521cd1eddf
3 changed files with 35 additions and 170 deletions

View File

@@ -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>

View File

@@ -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,36 +82,9 @@
// 优化超出长度的弹幕处理 - 改为标记并动画方式移除
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);
}
});
// 延迟移除,给动画足够时间
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);
danmakuList.value.splice(maxItems.value, danmakuList.value.length - maxItems.value);
}
// 设置一个定时器在动画完成后移除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"
:item="item"
:setting="setting"
:data-type="item.type"
class="danmaku-item"
:class="{
'danmaku-item-leaving': pendingRemovalItems.includes(item.randomId),
'batch-item': isInBatchUpdate
}"
>
<ClientDanmakuItem
:item="item"
:setting="setting"
/>
</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>

View File

@@ -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>