mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-10 18:26:45 +08:00
fix(glass): preserve horizontal lens while easing text stretch
This commit is contained in:
@@ -15,10 +15,10 @@ describe('createGlassNavbarDisplacementField', () => {
|
|||||||
expect(field.height).toBe(41)
|
expect(field.height).toBe(41)
|
||||||
expect(pixelAt(field, 50, 0)).toEqual([128, 128, 128, 255])
|
expect(pixelAt(field, 50, 0)).toEqual([128, 128, 128, 255])
|
||||||
expect(pixelAt(field, 50, 20)).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, 5, 20)[0]).toBeLessThan(120)
|
||||||
expect(pixelAt(field, 95, 20)[0]).toBeGreaterThan(136)
|
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([
|
it.each([
|
||||||
@@ -68,6 +68,27 @@ describe('createGlassNavbarDisplacementField', () => {
|
|||||||
expect(field.height).toBe(1)
|
expect(field.height).toBe(1)
|
||||||
expect([...field.pixels]).toEqual([128, 128, 128, 255])
|
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', () => {
|
describe('supportsGlassNavbarLiveRefraction', () => {
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ const REFRACTION_BAND_PX = 24
|
|||||||
// 峰值靠近外沿,内侧有足够距离释放放大率;对称波峰会在窄轮廓内反向采样。
|
// 峰值靠近外沿,内侧有足够距离释放放大率;对称波峰会在窄轮廓内反向采样。
|
||||||
const MAX_DISPLACEMENT_BAND_RATIO = 0.42
|
const MAX_DISPLACEMENT_BAND_RATIO = 0.42
|
||||||
const PEAK_DEPTH_RATIO = 0.16
|
const PEAK_DEPTH_RATIO = 0.16
|
||||||
|
// 文本沿纵向滚动穿过长边时,限制字形高度的局部伸缩;短边保留更强的水平透镜。
|
||||||
|
const VERTICAL_DISPLACEMENT_BAND_RATIO = 0.1
|
||||||
|
|
||||||
function normalizePixelSize(value: number) {
|
function normalizePixelSize(value: number) {
|
||||||
return Number.isFinite(value) ? Math.max(1, Math.round(value)) : 1
|
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
|
if (signedDistance > 0 || distanceInside <= outerGuard || distanceInside >= bandWidth) continue
|
||||||
|
|
||||||
const profile = refractionProfile(distanceInside, bandWidth, outerGuard)
|
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 =
|
const gradientX =
|
||||||
roundedRectangleSignedDistance(sampleX + 0.5, sampleY, pixelWidth, pixelHeight, pixelRadius) -
|
roundedRectangleSignedDistance(sampleX + 0.5, sampleY, pixelWidth, pixelHeight, pixelRadius) -
|
||||||
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,
|
DISPLACEMENT_NEUTRAL_CHANNEL + (channelAmplitude * gradientX * profile) / gradientLength,
|
||||||
)
|
)
|
||||||
pixels[offset + 2] = clampChannel(
|
pixels[offset + 2] = clampChannel(
|
||||||
DISPLACEMENT_NEUTRAL_CHANNEL + (channelAmplitude * gradientY * profile) / gradientLength,
|
DISPLACEMENT_NEUTRAL_CHANNEL + (verticalAmplitude * gradientY * verticalProfile) / gradientLength,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user