From 59d9244f9e1a1357d16c56d9a67064212bf4a0b0 Mon Sep 17 00:00:00 2001 From: InfinityPacer Date: Sun, 6 Sep 2026 01:32:14 +0800 Subject: [PATCH] fix(glass): preserve horizontal lens while easing text stretch --- .../__tests__/glassNavbarRefraction.spec.ts | 25 +++++++++++++++++-- src/utils/glassNavbarRefraction.ts | 7 +++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/utils/__tests__/glassNavbarRefraction.spec.ts b/src/utils/__tests__/glassNavbarRefraction.spec.ts index 1dbe9d9e..291d973c 100644 --- a/src/utils/__tests__/glassNavbarRefraction.spec.ts +++ b/src/utils/__tests__/glassNavbarRefraction.spec.ts @@ -15,10 +15,10 @@ describe('createGlassNavbarDisplacementField', () => { expect(field.height).toBe(41) expect(pixelAt(field, 50, 0)).toEqual([128, 128, 128, 255]) expect(pixelAt(field, 50, 20)).toEqual([128, 128, 128, 255]) - expect(pixelAt(field, 50, 5)[2]).toBeLessThan(120) + expect(pixelAt(field, 50, 5)[2]).toBeLessThan(128) expect(pixelAt(field, 5, 20)[0]).toBeLessThan(120) expect(pixelAt(field, 95, 20)[0]).toBeGreaterThan(136) - expect(pixelAt(field, 50, 35)[2]).toBeGreaterThan(136) + expect(pixelAt(field, 50, 35)[2]).toBeGreaterThan(128) }) it.each([ @@ -68,6 +68,27 @@ describe('createGlassNavbarDisplacementField', () => { expect(field.height).toBe(1) expect([...field.pixels]).toEqual([128, 128, 128, 255]) }) + + it('limits vertical text stretching without flattening the horizontal lens', () => { + const field = createGlassNavbarDisplacementField({ width: 1423, height: 64, radius: 16 }) + const displacement = (x: number, y: number, channel: number) => -34 * (pixelAt(field, x, y)[channel] / 255 - 0.5) + let maximumHorizontal = 0 + let maximumVertical = 0 + let minimumVerticalStep = Infinity + let maximumVerticalStep = 0 + for (let y = 1; y < field.height; y += 1) { + const previous = displacement(711, y - 1, 2) + const current = displacement(711, y, 2) + maximumVertical = Math.max(maximumVertical, Math.abs(current)) + minimumVerticalStep = Math.min(minimumVerticalStep, 1 + current - previous) + maximumVerticalStep = Math.max(maximumVerticalStep, 1 + current - previous) + } + for (let x = 0; x < 32; x += 1) maximumHorizontal = Math.max(maximumHorizontal, Math.abs(displacement(x, 32, 0))) + expect(maximumHorizontal).toBeGreaterThan(7) + expect(maximumVertical).toBeLessThan(2.6) + expect(minimumVerticalStep).toBeGreaterThan(0.59) + expect(maximumVerticalStep).toBeLessThan(1.41) + }) }) describe('supportsGlassNavbarLiveRefraction', () => { diff --git a/src/utils/glassNavbarRefraction.ts b/src/utils/glassNavbarRefraction.ts index f5312a20..11469bb2 100644 --- a/src/utils/glassNavbarRefraction.ts +++ b/src/utils/glassNavbarRefraction.ts @@ -37,6 +37,8 @@ const REFRACTION_BAND_PX = 24 // 峰值靠近外沿,内侧有足够距离释放放大率;对称波峰会在窄轮廓内反向采样。 const MAX_DISPLACEMENT_BAND_RATIO = 0.42 const PEAK_DEPTH_RATIO = 0.16 +// 文本沿纵向滚动穿过长边时,限制字形高度的局部伸缩;短边保留更强的水平透镜。 +const VERTICAL_DISPLACEMENT_BAND_RATIO = 0.1 function normalizePixelSize(value: number) { return Number.isFinite(value) ? Math.max(1, Math.round(value)) : 1 @@ -107,6 +109,9 @@ export function createGlassNavbarDisplacementField({ if (signedDistance > 0 || distanceInside <= outerGuard || distanceInside >= bandWidth) continue const profile = refractionProfile(distanceInside, bandWidth, outerGuard) + const verticalProgress = (distanceInside - outerGuard) / (bandWidth - outerGuard) + const verticalProfile = Math.sin(Math.PI * verticalProgress) ** 2 + const verticalAmplitude = (bandWidth * VERTICAL_DISPLACEMENT_BAND_RATIO * 255) / HIGH_REFRACTION_SCALE_PX const gradientX = roundedRectangleSignedDistance(sampleX + 0.5, sampleY, pixelWidth, pixelHeight, pixelRadius) - roundedRectangleSignedDistance(sampleX - 0.5, sampleY, pixelWidth, pixelHeight, pixelRadius) @@ -120,7 +125,7 @@ export function createGlassNavbarDisplacementField({ DISPLACEMENT_NEUTRAL_CHANNEL + (channelAmplitude * gradientX * profile) / gradientLength, ) pixels[offset + 2] = clampChannel( - DISPLACEMENT_NEUTRAL_CHANNEL + (channelAmplitude * gradientY * profile) / gradientLength, + DISPLACEMENT_NEUTRAL_CHANNEL + (verticalAmplitude * gradientY * verticalProfile) / gradientLength, ) } }