/** * 命中反馈的整屏层:边沿泛红 + 条上的读数。 * * # 大号数字刻意不放在画面正中 * * 第一版把 `-7 / 100 → 93` 放在正中,结果它正好压在瞄准点上——被打中的那一下恰恰是最 * 需要看着准星的时候。现在数字由 `VideoSurface` 画在**准星旁边**(跟着瞄准点走,不挡 * 目标),这里只留边沿泛红与顶部一条读数。 * * # 泛红为什么贴着边沿 * * 旧版是一整块径向渐变盖在画面上,等于在最要紧的一瞬间把画面调暗。现在颜色只在四条边上 * 用 `inset` 阴影渗进来,中间保持原样——"被打到"这件事从四周传来,而不是挡在眼前。 * * # 不给方向 * * 命中事件里的 `index` 是**哪一条上报先发现了掉血**,不是"从哪一侧打来的"(主板一次广播 * 四条、血值相同,谁先到达取决于 UDP)。把它画成方向箭头会是一条看起来很专业、实际上 * 完全错误的线索,比不画糟得多。所以这一层是对称的。 * * `key={hit.seq}` 是让 CSS 动画每次都从头开始的关键:同一个元素上换属性不会重启动画, * 于是第二下命中就再也闪不出来了——一个"第一次有效"的 bug。 */ import type { HitPulseRecord } from "../lib/store"; import type { HitFeedbackConfig } from "../lib/types"; interface Props { hit: HitPulseRecord | null; config: HitFeedbackConfig; /** 这一下是不是打在我这一边(裁判端两边都显示,但只有相关那一路闪)。 */ relevant: boolean; label?: string; } export function HitFlash({ hit, config, relevant, label }: Props) { if (!hit || !relevant || !config.flash) return null; // 强度换算成"边沿渗进来多深"。上限压在 70px:再深就开始吃掉画面。 const depth = Math.round(38 + 34 * Math.min(2, Math.max(0.2, config.intensity))); return (
); }