维护记录
完成的工作
- 首页 hero 标题「海边」粒子化:新增
layouts/partials/home-title.html,<h1 class="home-title"> 里保留真实文字(SEO / 屏幕阅读器 / 无 JS 兜底)+ aria-hidden 的粒子 canvas + #pt-title-config;list.html 的首页第一页分支改为调用该 partial ✅ - 新增
assets/js/home-title.js:从 DOM 文字读 computed 宋体栈 / 颜色 / 字号,离屏 canvas 画加粗描边的字形蒙版(按 DOM 基线对齐),交给 WASM alloc/build 撒成 1.5 CSS px 网格的粒子 ✅ - 入场用 0.35s 交叉淡入(文字 opacity 0 / canvas opacity 1),粒子先
settle() 直接落位,不播 build() 的飞入 —— 标题画布太小,飞入会先出现「字没了、粒子还在几百 px 外」的空窗 ✅ - 常驻 30Hz 固定步长
tick() + putImageData;document.hidden、标题滚出视口、待机屏保 pt-bg-on 时暂停,恢复时重置计时 ✅ - 缩放(150ms 防抖)与深色切换(MutationObserver 监听
html.class)时重建字形与粒子,保持 .is-particle,不重播交叉淡入 ✅ - WASM 引擎
ripple_on 新增 mode 2:相位只取粒子目标位 x,横向 sin / 纵向 cos 组成很扁的椭圆轨道,叠加一个不可通约的次波;相位写成 kx − phase 保证浪从左向右推(4.2s 一圈、±0.8 / ±2.2 CSS px)✅ custom.css:.home-title 加系统宋体栈,canvas 上下各外扩 0.6rem、pointer-events: none、默认 opacity 0;.is-particle 做交叉淡入,reduced-motion 去掉过渡、print 强制文字并隐藏 canvas ✅- 降级:无 JS / wasm 失败 / build 失败 → 纯文字;
?pt-title=off、saveData 不拉 wasm;prefers-reduced-motion 只画一帧静态粒子、不跑循环。诊断写在 html[data-pt-title] 与 data-pt-title-particles|-step|-size ✅ - 窄屏适配(最初 375 档只有 2.25rem / 36px):1.5 CSS px 网格在 dpr 2 下是 step 3 / 边长 2,宋体细横画被采成噪点;JS 在「字号 < 40px 且 dpr 2」时退到 2px 无缝网格(step 2 / 边长 2),桌面 48px 与 dpr 3 手机仍保留缝隙 ✅
- 单测新增 4 项:同列相干、位移有界且相位前进、零振幅静止、向右传播;
cargo test 44 项全部通过 ✅ - 重建
particles.wasm + particles.sha256(34,118 字节)与 Tailwind 产物;./bin/preflight.sh 全绿 ✅ - 首轮实拍 1280 / 375 两档(@2x,字号放大前):粒子字形可辨、文字已淡出、canvas 尺寸 1360×145(桌面)/ 622×118(窄屏);
?pt-title=off 与粒子模式的 h1 盒子完全一致(292.5,120,680,53.328125),零位移 ✅ - 首轮 CDP 实测:粒子数 792 / step 3 / size 2,画布哈希 0.9s 内持续变化;深色切换后粒子颜色 23,23,23 → 255,255,255 且不重播淡入;滚出视口后哈希不变、滚回后恢复 ✅
- 应用户反馈把 hero 字号放大一倍:桌面 3rem → 6rem(96px)、窄屏 2.25rem → 4.5rem(72px);粒子数 792 → 2662 / 468 → 1587,canvas 1360×252 / 622×198;窄屏 72px 不再触发 2px 无缝回退,恢复留缝颗粒,字也更好认 ✅
AGENTS.md 增加硬性约束:模型不得读取 / 分析任何图片(截图、设计稿、照片等),除非用户当次明确要求;界面验收改用 DOM / computed style / CDP 文本与单元测试等可复现证据,截图只生成给用户看 ✅- 应用户反馈把粒子排列打散:WASM 新增
jitter_on(amp),build 后按 hash01(i) 给每颗粒子的目标位叠加一次确定性抖动(先 0.8 CSS px,后加到 1.0,dpr 2 下 ±2 设备 px;重复调用不叠加、重建可复现),字形仍是同一张蒙版,行波相位照旧取 tx;诊断新增 data-pt-title-jitter ✅ - 应用户反馈继续加强随机与流动:新增
size_jitter_on() 把统一 2px 方块变成 1–3px 混合、wobble_on() 给每颗粒子独立相位微动(±0.6 / ±0.9 CSS px,3s 循环,叠加在相干行波上,浪照旧向右推);诊断新增 data-pt-title-sizeJitter / wobble;单测 +3(48 项),wasm 37,327 字节 ✅
遇到的问题
- 宋体细横画在 2px 采样网格上会漏采,3rem 下「边」字明显断续 ⚠️ - 离屏蒙版加
ink: 0.6 的 strokeText(lineJoin: round),并把 pitchCss 从 2 调到 1.5;dpr 2 下 step 3 / 边长 2,字形完整且仍看得出颗粒。 sin(kx + phase) 的浪实际向左跑,与验收的「从左滚到右」相反 ⚠️ - 改成 kx − phase,并加单测 ripple_wave_mode_travels_to_the_right 把方向锁住。- 最初按墨迹包围盒在画布居中,实测粒子比 DOM 文字高 3.4 CSS px,交叉淡入时文字会往上跳 ⚠️ - 用
line-height + fontBoundingBoxAscent/Descent 算出与 DOM 相同的基线再落笔,实测墨迹中心差 <0.5 CSS px。 - canvas 是 replaced element:绝对定位下
left/right + width:auto 不会撑开,会退回 300×150 固有尺寸 ⚠️ - 显式给 width: 100% 与 height: calc(100% + 1.2rem)。 --screenshot --virtual-time-budget 下 rAF 与 CSS 过渡都不推进,截到的是「文字尚未淡出」的静态帧,差点误判为粒子没生效 ⚠️ - 改用 CDP 实时驱动(导航 → 等 2.5s → Page.captureScreenshot / Runtime.evaluate),并读取 data-pt-title 与 canvas 像素。- 用
deno 直接跑任务会被本机 mise shim 拦下(未设置全局版本)⚠️ - 用仓库 vendored 的 .tools/deno/bin/deno(bin/lib.sh 本来就是这么做的)。
下次建议
- 若后续觉得起伏太克制,可把
waveAmpX/Y 调到 1.2 / 3;想更「颗粒」就把 pitchCss 调回 1.8–2.0(粒子会更少、缝更大),不必动引擎。 - 可选的 hover 散开:引擎已有
pointer_set,给标题 canvas 接一段 pointermove 即可复用屏保的斥力场。 - 标题文案将来若变化,建议在 375 与 1280 各截一张图做字形回归(
?pt-title=off 对照最直观)。