fix(弹幕组件): 修复低端机弹幕突然消失的问题

This commit is contained in:
hzj 2026-05-15 14:40:11 +08:00
parent 0bb4b13586
commit 54e09023f0
4 changed files with 156 additions and 20 deletions

View File

@ -1,6 +1,10 @@
<!-- Barrage.vue 优化版 -->
<template>
<div ref="containerRef" class="barrage-root" :class="{ 'barrage-lite': liteAnimations }">
<div
ref="containerRef"
class="barrage-root"
:class="{ 'barrage-lite': liteAnimations, 'barrage-paused': isBarragePaused }"
>
<div
v-for="(track, index) in visibleTracks"
:key="index"
@ -87,6 +91,7 @@ const tracks = ref([[], []])
const containerRef = ref(null)
const idCounter = ref(0)
const currentIndex = ref(0)
const isBarragePaused = ref(false)
const liteAnimations = shouldUseLiteAnimations()
const visibleTracks = computed(() => tracks.value.filter((track) => track.length > 0))
const effectiveMaxItems = computed(() => (liteAnimations ? Math.min(props.maxItems, 1) : props.maxItems))
@ -186,7 +191,7 @@ function getNextBarrage() {
}
function canRunBarrage() {
return isPageVisible && isInViewport && props.barrageList.length > 0
return !isBarragePaused.value && isPageVisible && isInViewport && props.barrageList.length > 0
}
function stopBarrage(clearTracks = false) {
@ -218,9 +223,24 @@ function restartBarrage(clearTracks = false) {
startBarrage()
}
function pauseBarrage(clearTracks = false) {
isBarragePaused.value = true
stopBarrage(clearTracks)
}
function resumeBarrage() {
isBarragePaused.value = false
startBarrage()
}
function handleVisibilityChange() {
isPageVisible = !document.hidden
restartBarrage(true)
if (isPageVisible && isInViewport) {
resumeBarrage()
return
}
pauseBarrage(false)
}
function setupViewportObserver() {
@ -231,7 +251,12 @@ function setupViewportObserver() {
viewportObserver = new IntersectionObserver(
([entry]) => {
isInViewport = entry?.isIntersecting !== false
restartBarrage(true)
if (isInViewport && isPageVisible) {
resumeBarrage()
return
}
pauseBarrage(false)
},
{ rootMargin: '80px 0px', threshold: 0 },
)
@ -262,7 +287,12 @@ watch(
[() => props.barrageList, () => locale.value],
() => {
currentIndex.value = 0
restartBarrage(true)
if (props.barrageList.length === 0) {
stopBarrage(true)
return
}
restartBarrage(false)
},
{ deep: false },
)
@ -300,6 +330,10 @@ watch(
will-change: auto;
}
.barrage-paused .barrage-item {
animation-play-state: paused;
}
.barrage-avatar {
width: 10vw;
border-radius: 50%;

View File

@ -1,6 +1,10 @@
<!-- Barrage.vue 优化版 -->
<template>
<div ref="containerRef" class="barrage-root" :class="{ 'barrage-lite': liteAnimations }">
<div
ref="containerRef"
class="barrage-root"
:class="{ 'barrage-lite': liteAnimations, 'barrage-paused': isBarragePaused }"
>
<div
v-for="(track, index) in visibleTracks"
:key="index"
@ -92,6 +96,7 @@ const tracks = ref([[], []])
const containerRef = ref(null)
const idCounter = ref(0)
const currentIndex = ref(0)
const isBarragePaused = ref(false)
const liteAnimations = shouldUseLiteAnimations()
const visibleTracks = computed(() => tracks.value.filter((track) => track.length > 0))
const effectiveMaxItems = computed(() => (liteAnimations ? Math.min(props.maxItems, 1) : props.maxItems))
@ -182,7 +187,7 @@ function getNextBarrage() {
}
function canRunBarrage() {
return isPageVisible && isInViewport && props.barrageList.length > 0
return !isBarragePaused.value && isPageVisible && isInViewport && props.barrageList.length > 0
}
function stopBarrage(clearTracks = false) {
@ -214,9 +219,24 @@ function restartBarrage(clearTracks = false) {
startBarrage()
}
function pauseBarrage(clearTracks = false) {
isBarragePaused.value = true
stopBarrage(clearTracks)
}
function resumeBarrage() {
isBarragePaused.value = false
startBarrage()
}
function handleVisibilityChange() {
isPageVisible = !document.hidden
restartBarrage(true)
if (isPageVisible && isInViewport) {
resumeBarrage()
return
}
pauseBarrage(false)
}
function setupViewportObserver() {
@ -227,7 +247,12 @@ function setupViewportObserver() {
viewportObserver = new IntersectionObserver(
([entry]) => {
isInViewport = entry?.isIntersecting !== false
restartBarrage(true)
if (isInViewport && isPageVisible) {
resumeBarrage()
return
}
pauseBarrage(false)
},
{ rootMargin: '80px 0px', threshold: 0 },
)
@ -258,7 +283,12 @@ watch(
[() => props.barrageList, () => locale.value],
() => {
currentIndex.value = 0
restartBarrage(true)
if (props.barrageList.length === 0) {
stopBarrage(true)
return
}
restartBarrage(false)
},
{ deep: false },
)
@ -296,6 +326,10 @@ watch(
will-change: auto;
}
.barrage-paused .barrage-item {
animation-play-state: paused;
}
.barrage-avatar {
width: 10vw;
border-radius: 50%;

View File

@ -1,6 +1,10 @@
<!-- Barrage.vue 优化版 -->
<template>
<div ref="containerRef" class="barrage-root" :class="{ 'barrage-lite': liteAnimations }">
<div
ref="containerRef"
class="barrage-root"
:class="{ 'barrage-lite': liteAnimations, 'barrage-paused': isBarragePaused }"
>
<div
v-for="(track, index) in visibleTracks"
:key="index"
@ -92,6 +96,7 @@ const tracks = ref([[], []])
const containerRef = ref(null)
const idCounter = ref(0)
const currentIndex = ref(0)
const isBarragePaused = ref(false)
const liteAnimations = shouldUseLiteAnimations()
const visibleTracks = computed(() => tracks.value.filter((track) => track.length > 0))
const effectiveMaxItems = computed(() => (liteAnimations ? Math.min(props.maxItems, 1) : props.maxItems))
@ -182,7 +187,7 @@ function getNextBarrage() {
}
function canRunBarrage() {
return isPageVisible && isInViewport && props.barrageList.length > 0
return !isBarragePaused.value && isPageVisible && isInViewport && props.barrageList.length > 0
}
function stopBarrage(clearTracks = false) {
@ -214,9 +219,24 @@ function restartBarrage(clearTracks = false) {
startBarrage()
}
function pauseBarrage(clearTracks = false) {
isBarragePaused.value = true
stopBarrage(clearTracks)
}
function resumeBarrage() {
isBarragePaused.value = false
startBarrage()
}
function handleVisibilityChange() {
isPageVisible = !document.hidden
restartBarrage(true)
if (isPageVisible && isInViewport) {
resumeBarrage()
return
}
pauseBarrage(false)
}
function setupViewportObserver() {
@ -227,7 +247,12 @@ function setupViewportObserver() {
viewportObserver = new IntersectionObserver(
([entry]) => {
isInViewport = entry?.isIntersecting !== false
restartBarrage(true)
if (isInViewport && isPageVisible) {
resumeBarrage()
return
}
pauseBarrage(false)
},
{ rootMargin: '80px 0px', threshold: 0 },
)
@ -258,7 +283,12 @@ watch(
[() => props.barrageList, () => locale.value],
() => {
currentIndex.value = 0
restartBarrage(true)
if (props.barrageList.length === 0) {
stopBarrage(true)
return
}
restartBarrage(false)
},
{ deep: false },
)
@ -296,6 +326,10 @@ watch(
will-change: auto;
}
.barrage-paused .barrage-item {
animation-play-state: paused;
}
.barrage-avatar {
width: 10vw;
border-radius: 50%;

View File

@ -1,6 +1,10 @@
<!-- 告白墙弹幕 -->
<template>
<div ref="containerRef" class="barrage-root" :class="{ 'barrage-lite': liteAnimations }">
<div
ref="containerRef"
class="barrage-root"
:class="{ 'barrage-lite': liteAnimations, 'barrage-paused': isBarragePaused }"
>
<div
v-for="(track, index) in visibleTracks"
:key="index"
@ -127,6 +131,7 @@ const tracks = ref([[], []])
const containerRef = ref(null)
const idCounter = ref(0)
const currentIndex = ref(0)
const isBarragePaused = ref(false)
const liteAnimations = shouldUseLiteAnimations()
const visibleTracks = computed(() => tracks.value.filter((track) => track.length > 0))
const effectiveMaxItems = computed(() => (liteAnimations ? Math.min(props.maxItems, 1) : props.maxItems))
@ -204,7 +209,7 @@ function getNextBarrage() {
}
function canRunBarrage() {
return isPageVisible && isInViewport && props.barrageList.length > 0
return !isBarragePaused.value && isPageVisible && isInViewport && props.barrageList.length > 0
}
function stopBarrage(clearTracks = false) {
@ -236,9 +241,24 @@ function restartBarrage(clearTracks = false) {
startBarrage()
}
function pauseBarrage(clearTracks = false) {
isBarragePaused.value = true
stopBarrage(clearTracks)
}
function resumeBarrage() {
isBarragePaused.value = false
startBarrage()
}
function handleVisibilityChange() {
isPageVisible = !document.hidden
restartBarrage(true)
if (isPageVisible && isInViewport) {
resumeBarrage()
return
}
pauseBarrage(false)
}
function setupViewportObserver() {
@ -249,7 +269,12 @@ function setupViewportObserver() {
viewportObserver = new IntersectionObserver(
([entry]) => {
isInViewport = entry?.isIntersecting !== false
restartBarrage(true)
if (isInViewport && isPageVisible) {
resumeBarrage()
return
}
pauseBarrage(false)
},
{ rootMargin: '80px 0px', threshold: 0 },
)
@ -280,7 +305,12 @@ watch(
() => props.barrageList,
() => {
currentIndex.value = 0
restartBarrage(true)
if (props.barrageList.length === 0) {
stopBarrage(true)
return
}
restartBarrage(false)
},
{ deep: false },
)
@ -324,6 +354,10 @@ watch(
will-change: auto;
}
.barrage-paused .barrage-item {
animation-play-state: paused;
}
.avatar-pair {
position: relative;
display: flex;