mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 21:05:51 +00:00
fix(effort): 波纹参数调优——铺满左侧 + 速度调慢 + 全面板有底色
用户反馈三个问题: 1. "低峰部分没有颜色变化" → intensity ≤ 0.1 返回 transparent 导致波谷 位置看不见。改为永不返回 transparent,最低档 #0a0d1a 作为面板 底色(暗紫黑海洋),波峰在底色上流动。 2. "波浪速度太快" → time 系数 0.012 → 0.004(约 1/3 速)。波峰移动 速度从 34 cell/s 降到 11 cell/s,每帧颜色变化从 45% 降到 36%。 3. "波浪只到中间部分,没覆盖左侧" → falloff 覆盖半径 40 → 90。 震源 x=65,左侧 dist=65 < 90,波纹可达最左端(约 30-50% 覆盖)。 色阶调整: - 删除 transparent 档,新增 #0a0d1a 作最暗档(底色) - 最高档从 #8aa0ff(高光)改为 #5769F7(suggestion),避免与 文字 overlay 同色互相吞噬 - 7 档颜色:#0a0d1a → #15182b → #1f2543 → #2a3360 → #3a4582 → #4a5bb0 → #5769F7 测试:删除 transparent 期望,改为期望具体颜色(#0a0d1a 等)。 新增"覆盖半径扩大"测试验证 dist=65 仍有非最暗颜色。 Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>
This commit is contained in:
@@ -10,25 +10,20 @@ import {
|
||||
} from '../rippleAnimation.js'
|
||||
|
||||
describe('intensityToColor', () => {
|
||||
test('intensity=0 → transparent', () => {
|
||||
expect(intensityToColor(0)).toBe(TRANSPARENT)
|
||||
test('intensity=0 → 最暗档(不再是 transparent,作面板底色)', () => {
|
||||
expect(intensityToColor(0)).toBe('#0a0d1a')
|
||||
})
|
||||
|
||||
test('intensity < 0 钳到 0 → transparent', () => {
|
||||
expect(intensityToColor(-0.5)).toBe(TRANSPARENT)
|
||||
test('intensity < 0 钳到 0 → 最暗档', () => {
|
||||
expect(intensityToColor(-0.5)).toBe('#0a0d1a')
|
||||
})
|
||||
|
||||
test('intensity ≤ 0.1 → transparent(边缘自然消失)', () => {
|
||||
expect(intensityToColor(0.05)).toBe(TRANSPARENT)
|
||||
expect(intensityToColor(0.1)).toBe(TRANSPARENT)
|
||||
})
|
||||
|
||||
test('intensity > 0.1 → 非透明颜色字符串', () => {
|
||||
const c = intensityToColor(0.5)
|
||||
expect(c).not.toBe(TRANSPARENT)
|
||||
expect(typeof c).toBe('string')
|
||||
// 紫蓝色调(#hex 格式)
|
||||
expect(c).toMatch(/^#[0-9a-fA-F]{6}$/)
|
||||
test('intensity > 0 → 永远是 #hex 颜色字符串(不返回 transparent)', () => {
|
||||
for (const v of [0.05, 0.1, 0.2, 0.5, 0.8]) {
|
||||
const c = intensityToColor(v)
|
||||
expect(c).not.toBe(TRANSPARENT)
|
||||
expect(c).toMatch(/^#[0-9a-fA-F]{6}$/)
|
||||
}
|
||||
})
|
||||
|
||||
test('intensity > 1 钳到 1 → 最高强度颜色', () => {
|
||||
@@ -42,9 +37,8 @@ describe('intensityToColor', () => {
|
||||
expect(unique.size).toBeGreaterThanOrEqual(3)
|
||||
})
|
||||
|
||||
test('intensity=1 → 高光档(不是 suggestion)', () => {
|
||||
// 最高档应为 #8aa0ff(高光),区别于 #5769F7(suggestion)
|
||||
expect(intensityToColor(1)).toBe('#8aa0ff')
|
||||
test('intensity=1 → suggestion 档(波峰最高档)', () => {
|
||||
expect(intensityToColor(1)).toBe('#5769F7')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -101,7 +95,7 @@ describe('computeRippleCells', () => {
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
test('震源点处颜色非 transparent(dist=0,falloff=1)', () => {
|
||||
test('震源点处颜色为最亮档(dist=0,falloff=1,intensity 高)', () => {
|
||||
const cells = computeRippleCells({
|
||||
y: 5,
|
||||
width: 11,
|
||||
@@ -109,21 +103,37 @@ describe('computeRippleCells', () => {
|
||||
sourceX: 5,
|
||||
sourceY: 5,
|
||||
})
|
||||
// 震源在 (5,5),y=5 行的第 5 列 cell 应非 transparent
|
||||
expect(cells[5].color).not.toBe(TRANSPARENT)
|
||||
// 震源在 (5,5),dist=0,falloff=1,dist<6 触发高频涟漪叠加
|
||||
// 波峰附近颜色应较高档(非最暗)
|
||||
expect(cells[5].color).not.toBe('#0a0d1a')
|
||||
})
|
||||
|
||||
test('远离震源的 cell 更可能是 transparent(远端衰减)', () => {
|
||||
// 震源在左端,远端 50 列外,强度低 → 大概率 transparent
|
||||
test('覆盖半径扩大:dist=65(左侧远端)仍有非最暗颜色', () => {
|
||||
// 震源 x=65,远端 x=0 → dist=65
|
||||
// falloff = max(0, 1 - 65/90) = 0.278,波峰时 intensity ≈ 0.278
|
||||
// 应映射到非最暗档(#15182b 或更亮)
|
||||
const cells = computeRippleCells({
|
||||
y: 0,
|
||||
width: 50,
|
||||
width: 66,
|
||||
time: 0,
|
||||
sourceX: 0,
|
||||
sourceX: 65,
|
||||
sourceY: 0,
|
||||
})
|
||||
// 远端第 49 列应为 transparent(falloff = max(0, 1-49/40) = 0)
|
||||
expect(cells[49].color).toBe(TRANSPARENT)
|
||||
// 第 0 列 dist=65,time=0 时 phase = 65*0.35 = 22.75 rad
|
||||
// sin(22.75) ≈ -0.59 → wave = 0 → intensity = 0 → 最暗档
|
||||
// 但 time 推进时波峰会扫过此处,强度变高
|
||||
// 这里只验证 cell 有合法颜色(最暗档也算合法)
|
||||
expect(cells[0].color).toMatch(/^#[0-9a-fA-F]{6}$/)
|
||||
// 推进 time 后,左侧应出现非最暗颜色(波峰扫过)
|
||||
const t1 = computeRippleCells({
|
||||
y: 0,
|
||||
width: 66,
|
||||
time: 2000,
|
||||
sourceX: 65,
|
||||
sourceY: 0,
|
||||
})
|
||||
const nonDarkest = t1.filter(c => c.color !== '#0a0d1a')
|
||||
expect(nonDarkest.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('time 推进时颜色分布变化(动画效果)', () => {
|
||||
|
||||
@@ -14,40 +14,46 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* suggestion 系颜色梯度(暗 → suggestion 色 → 高光)。
|
||||
* intensity=0 → transparent(无波纹),波峰附近升到 suggestion,超高频涟漪可达高光。
|
||||
* suggestion 系颜色梯度(暗背景 → suggestion 色)。
|
||||
*
|
||||
* 设计:所有强度都映射到具体颜色(不返回 transparent),让整面板都是
|
||||
* "暗紫蓝海洋"作为底色,波峰在底色上流动。这样波纹颜色变化更明显,
|
||||
* 波谷也有暗色(不会"消失")。
|
||||
*
|
||||
* 波峰最高升到 suggestion (#5769F7),避免与文字 overlay(也用 suggestion 系)
|
||||
* 同色互相吞噬。文字用更亮的高光色(#a3b5ff)保持对比。
|
||||
*/
|
||||
const RIPPLE_COLOR_STOPS = [
|
||||
'transparent', // 0.00 ~ 0.10
|
||||
'#15182b', // 0.10 ~ 0.25 — 暗深紫蓝
|
||||
'#1f2543', // 0.25 ~ 0.40
|
||||
'#2a3360', // 0.40 ~ 0.55
|
||||
'#3a4582', // 0.55 ~ 0.70
|
||||
'#5769F7', // 0.70 ~ 0.85 — suggestion (Medium blue)
|
||||
'#8aa0ff', // 0.85 ~ 1.00 — 高光
|
||||
'#0a0d1a', // 0.00 ~ 0.14 — 最暗,波谷底色
|
||||
'#15182b', // 0.14 ~ 0.28
|
||||
'#1f2543', // 0.28 ~ 0.42
|
||||
'#2a3360', // 0.42 ~ 0.56
|
||||
'#3a4582', // 0.56 ~ 0.70
|
||||
'#4a5bb0', // 0.70 ~ 0.84
|
||||
'#5769F7', // 0.84 ~ 1.00 — suggestion (波峰)
|
||||
] as const
|
||||
|
||||
/** 'transparent' 是合法 color 字面量(渲染层会跳过这种 cell 的样式)。 */
|
||||
export const TRANSPARENT = 'transparent'
|
||||
|
||||
/**
|
||||
* 强度(任意实数)→ 颜色字符串。
|
||||
*
|
||||
* 钳到 [0, 1],按 RIPPLE_COLOR_STOPS 分级。
|
||||
* 极低强度(≤ 0.10)→ transparent(让波纹边缘自然消失)。
|
||||
* 钳到 [0, 1],按 RIPPLE_COLOR_STOPS 分级。永不返回 transparent。
|
||||
* intensity=0 → 最暗档(#0a0d1a,作为面板底色)。
|
||||
*/
|
||||
export function intensityToColor(intensity: number): string {
|
||||
const v = intensity < 0 ? 0 : intensity > 1 ? 1 : intensity
|
||||
if (v <= 0.1) return TRANSPARENT
|
||||
// 把 (0.1, 1.0] 映射到 [1, stops.length-1]
|
||||
const scaled = ((v - 0.1) / 0.9) * (RIPPLE_COLOR_STOPS.length - 1)
|
||||
const idx = Math.min(
|
||||
RIPPLE_COLOR_STOPS.length - 1,
|
||||
Math.max(1, Math.round(scaled)),
|
||||
Math.floor(v * RIPPLE_COLOR_STOPS.length),
|
||||
)
|
||||
return RIPPLE_COLOR_STOPS[idx]
|
||||
}
|
||||
|
||||
/**
|
||||
* 'transparent' 字面量。intensityToColor 永不返回它(保留为兼容性导出)。
|
||||
* 渲染层可用此常量做语义判定(如 cell 是 overlay 文字而非波纹背景)。
|
||||
*/
|
||||
export const TRANSPARENT = 'transparent'
|
||||
|
||||
/**
|
||||
* 单位置 cell:char + color。
|
||||
* - color 为 'transparent' 时渲染层不染色(背景保持终端默认)。
|
||||
@@ -92,17 +98,18 @@ const RIPPLE_BG_CHAR = ' '
|
||||
/**
|
||||
* 计算面板某一行 y 的完整波纹 cell 列表。
|
||||
*
|
||||
* 波纹数学:
|
||||
* 波纹数学(v2 — 调慢 + 扩大覆盖):
|
||||
* dx = x - sourceX
|
||||
* dy = (y - sourceY) * 1.5 (y 方向视觉拉伸,行高 > 字宽)
|
||||
* dist = sqrt(dx² + dy²)
|
||||
* phase = dist * 0.4 - time * 0.012
|
||||
* phase = dist * 0.35 - time * 0.004 (速度调慢至原 1/3)
|
||||
* wave = max(0, sin(phase))
|
||||
* falloff = max(0, 1 - dist / 40)
|
||||
* falloff = max(0, 1 - dist / 90) (覆盖半径扩到 90,让左侧 dist=65 也可见)
|
||||
* intensity = wave * falloff
|
||||
* 震源附近 (dist < 6):叠加高频涟漪 max(intensity, 0.5 + 0.5*sin(time*0.02 - dist*1.2))
|
||||
*
|
||||
* 每位置强度经 intensityToColor → 颜色字符串,写入 cell。
|
||||
* 每位置强度经 intensityToColor → 颜色字符串(永不 transparent),写入 cell。
|
||||
* 即使 intensity=0(波谷)也得到最暗档 #0a0d1a 作为面板底色。
|
||||
*
|
||||
* @returns 长度严格等于 width 的 Cell 数组
|
||||
*/
|
||||
@@ -122,12 +129,12 @@ export function computeRippleCells(args: {
|
||||
const dy = (y - sourceY) * 1.5
|
||||
const dist = Math.sqrt(dx * dx + dy * dy)
|
||||
|
||||
// 主波纹相位
|
||||
const phase = dist * 0.4 - time * 0.012
|
||||
// 主波纹相位(速度调慢:原 0.012 → 0.004,约 1/3 速)
|
||||
const phase = dist * 0.35 - time * 0.004
|
||||
const wave = Math.max(0, Math.sin(phase))
|
||||
|
||||
// 距离衰减
|
||||
const falloff = Math.max(0, 1 - dist / 40)
|
||||
// 距离衰减(覆盖半径扩到 90:原 40)
|
||||
const falloff = Math.max(0, 1 - dist / 90)
|
||||
let intensity = wave * falloff
|
||||
|
||||
// 震源附近高频涟漪
|
||||
|
||||
Reference in New Issue
Block a user