Commit b85ac222 by luoqi

feat(矩阵): 悬停 = 瞄准 —— 角括号准星 + 行列十字

产品:「鼠标可以标出忽大忽小的瞄靶,其他动效奇思妙想一下」。
点下去已经有一串粒子飞向助手(pacStreamFly)——悬停这一段就接在它前面:**瞄准,然后发射**。

── 一件事的两半 ────────────────────────────────────────────────
· **准星**(globals.css `.pac-reticle`)—— 你正指着**哪一格**
  四角角括号,零 DOM 零 SVG(8 条渐变画四个角,随格子尺寸自适应)。
  两段动画:先「咬合」(0.18s 从格子外一口收进来),再「呼吸」(2.4s,±1.5px)。
  ️ 呼吸幅度刻意只有 1.5px —— 再大就从"锁定中"变成"这里出错了"。
  按下时收紧 3.5px 并停住:扣扳机的那一下,紧接着交给粒子。
· **十字**(组件里的行/列高亮)—— 那一格**是谁 × 什么时候**
  8 行 × 6 列,格子离行头和列头都很远;光把格子框起来答不了后一半。
  命中行:行标签右移 2px + 上主色(位移比变色更早被眼睛捕捉到,而那列字只有两三个)。
  命中列:列头圆点长大一圈,当这条轴的刻度指针。
  其余整片退让(opacity 35~45%), 不是给命中的加亮 —— 加亮会动到色相,而色相本身是信息。

── ️ 几条老纪律都没破 ─────────────────────────────────────────
· 「hover  别换底色,会在渐变面上戳一个洞」管的是**一格**单独换底;
  这里是**一行 + 一列**,读出来是十字不是洞,且瞬时、 从不由数量驱动。白纱封顶 8%。
· 实线框留给「**已经选中**」,准星是「**正指着**」—— 两个状态长得一样就分不出,
  而这一点下去人就发出去了。
· focus-visible 仍走实线 outline:键盘可达性 不能只靠一段动画。
· `prefers-reduced-motion`:准星照旧画出来(它是**信息**),只是不动。

🔴 `pac-reticle` 由 **state** 挂, 不写成 CSS 的 `:hover::after`:
  0 人的格子是 disabled,而 disabled 的 hover 跨浏览器不一致 —— 走 state 才拦得住;
  且准星与十字必须同生同灭,同一个来源才不会一个亮一个不亮。
️ 整片渐变面上挂 `onMouseLeave` 兜底清空:只靠每格的 leave,鼠标直接飞出容器时
  有概率被下一个 enter 抢先,十字会留在原地不走。

验证:tsc 绿,next build 通过;产物 CSS 里 `.pac-reticle::after`、两个 keyframes、
`:active` 与 reduced-motion 分支都在,白纱编成 #ffffff14。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent e6558355
...@@ -297,6 +297,73 @@ body { ...@@ -297,6 +297,73 @@ body {
.pac-stream-dot { display: none; } .pac-stream-dot { display: none; }
} }
/* ── 初选矩阵:悬停 = **瞄准**(点下去才是发射,接上面那串粒子)────────────
⚠️ 这一整段只在 `pool-matrix` 用。它做的是**一件事的两半**:
· 准星(这里)—— 你正指着**哪一格**;
· 十字(组件里的行/列高亮)—— 那一格**是谁 × 什么时候**。
8 行 × 6 列的表,格子离行头和列头都很远,光把格子框起来答不了后一半。
🔴 用**角括号**而不是整圈实线框:
实线框留给「**已经选中**」(组件里 `selected` 那一支)。两个状态长得一样的话,
"我正指着" 和 "我已经选了" 就分不出来 —— 而这一点下去人就发出去了。
⚠️ 画在**伪元素**上,⛔ 不画在按钮自己的 background 上:
按钮的底色要留给十字那层薄纱,两者叠在一起时得各归各的图层。
⚠️ 这个类由组件**按 state 挂**(只挂在瞄中的那一格),所以下面 `::after` ⛔ 不加 `:hover` ——
0 人的格子是 disabled,而 disabled 的 hover 跨浏览器不一致,靠 CSS 拦不住。 */
.pac-reticle { position: relative; }
.pac-reticle::after {
content: '';
position: absolute;
inset: 2px;
pointer-events: none;
--pac-ret-c: var(--color-brand-600);
--pac-ret-b: 1.5px; /* 线宽 */
--pac-ret-l: 7px; /* 每个角的臂长 */
/* 四个角 = 8 条渐变(每角一横一竖)。⚠️ 这样零 DOM、零 SVG,
而且随格子尺寸自适应(角永远贴在四角,中间留空)。 */
background-image:
linear-gradient(var(--pac-ret-c), var(--pac-ret-c)),
linear-gradient(var(--pac-ret-c), var(--pac-ret-c)),
linear-gradient(var(--pac-ret-c), var(--pac-ret-c)),
linear-gradient(var(--pac-ret-c), var(--pac-ret-c)),
linear-gradient(var(--pac-ret-c), var(--pac-ret-c)),
linear-gradient(var(--pac-ret-c), var(--pac-ret-c)),
linear-gradient(var(--pac-ret-c), var(--pac-ret-c)),
linear-gradient(var(--pac-ret-c), var(--pac-ret-c));
background-repeat: no-repeat;
background-size:
var(--pac-ret-l) var(--pac-ret-b), var(--pac-ret-b) var(--pac-ret-l),
var(--pac-ret-l) var(--pac-ret-b), var(--pac-ret-b) var(--pac-ret-l),
var(--pac-ret-l) var(--pac-ret-b), var(--pac-ret-b) var(--pac-ret-l),
var(--pac-ret-l) var(--pac-ret-b), var(--pac-ret-b) var(--pac-ret-l);
background-position:
0 0, 0 0,
100% 0, 100% 0,
0 100%, 0 100%,
100% 100%, 100% 100%;
/* ⭐ 两段:先**咬合**(从格子外一口收进来),再**呼吸**(产品要的"忽大忽小")。
⚠️ 呼吸幅度只有 1.5px、周期 2.4s —— 再大就从"锁定中"变成"这里出错了"。 */
animation:
pacReticleLock 0.18s cubic-bezier(0.2, 1.3, 0.4, 1),
pacReticleBreathe 2.4s ease-in-out 0.18s infinite;
}
/* 扣扳机的那一下:准星收紧,紧接着交给粒子(pacStreamFly) */
.pac-reticle:active::after { inset: 3.5px; animation: none; transition: inset 0.08s ease-out; }
@keyframes pacReticleLock {
from { inset: -2px; opacity: 0; }
to { inset: 2px; opacity: 1; }
}
@keyframes pacReticleBreathe {
0%, 100% { inset: 2px; opacity: 1; }
50% { inset: 3.5px; opacity: 0.7; }
}
@media (prefers-reduced-motion: reduce) {
/* 无障碍:准星照旧画出来(它是**信息**:你指着哪一格),只是不动 */
.pac-reticle::after { animation: none; }
}
/* ── 「分一批新的 · 里世界」传送门 —— **2026-08-11 删** ────────────── /* ── 「分一批新的 · 里世界」传送门 —— **2026-08-11 删** ──────────────
这里原来是那版全屏「打地鼠」的动效:一个 200×200 的小圆从按钮位置 这里原来是那版全屏「打地鼠」的动效:一个 200×200 的小圆从按钮位置
`transform: scale()` 放大盖满全屏(`.pac-portal-disc`),内容随后 `opacity` 淡入 `transform: scale()` 放大盖满全屏(`.pac-portal-disc`),内容随后 `opacity` 淡入
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment