fix(做对了): 点评轮盘改成烟花开始 1.5 秒就弹,不再等烟花落完干等三四秒
Deploy / deploy (push) Canceled after 0s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-26 15:21:29 +08:00
1 parent 6176b80ad9
commit 7ff776b9d8
2 files changed
+12 -53

No files matched your search

@@ -36,7 +36,7 @@ const { celebrate } = useFireworks()
// 只对已经能评价、且还没评过的人弹:后端要求先有 AC 才收评价,这里刚 AC 完正好;
// mine 非 null 说明早就评过了,别再打扰。
//
// 点评是强制的,但弹窗要等烟花放完才出来(两三秒,加上查一次「评过没有」),而换题会把
// 点评是强制的,但弹窗要等烟花放一会儿才出来(1.5 秒,加上查一次「评过没有」),而换题会把
// 定时器取消(见下面换题的 watch)—— 「下一题」在通过的那一刻就出现了,学生手快点
// 下去,点评就跳过了。所以这段时间里欠着一次点评(reviewOwed),离开这道题的导航
// 先拦下来:立刻弹点评,选完再去原来要去的地方。
@@ -130,23 +130,10 @@ function continueAfterReview() {
}
/**
* 烟花放完再弹点评,两边一个时间(十种烟花长短不一,两到三秒)。
* 每次通过记一个号,换题、被导航拦下提前弹了,就把号作废,放完也不再弹。
* 烟花开始后固定 1.5 秒弹点评,不等它放完 —— 十种烟花长短不一,等放完的话慢的那几种
* 要干等三四秒。没放完的半场盖在弹窗上面接着放(useFireworks 的 Z_INDEX)。
*/
let reviewAfterFireworks = 0
function showCommentPanelAfter(fireworks: Promise<void>) {
const run = ++reviewAfterFireworks
fireworks.then(() => {
if (run === reviewAfterFireworks) openReview()
})
}
function cancelCommentPanel() {
reviewAfterFireworks++
}
// 离开题目页时烟花那边当作放完了,不能接着去查、去弹
onScopeDispose(cancelCommentPanel)
const { start: showCommentPanelDelayed, stop: cancelCommentPanel } = useTimeoutFn(openReview, 1500, { immediate: false })
/**
* 只拦「离开这道题」:换 query(左栏切页签会改 `?tab=`)不算。弹窗已经开着的时候
@@ -241,7 +228,7 @@ watch(
if (result !== SubmissionStatus.accepted) return
// 3. 烟花放完弹出评价框。比赛里不打扰;题单里 1.5 秒后要跳回题单页,弹了也会被冲掉
// 3. 烟花放一会儿弹出评价框。比赛里不打扰;题单里 1.5 秒后要跳回题单页,弹了也会被冲掉
// (评价是欠着的这件事马上记下:方块还在亮的时候离开这道题,也要先评)
if (ctx.value.reviewAfterAccepted) reviewOwed.value = true
@@ -249,8 +236,8 @@ watch(
const id = submission.value?.id
void afterCasesRevealed(id).then(() => {
if (submission.value?.id !== id) return
const fireworks = celebrate()
if (ctx.value.reviewAfterAccepted) showCommentPanelAfter(fireworks)
celebrate()
if (ctx.value.reviewAfterAccepted) showCommentPanelDelayed()
})
if (ctx.value.backToProblemSet) {
@@ -74,8 +74,8 @@ const PALETTES = [
const RAINBOW = [COLORS.pink, COLORS.orange, COLORS.gold, COLORS.green, COLORS.blue, COLORS.purple]
/**
* 盖在弹层上面放。点评弹窗平时等烟花放完才出来(见 celebrate 的返回值),但学生在放的时候
* 点「下一题」,点评会被提前叫出来 —— 默认的 zIndex 100 在 Naive 弹层(2000 起)底下,
* 盖在弹层上面放。点评弹窗在烟花开始后固定一段时间就出来(见 SubmissionEffects),
* 那时烟花多半还没放完 —— 默认的 zIndex 100 在 Naive 弹层(2000 起)底下,
* 剩下的半场就被遮罩挡住了。画布本身 pointer-events: none,不挡点评的点击。
*/
const Z_INDEX = 3000
@@ -113,11 +113,6 @@ export function useFireworks() {
// 动画期间创建的定时器,组件卸载时统一清理,避免离开页面后动画继续
const timers = new Set<ReturnType<typeof setTimeout>>()
let disposed = false
// 这一场最后一次发射的 promise:canvas-confetti 同一块画布上的粒子是一场动画,
// 后发的接进去,promise 在所有粒子都落完的时候才 resolve
let lastShot: Promise<unknown> | null = null
// 还没发出去的异步准备(表情包位图在解码),算「还在放」
let preparing = 0
function runLater(fn: () => void, ms: number) {
const id = setTimeout(() => {
@@ -151,22 +146,7 @@ export function useFireworks() {
}
function fire(options: confetti.Options) {
lastShot = confetti({ zIndex: Z_INDEX, ...options }) ?? lastShot
}
/** 定时器都走完、最后一把粒子也落完。离开页面(定时器被清掉)就当放完了 */
function finished(): Promise<void> {
return new Promise((resolve) => {
const check = () => {
if (disposed) return resolve()
if (timers.size > 0 || preparing > 0) {
setTimeout(check, 50)
return
}
Promise.resolve(lastShot).then(() => resolve())
}
check()
})
void confetti({ zIndex: Z_INDEX, ...options })
}
/** 没有真火箭:沿上升路线一路撒往下掉的小火星,当作尾焰。x0 → x、从屏幕底到 y */
@@ -429,7 +409,6 @@ export function useFireworks() {
// 效果10: 表情包烟花 —— 一左一右炸出两团表情
() => {
preparing++
loadEmojiShapes()
.then((emojiShapes) => {
if (disposed) return
@@ -447,19 +426,12 @@ export function useFireworks() {
runLater(() => fire({ ...options, origin: { x: 0.68, y: 0.42 } }), 300)
})
.catch(() => {})
.finally(() => preparing--)
},
]
/** 最多等这么久:粒子落完的回调万一没来,别让点评弹窗一直不出来 */
const MAX_WAIT_MS = 4000
/**
* 触发随机烟花效果,返回的 promise 在这一场放完时 resolve(点评弹窗等它)
*/
function celebrate(): Promise<void> {
/** 触发随机烟花效果 */
function celebrate() {
pick(fireworkTypes)()
return Promise.race([finished(), new Promise<void>((resolve) => setTimeout(resolve, MAX_WAIT_MS))])
}
return {