Spaces:
Sleeping
Sleeping
refactor(HistoryDatabase): enhance card expansion logic with pending state management
Browse files- Introduced a pending state to track the target expansion state during animations.
- Improved debounce handling to ensure smoother transitions and prevent rapid state changes.
- Updated logic to check for new pending states after animations complete, enhancing responsiveness and stability.
frontend/src/components/HistoryDatabase.vue
CHANGED
|
@@ -106,6 +106,7 @@ const historyContainer = ref(null)
|
|
| 106 |
let observer = null
|
| 107 |
let isAnimating = false // 动画锁,防止闪烁
|
| 108 |
let expandDebounceTimer = null // 防抖定时器
|
|
|
|
| 109 |
|
| 110 |
// 卡片布局配置 - 调整为更宽的比例
|
| 111 |
const CARDS_PER_ROW = 4
|
|
@@ -316,35 +317,60 @@ onMounted(() => {
|
|
| 316 |
observer = new IntersectionObserver(
|
| 317 |
(entries) => {
|
| 318 |
entries.forEach((entry) => {
|
| 319 |
-
// 如果正在动画中,忽略新的触发
|
| 320 |
-
if (isAnimating) return
|
| 321 |
-
|
| 322 |
const shouldExpand = entry.isIntersecting
|
| 323 |
|
| 324 |
-
//
|
| 325 |
-
|
| 326 |
|
| 327 |
-
// 清除之前的防抖定时器
|
| 328 |
if (expandDebounceTimer) {
|
| 329 |
clearTimeout(expandDebounceTimer)
|
| 330 |
expandDebounceTimer = null
|
| 331 |
}
|
| 332 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
// 使用防抖延迟状态切换,防止快速闪烁
|
| 334 |
// 展开时延迟较短(50ms),收起时延迟较长(200ms)以增加稳定性
|
| 335 |
const delay = shouldExpand ? 50 : 200
|
| 336 |
|
| 337 |
expandDebounceTimer = setTimeout(() => {
|
| 338 |
-
//
|
| 339 |
if (isAnimating) return
|
| 340 |
|
|
|
|
|
|
|
|
|
|
| 341 |
// 设置动画锁
|
| 342 |
isAnimating = true
|
| 343 |
-
isExpanded.value =
|
|
|
|
| 344 |
|
| 345 |
-
// 动画完成后解除锁定
|
| 346 |
setTimeout(() => {
|
| 347 |
isAnimating = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
}, 750)
|
| 349 |
}, delay)
|
| 350 |
})
|
|
|
|
| 106 |
let observer = null
|
| 107 |
let isAnimating = false // 动画锁,防止闪烁
|
| 108 |
let expandDebounceTimer = null // 防抖定时器
|
| 109 |
+
let pendingState = null // 记录待执行的目标状态
|
| 110 |
|
| 111 |
// 卡片布局配置 - 调整为更宽的比例
|
| 112 |
const CARDS_PER_ROW = 4
|
|
|
|
| 317 |
observer = new IntersectionObserver(
|
| 318 |
(entries) => {
|
| 319 |
entries.forEach((entry) => {
|
|
|
|
|
|
|
|
|
|
| 320 |
const shouldExpand = entry.isIntersecting
|
| 321 |
|
| 322 |
+
// 更新待执行的目标状态(无论是否在动画中都要记录最新的目标状态)
|
| 323 |
+
pendingState = shouldExpand
|
| 324 |
|
| 325 |
+
// 清除之前的防抖定时器(新的滚动意图会覆盖旧的)
|
| 326 |
if (expandDebounceTimer) {
|
| 327 |
clearTimeout(expandDebounceTimer)
|
| 328 |
expandDebounceTimer = null
|
| 329 |
}
|
| 330 |
|
| 331 |
+
// 如果正在动画中,只记录状态,等动画结束后处理
|
| 332 |
+
if (isAnimating) return
|
| 333 |
+
|
| 334 |
+
// 如果目标状态与当前状态相同,不需要处理
|
| 335 |
+
if (shouldExpand === isExpanded.value) {
|
| 336 |
+
pendingState = null
|
| 337 |
+
return
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
// 使用防抖延迟状态切换,防止快速闪烁
|
| 341 |
// 展开时延迟较短(50ms),收起时延迟较长(200ms)以增加稳定性
|
| 342 |
const delay = shouldExpand ? 50 : 200
|
| 343 |
|
| 344 |
expandDebounceTimer = setTimeout(() => {
|
| 345 |
+
// 检查是否正在动画
|
| 346 |
if (isAnimating) return
|
| 347 |
|
| 348 |
+
// 检查待执行状态是否仍需要执行(可能已被后续滚动覆盖)
|
| 349 |
+
if (pendingState === null || pendingState === isExpanded.value) return
|
| 350 |
+
|
| 351 |
// 设置动画锁
|
| 352 |
isAnimating = true
|
| 353 |
+
isExpanded.value = pendingState
|
| 354 |
+
pendingState = null
|
| 355 |
|
| 356 |
+
// 动画完成后解除锁定,并检查是否有待处理的状态变化
|
| 357 |
setTimeout(() => {
|
| 358 |
isAnimating = false
|
| 359 |
+
|
| 360 |
+
// 动画结束后,检查是否有新的待执行状态
|
| 361 |
+
if (pendingState !== null && pendingState !== isExpanded.value) {
|
| 362 |
+
// 延迟一小段时间再执行,避免太快切换
|
| 363 |
+
expandDebounceTimer = setTimeout(() => {
|
| 364 |
+
if (pendingState !== null && pendingState !== isExpanded.value) {
|
| 365 |
+
isAnimating = true
|
| 366 |
+
isExpanded.value = pendingState
|
| 367 |
+
pendingState = null
|
| 368 |
+
setTimeout(() => {
|
| 369 |
+
isAnimating = false
|
| 370 |
+
}, 750)
|
| 371 |
+
}
|
| 372 |
+
}, 100)
|
| 373 |
+
}
|
| 374 |
}, 750)
|
| 375 |
}, delay)
|
| 376 |
})
|