# Ultracode 动画参数手册 > 这份文档解释轨道里每一个数字、每一条公式代表什么,以及调大/调小会带来什么变化。 > 代码位置:`lib/client.js` → `paintDither()` / `DitherCanvas()`。 --- ## 0. 一帧画面由四层叠成 | 层 | 画什么 | 代码 | |---|---|---| | ① 底晕 | 右侧的淡紫色渐变,跟随充能前沿 | `ctx.createLinearGradient` + `fillRect` | | ② 紫格 | 每格一块淡紫圆角方块,决定“有像素” | `fillRoundRect`(第一次) | | ③ 亮格 | 波峰经过时把该格提亮(脉冲/前沿闪白);深色用更亮的紫,浅色用亮白 | `fillRoundRect`(第二次) | | ④ DOM | 轨道、填充宽度、旋钮、刻度点 | `EffortSlider` 的 JSX + CSS | --- ## 1. 时间轴 | 时间 | 发生什么 | |---|---| | 0 ms | 浮层挂载,`progress = 0`,波前在最右端 | | 0 → 1600 ms | `progress` 从 0 线性到 1,像素从右往左逐格出现 | | 1600 ms 之后 | `progress = 1`,只剩“待机脉冲”,每行独立向左流动 | | 切档瞬间 | 标题 300 ms 上浮 + 300 ms 轻微抖动/RGB 错位 | | 拖动/点击切档 | 填充宽度、旋钮位置 120 ms 缓动(`cubic-bezier(.2,.8,.2,1)`) | **`progress`**:0 = 一格都没有,1 = 铺满。它只由 `(now - start) / 1600` 决定,**不受系统“减少动态效果”影响**(代码刻意忽略了该媒体查询)。 --- ## 2. 尺寸与网格 | 常量 | 值 | 含义 | 调大/调小 | |---|---|---|---| | `TRACK_H` | 28 px | 轨道高度 | 改高度要同步改旋钮 | | `KNOB_W × KNOB_H` | 26 × 32 px | 旋钮尺寸(比轨道高 4px,上下各盖住 2px) | 比 `TRACK_H` 矮就会漏出纹理 | | `TRACK_W` | 276 px | 308 − 16×2,画布宽度 | — | | `CELL` | `TRACK_H / 6` ≈ **4.67 px** | 一格的边长(6 行等分) | 改 6 为 5 → 块更大更粗 | | `rows` | **6** | 行数 | 行数越少越“粗颗粒” | | `cols` | `ceil(276 / 4.67)` = **60** | 列数 | 随 `CELL` 变化 | | 格子总数 | **360** | 60 × 6 | 每帧遍历 360 格 | | `size` | 深色 `CELL × 0.72` ≈ **3.36 px**;浅色 `CELL × 0.88` | 方块实际边长 | 0.72 越大越满,1.0 就没有格缝 | | 圆角 | 1.1 px(底)/ 0.7 px(亮格) | 方块圆角 | 越大越圆润 | > 一格 = 4.67 px,方块 = 3.36 px,**缝隙 ≈ 1.31 px**,这就是“像素颗粒感”的来源。 --- ## 3. 铺开(sweep)相关 | 变量 | 公式 | 含义 | |---|---|---| | `front` | `1 - progress` | 波前位置:1 = 最右,0 = 最左 | | `t` | `x / w` | 该格在轨道上的横向位置(0 左,1 右) | | `edgeJit` | `(hash - 0.5) × 0.05` | 波前边缘 ±2.5% 的参差,避免一条死板的直线 | | 跳过条件 | `t < front + edgeJit` | 还没被扫到的格子直接不画 | | `appear` | `0.35 + 0.65 × clamp((t - front - edgeJit) / 0.04)` | 刚被扫过的格子在 4% 宽度内从 35% 亮到 100%,形成“逐个亮起” | **调参**:`0.04` 调大 → 出现得更柔和、拖尾更长;调小 → 更像硬边扫描。 --- ## 4. 每格的亮度(核心公式) ``` pulse = max( (1-u1)^(2.6~6), (1-u2)^3.4 × (0.55~1.0) ) × 0.85 u1、u2 来自两列波,ω2 = ω1 × N2/N1(相速度相同 → 整体刚性左移) flick = valueNoise(col, row, time, 1.5, seed=5) // 0..1 的慢噪声 gap = 0.6 + 0.4 × clamp((flick - (1-fade)×0.5) / 0.3) // 格子“在不在” vis = clamp((t - 0.2) / 0.18) // 20% 之前渐隐 ghost = 0.06 × clamp((flick-0.5)/0.5) × (1-vis) // 左端若隐若现的余烬 base = ditherAlphaAt(x) = (0.92 + 0.08t) × (0.25 + 0.75·hash) // 每格固有随机 gridAlpha = base × baseAlpha × gap × appear × 1.15 × (0.9 + 0.1·hash) × strength ``` | 数值 | 作用 | 观感 | |---|---|---| | `0.2` / `0.18` | “到 Medium(20%)才开始消失,38% 满亮” | 调大 0.2 → 更早变空 | | `0.04`(baseAlpha 下限) | 最左端仍保留 4% 亮度 | 保证不是纯黑 | | `1.15` | 深色整体增益 | 调大更亮更实 | | `strength` | 设置里“纹理强度”:soft = 0.75,normal = 1 | — | | `gap` 的 0.6 | 最稀时的“出格率” | 调大 → 左端格子更多 | **两层颜色** | 层 | 颜色 | alpha | 含义 | |---|---|---|---| | 紫格 | `rgb(208,192,246)` | `gridAlpha × 0.55` | 常驻底色像素 | | 亮格 | `rgb(220,204,255)` | `gridAlpha × (0.18 + 0.62 × whiteAmt)` | 脉冲/前沿的高光 | **`whiteAmt`(亮格强度)** ``` whiteAmt = pulse × (0.35 + 0.65t) × (0.55 + 0.45·hash) × strength if (progress < 0.999) whiteAmt = max(whiteAmt, flash) flash = clamp(1 - (t - front) / 0.16) × (0.55 + 0.45·hash) ``` - `0.35 + 0.65t`:越靠右,脉冲越亮(左边克制) - `flash` 的 `0.16`:铺开时前沿 16% 宽度内的格子会“闪一下”,形成递进 - `0.18 + 0.62`:亮格从 18% 到 80% 透明度,不会烧成纯白 --- ## 5. 待机脉冲(铺满之后) | 变量 | 值/范围 | 含义 | |---|---|---| | `rowWaves` | 3 ~ 7 | 每行在轨道上同时存在几个波峰(按行随机) | | `rowSpeed` | 5 ~ 15(弧度/秒) | 每行波移动速度(按行随机,所以行与行不同步) | | `rowPhase` | 0 ~ 2π | 每行初始相位 | | `pulse` 指数 | 第 1 列 2.6–6(按格)、第 2 列固定 3.4 | 只改单格“陡缓”,**不改峰值时刻**,所以不会影响方向 | | 第二列波 | `N2 = 2–5`,`ω2 = ω1 × N2/N1` | 两列波同相速度:叠加只是让图案更随机,整体仍刚性向左,不会互相追尾 | | 合成系数 | `× 0.85` | 两列取 max 会把亮度抬高,压回来保留颗粒对比 | | 相位 | 只由 `列 × 行 × 时间` 决定 | **不能掺每格随机量**:曾加过 0–2.2 rad 的每格抖动,比相邻格相位差(0.31–0.73 rad)还大,导致 34.5% 的相邻格反向亮起 | | 方向 | 恒定向左 | 由 `phase` 的定义保证 | > 这就是“每行各走各的波、一律从右往左”的实现。 --- ## 6. 底晕(wash) | 变量 | 值 | 含义 | |---|---|---| | `k` | `1 - progress` | 充能前沿 | | 停止点 1 | `0` | 左端透明 | | 停止点 2 | `k - 0.08` | 前沿前 8% 仍透明 | | 停止点 3 | `k + 0.18` | 前沿后 18% 到 30% 紫 | | 停止点 4 | `1` | 最右 75% 紫 | | 颜色 | 深色 `rgba(112,78,162, …)` / `rgba(116,84,170, …)`;浅色 `rgba(142,118,192, 0 / 0.45 / 1)` | 紫色底 | **含义**:底晕跟着波前长出来,不是固定渐变;铺满后 `k=0`,整条轨道覆盖。 --- ## 7. 触发与重播规则 | 变量 | 含义 | |---|---| | `animKey` | `sessionId + model + index + ultra`,标识“哪个状态” | | `lastPlayedKey` | 上一次**播完**的 `animKey` | | `playSeq` | 用户主动拖到 Ultracode 时 +1,强制重播 | | 打开时 | `animKey === lastPlayedKey` → 直接终帧,不重播 | | 拖到满格 | `onPreview` 里命中 `ultra && next !== index` → 清空 `lastPlayedKey` + `playSeq+1` | | 松手 | **不触发**任何重播(拖入即算一次) | --- ## 8. 文字与其它动画 | 元素 | 时长/参数 | 含义 | |---|---|---| | 档位文字上浮 | 300 ms,`cubic-bezier(.2,.85,.25,1)` | 切档时从下方 85% 升上来 | | 抖动/RGB 错位 | 300 ms,2 步 | `dces-glitch`,幅度 ±0.6 px | | 填充宽度 | 120 ms | 拖拽时跟手,松手缓动 | | 旋钮位置 | 120 ms | 同上 | | 刻度点 | Ultracode 时 `opacity: 0` | 拉满后不显示 | --- ## 9. 浅色主题(0.9.2) 浅色的配色是按官方参考实测重写的,**动画管线与深色完全共用**(铺开、双波脉冲、前沿闪白、外焰一样不少),只换参数。 | 变量 | 值 | 含义 | |---|---|---| | 网格 | 6 行 × 60 列(与深色同一套 `CELL`) | — | | `size` | `CELL × 0.88` | 缝约 12%,透出底渐变形成网格感 | | 底渐变 | `rgba(142,118,192, 0 → 0.45 → 1)`,跟充能前沿 | 左端暖灰、右端饱和紫 | | 出现 | `lt = clamp((t - 0.05) / 0.2)` | 左端 5% 干净,之后淡入 | | 块色 p50 | `(187-32t, 184-46t, 206+4t)` | 中位色随 t 变深变紫 | | 色差 | `sp = 22 + 66t`,`off = (u - 0.42) × sp`,正值再 ×1.1 | 左端几乎同色,右端拉开深/亮两极 | | 偏移映射 | 偏深:三通道同步下压;偏亮:g×1.05、b×0.65 | 偏深更紫、偏亮更白 | | 外焰 | `density = clamp((t-0.12)/0.35)`,`presence = clamp((0.2+0.8·density − flicker)/0.2)` | 左侧像素随机明灭,右侧恒定实心 | | 亮格 | `rgb(247,243,255)`,alpha `= lAlpha × (0.12 + 0.88·whiteAmt)`,内缩 14% | 波峰/前沿闪白 | | 合并块 | 2×2 占 2%、2×1 占 4%、1×2 占 2% | 以规则 1×1 为主,否则网格会碎 | > 实测依据:官方截图逐列均值 + 块级 p10/p50/p90 分位数,各段误差 ≤ 11(官方自身列间波动就有 ±13)。 --- ## 10. 已知冗余(不影响运行) | 位置 | 问题 | |---|---| | `var white = ...` | 定义了但没用到 | | `var level = ...` | 计算了但没参与绘制 | 清理它们不会改变任何观感。 --- ## 11. 快速调参对照 | 你想要 | 改哪里 | |---|---| | 像素更亮/更实 | `gridAlpha` 里的 `1.15`,或紫格 alpha 的 `0.55` | | 像素更密 | `CELL` 改小(或行数 6 → 7) | | 块更大 | `size` 的 `0.72` 调大 | | 铺开更快 | `1600`(ms) | | 左边更空 | `vis` 的 `0.2` 调大 | | 脉冲更明显 | `0.62`(亮格 alpha 系数)调大 | | 脉冲形状更尖 | `pulse` 的指数 3.2 调大(整行统一,别按格改) | | 底晕更紫 | wash 的 `0.75` / `0.3` 调大 | | 文字动画更轻 | `dces-glitch` 的 `.3s` 或直接去掉 class |