mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-08-15 10:34:35 +08:00
Co-authored-by: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com>
This commit is contained in:
@@ -210,6 +210,27 @@ describe('useThemeCustomizer glass settings', () => {
|
||||
expect(Number(document.body.style.getPropertyValue('--glass-background-visibility'))).toBeCloseTo(0.48)
|
||||
expect(Number(document.documentElement.style.getPropertyValue('--glass-surface-density'))).toBeCloseTo(0.72)
|
||||
expect(Number(document.body.style.getPropertyValue('--glass-tint-density'))).toBeCloseTo(0.65)
|
||||
expect(document.documentElement.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('6.7px')
|
||||
expect(document.body.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('6.7px')
|
||||
})
|
||||
|
||||
it('keeps overlay clarity synchronized across preview, cancel, and commit', () => {
|
||||
persistPartialThemeCustomizerSettings({ glassTransparencyStrength: 50 })
|
||||
expect(document.documentElement.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('6.7px')
|
||||
|
||||
previewGlassSettings({ glassTransparencyStrength: 100 })
|
||||
expect(document.documentElement.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('5px')
|
||||
expect(document.body.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('5px')
|
||||
|
||||
cancelGlassPreview()
|
||||
expect(document.documentElement.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('6.7px')
|
||||
expect(document.body.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('6.7px')
|
||||
|
||||
previewGlassSettings({ glassTransparencyStrength: 20 })
|
||||
commitGlassPreview()
|
||||
expect(readThemeCustomizerSettings().glassTransparencyStrength).toBe(20)
|
||||
expect(document.documentElement.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('7.8px')
|
||||
expect(document.body.style.getPropertyValue('--glass-overlay-clarity-blur')).toBe('7.8px')
|
||||
})
|
||||
|
||||
it('previews glass settings without persisting them', () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
GLASS_OPTICAL_STRENGTH_MAX,
|
||||
getGlassCssFrostBlur,
|
||||
getGlassMaterialResponse,
|
||||
getGlassOverlayClarityBlur,
|
||||
getGlassOpticalCssTransmissionBrightness,
|
||||
getGlassOpticalPresetKey,
|
||||
getGlassOpticalPresetParameters,
|
||||
@@ -480,6 +481,7 @@ export function applyThemeCustomizerRootSettings(
|
||||
|
||||
const materialResponse = getGlassMaterialResponse(settings.glassAppearance, settings.glassTransparencyStrength)
|
||||
const frostBlur = getGlassCssFrostBlur(settings.glassTransparencyStrength)
|
||||
const overlayClarityBlur = getGlassOverlayClarityBlur(settings.glassTransparencyStrength)
|
||||
const applyGlassResponse = (element: HTMLElement) => {
|
||||
element.style.setProperty('--glass-background-visibility', String(materialResponse.backgroundVisibility))
|
||||
element.style.setProperty('--glass-frost-blur-scale', String(materialResponse.frostBlurScale))
|
||||
@@ -488,6 +490,7 @@ export function applyThemeCustomizerRootSettings(
|
||||
element.style.setProperty('--glass-tint-density', String(materialResponse.tintDensity))
|
||||
element.style.setProperty('--glass-blur-surface', `${frostBlur.surface}px`)
|
||||
element.style.setProperty('--glass-blur-raised', `${frostBlur.raised}px`)
|
||||
element.style.setProperty('--glass-overlay-clarity-blur', `${overlayClarityBlur}px`)
|
||||
}
|
||||
|
||||
document.documentElement.setAttribute('data-glass-appearance', settings.glassAppearance)
|
||||
|
||||
@@ -8,7 +8,7 @@ describe('glass overlay material styles', () => {
|
||||
const styles = readFileSync(resolve(cwd(), 'src/styles/themes/glass.scss'), 'utf8')
|
||||
|
||||
expect(styles).toContain('calc(0.1 + var(--glass-surface-density, 0.62) * 0.22)')
|
||||
expect(styles.match(/--glass-overlay-blur:\s*8px/g)).toHaveLength(2)
|
||||
expect(styles.match(/--glass-overlay-blur:\s*var\(--glass-overlay-clarity-blur, 6px\)/g)).toHaveLength(2)
|
||||
expect(styles).toContain('--glass-overlay-saturate: 115%')
|
||||
expect(styles).toContain('--glass-overlay-saturate: 120%')
|
||||
expect(styles).toContain('--glass-overlay-blur: min(var(--glass-blur-raised), 36px)')
|
||||
@@ -21,6 +21,20 @@ describe('glass overlay material styles', () => {
|
||||
expect(styles).not.toContain('background: rgba(3, 7, 18, 62%)')
|
||||
})
|
||||
|
||||
it('protects ordinary clear and tinted content without changing raised or frosted materials', () => {
|
||||
const styles = readFileSync(resolve(cwd(), 'src/styles/themes/glass.scss'), 'utf8')
|
||||
|
||||
expect(styles).toContain('rgba(11, 19, 34, calc(0.12 + var(--glass-surface-density, 0.62) * 0.2))')
|
||||
expect(styles).toContain('rgba(11, 19, 34, calc(0.13 + var(--glass-surface-density, 0.62) * 0.23))')
|
||||
expect(styles).toContain('rgba(11, 19, 34, calc(0.12 + var(--glass-surface-density, 0.72) * 0.2)) 88%')
|
||||
expect(styles).toContain('rgba(11, 19, 34, calc(0.13 + var(--glass-surface-density, 0.72) * 0.23)) 89%')
|
||||
expect(styles).toContain('rgba(11, 19, 34, calc(0.07 + var(--glass-surface-density, 0.62) * 0.36))')
|
||||
expect(styles).toContain('rgba(11, 19, 34, calc(0.07 + var(--glass-surface-density, 0.72) * 0.36)) 84%')
|
||||
expect(styles).toContain('rgba(255, 255, 255, calc(0.035 + var(--glass-surface-density, 0.86) * 0.075))')
|
||||
expect(styles).toContain('rgba(255, 255, 255, calc(0.03 + var(--glass-surface-density, 0.86) * 0.07))')
|
||||
expect(styles).toContain('rgba(255, 255, 255, calc(0.045 + var(--glass-surface-density, 0.86) * 0.09))')
|
||||
})
|
||||
|
||||
it('renders colored chips as shadowless glass without flattening their variants', () => {
|
||||
const styles = readFileSync(resolve(cwd(), 'src/styles/themes/glass.scss'), 'utf8')
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
// 材质只覆盖统一表面 token;质量档只替换光学层,不改变业务组件契约。
|
||||
html[data-theme='glass'] {
|
||||
--glass-surface: rgba(11, 19, 34, calc(0.02 + var(--glass-surface-density, 0.62) * 0.3));
|
||||
--glass-surface-soft: rgba(11, 19, 34, calc(0.04 + var(--glass-surface-density, 0.62) * 0.32));
|
||||
--glass-surface: rgba(11, 19, 34, calc(0.12 + var(--glass-surface-density, 0.62) * 0.2));
|
||||
--glass-surface-soft: rgba(11, 19, 34, calc(0.13 + var(--glass-surface-density, 0.62) * 0.23));
|
||||
--glass-surface-raised: rgba(11, 19, 34, calc(0.07 + var(--glass-surface-density, 0.62) * 0.36));
|
||||
--glass-control: rgba(11, 19, 34, 52%);
|
||||
--glass-control-prominent: rgba(255, 255, 255, 7%);
|
||||
@@ -54,7 +54,7 @@ html[data-theme='glass'] {
|
||||
--glass-navbar-backdrop-filter: var(--glass-raised-backdrop-filter);
|
||||
--glass-navbar-scrolled-backdrop-filter: blur(3px) saturate(115%);
|
||||
--glass-overlay-surface: rgba(11, 19, 34, calc(0.1 + var(--glass-surface-density, 0.62) * 0.22));
|
||||
--glass-overlay-blur: 8px;
|
||||
--glass-overlay-blur: var(--glass-overlay-clarity-blur, 6px);
|
||||
--glass-overlay-saturate: 115%;
|
||||
--glass-overlay-scrim: rgba(3, 7, 18, 30%);
|
||||
--glass-overlay-backdrop-filter: blur(var(--glass-overlay-blur)) saturate(var(--glass-overlay-saturate));
|
||||
@@ -130,12 +130,12 @@ html[data-theme='glass'] {
|
||||
&[data-glass-appearance='tinted'] {
|
||||
--glass-surface: color-mix(
|
||||
in srgb,
|
||||
rgba(11, 19, 34, calc(0.02 + var(--glass-surface-density, 0.72) * 0.3)) 88%,
|
||||
rgba(11, 19, 34, calc(0.12 + var(--glass-surface-density, 0.72) * 0.2)) 88%,
|
||||
rgba(var(--v-theme-primary), calc(var(--glass-tint-density, 0.65) * 0.28))
|
||||
);
|
||||
--glass-surface-soft: color-mix(
|
||||
in srgb,
|
||||
rgba(11, 19, 34, calc(0.04 + var(--glass-surface-density, 0.72) * 0.32)) 89%,
|
||||
rgba(11, 19, 34, calc(0.13 + var(--glass-surface-density, 0.72) * 0.23)) 89%,
|
||||
rgba(var(--v-theme-primary), calc(var(--glass-tint-density, 0.65) * 0.26))
|
||||
);
|
||||
--glass-surface-raised: color-mix(
|
||||
@@ -187,7 +187,7 @@ html[data-theme='glass'] {
|
||||
rgba(11, 19, 34, calc(0.09 + var(--glass-surface-density, 0.72) * 0.2)) 88%,
|
||||
rgba(var(--v-theme-primary), calc(var(--glass-tint-density, 0.65) * 0.22))
|
||||
);
|
||||
--glass-overlay-blur: 8px;
|
||||
--glass-overlay-blur: var(--glass-overlay-clarity-blur, 6px);
|
||||
--glass-overlay-saturate: 120%;
|
||||
--glass-overlay-scrim: rgba(3, 7, 18, 32%);
|
||||
--glass-chip-backdrop-filter: blur(10px) saturate(165%) brightness(var(--glass-transmission-brightness));
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
getGlassCssFrostBlur,
|
||||
getGlassCoverScale,
|
||||
getGlassMaterialResponse,
|
||||
getGlassOverlayClarityBlur,
|
||||
getGlassOpticalCssTransmissionBrightness,
|
||||
getGlassOpticalDecay,
|
||||
getGlassOpticalBufferSize,
|
||||
@@ -269,6 +270,34 @@ describe('glass optics geometry', () => {
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('derives a continuous overlay clarity floor from transparency anchors', () => {
|
||||
const anchors = [
|
||||
[0, 8.9],
|
||||
[20, 7.8],
|
||||
[50, 6.7],
|
||||
[70, 5.8],
|
||||
[85, 5.4],
|
||||
[100, 5],
|
||||
] as const
|
||||
|
||||
for (const [transparency, blur] of anchors) {
|
||||
expect(getGlassOverlayClarityBlur(transparency)).toBe(blur)
|
||||
}
|
||||
|
||||
expect(getGlassOverlayClarityBlur(-1)).toBe(8.9)
|
||||
expect(getGlassOverlayClarityBlur(101)).toBe(5)
|
||||
expect(getGlassOverlayClarityBlur(Number.NaN)).toBe(6.7)
|
||||
|
||||
const samples = Array.from({ length: 101 }, (_, value) => getGlassOverlayClarityBlur(value))
|
||||
expect(samples.every((sample, index) => index === 0 || sample <= samples[index - 1])).toBe(true)
|
||||
|
||||
for (const anchor of [20, 50, 70, 85]) {
|
||||
const blur = getGlassOverlayClarityBlur(anchor)
|
||||
expect(Math.abs(getGlassOverlayClarityBlur(anchor - 1) - blur)).toBeLessThan(0.02)
|
||||
expect(Math.abs(getGlassOverlayClarityBlur(anchor + 1) - blur)).toBeLessThan(0.03)
|
||||
}
|
||||
})
|
||||
|
||||
it('returns preset copies so previews cannot mutate the shared matrix', () => {
|
||||
const first = getGlassOpticalPresetParameters('tinted', 'high', 'glide')
|
||||
first.translation = 0
|
||||
|
||||
@@ -250,6 +250,7 @@ const GLASS_SURFACE_DENSITY: Record<GlassAppearance, readonly number[]> = {
|
||||
const GLASS_TINT_DENSITY = [1, 0.9, 0.65, 0.48, 0.36, 0.28] as const
|
||||
const GLASS_FROST_DENSITY = [1, 0.9, 0.7, 0.34, 0.12, 0.04] as const
|
||||
const GLASS_FROSTED_DENSITY = [1, 0.82, 0.55, 0.28, 0.1, 0.025] as const
|
||||
const GLASS_OVERLAY_CLARITY_BLUR = [8.9, 7.8, 6.7, 5.8, 5.4, 5] as const
|
||||
|
||||
/** 在相邻业务锚点之间使用零斜率边界插值,避免滑杆经过锚点时出现视觉折线。 */
|
||||
function interpolateGlassResponse(value: unknown, anchors: readonly number[]) {
|
||||
@@ -292,6 +293,11 @@ export function getGlassCssFrostBlur(value: unknown) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 透明与色调浮层保留独立模糊下限,避免高通透度使临时内容直接暴露在复杂壁纸上。 */
|
||||
export function getGlassOverlayClarityBlur(value: unknown) {
|
||||
return interpolateGlassResponse(value, GLASS_OVERLAY_CLARITY_BLUR)
|
||||
}
|
||||
|
||||
/** 计算与 CSS `ease` 相同的交叉淡化进度,使 DOM 壁纸与 shader 双纹理保持同一时钟。 */
|
||||
export function getGlassWallpaperTransitionProgress(elapsed: number, duration: number) {
|
||||
if (duration <= 0 || elapsed >= duration) return 1
|
||||
|
||||
Reference in New Issue
Block a user