diff --git a/src/App.vue b/src/App.vue
index 1bb65d34..0d4e4a17 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1141,35 +1141,35 @@ onUnmounted(() => {
-
+
diff --git a/src/composables/__tests__/useGlassFixedShellBackplate.spec.ts b/src/composables/__tests__/useGlassFixedShellBackplate.spec.ts
index a4e9bdf6..a333e148 100644
--- a/src/composables/__tests__/useGlassFixedShellBackplate.spec.ts
+++ b/src/composables/__tests__/useGlassFixedShellBackplate.spec.ts
@@ -14,13 +14,13 @@ describe('shouldUseGlassFixedShellBackplate', () => {
themeName: 'glass',
}
- it('enables the direct wallpaper backplate only for affected authenticated Chromium rendering', () => {
+ it('enables the direct wallpaper backplate for every frosted Chromium quality', () => {
expect(shouldUseGlassFixedShellBackplate(eligible)).toBe(true)
+ expect(shouldUseGlassFixedShellBackplate({ ...eligible, quality: 'balanced' })).toBe(true)
+ expect(shouldUseGlassFixedShellBackplate({ ...eligible, quality: 'high' })).toBe(true)
expect(shouldUseGlassFixedShellBackplate({ ...eligible, needsStableFixedBackdrop: false })).toBe(false)
expect(shouldUseGlassFixedShellBackplate({ ...eligible, appearance: 'clear' })).toBe(false)
expect(shouldUseGlassFixedShellBackplate({ ...eligible, appearance: 'tinted' })).toBe(false)
- expect(shouldUseGlassFixedShellBackplate({ ...eligible, quality: 'balanced' })).toBe(false)
- expect(shouldUseGlassFixedShellBackplate({ ...eligible, quality: 'high' })).toBe(false)
expect(shouldUseGlassFixedShellBackplate({ ...eligible, themeName: 'transparent' })).toBe(false)
expect(shouldUseGlassFixedShellBackplate({ ...eligible, isAuthenticated: false })).toBe(false)
expect(shouldUseGlassFixedShellBackplate({ ...eligible, hasWallpaper: false })).toBe(false)
diff --git a/src/composables/__tests__/useGlassOpticalRenderer.spec.ts b/src/composables/__tests__/useGlassOpticalRenderer.spec.ts
index bf2bc0ab..2b07de48 100644
--- a/src/composables/__tests__/useGlassOpticalRenderer.spec.ts
+++ b/src/composables/__tests__/useGlassOpticalRenderer.spec.ts
@@ -456,6 +456,47 @@ describe('glass optical surface discovery', () => {
])
})
+ it('keeps frosted fixed surfaces on dynamics-only GPU composition', async () => {
+ const three = await import('three')
+ const render = vi.spyOn(three.WebGLRenderer.prototype, 'render')
+ const canvas = document.createElement('canvas')
+ const appearance = ref<'clear' | 'frosted' | 'tinted'>('clear')
+ appendOpticalSurface('layout-vertical-nav', { height: 800, width: 260, x: 0, y: 0 })
+ const scope = effectScope()
+ const renderer = scope.run(() =>
+ useGlassOpticalRenderer({
+ active: ref(true),
+ appearance,
+ canvas: ref(canvas),
+ quality: ref('balanced'),
+ routeKey: ref('/dashboard'),
+ surfaceSpace: 'fixed',
+ tintColor: ref('#8D51F9'),
+ wallpaperUrl: ref('https://example.com/wallpaper.jpg'),
+ }),
+ )
+
+ await vi.waitFor(() => expect(renderer?.state.value).toBe('ready'))
+ const scene = render.mock.calls.at(-1)?.[0] as unknown as {
+ children: Array<{
+ material: {
+ uniforms: {
+ uDynamicsOnly: { value: number }
+ }
+ }
+ }>
+ }
+ const dynamicsOnly = scene.children[0].material.uniforms.uDynamicsOnly
+ expect(dynamicsOnly.value).toBe(0)
+
+ appearance.value = 'frosted'
+ await vi.waitFor(() => expect(dynamicsOnly.value).toBe(1))
+
+ appearance.value = 'tinted'
+ await vi.waitFor(() => expect(dynamicsOnly.value).toBe(0))
+ scope.stop()
+ })
+
it('re-samples scroll surfaces and material weight on a shared page motion revision', async () => {
vi.spyOn(window, 'innerWidth', 'get').mockReturnValue(1200)
vi.spyOn(window, 'innerHeight', 'get').mockReturnValue(800)
diff --git a/src/composables/useGlassFixedShellBackplate.ts b/src/composables/useGlassFixedShellBackplate.ts
index a2269c2c..39667798 100644
--- a/src/composables/useGlassFixedShellBackplate.ts
+++ b/src/composables/useGlassFixedShellBackplate.ts
@@ -56,14 +56,13 @@ export function isChromiumFixedShellBackplateBrowser(browserIdentity: GlassFixed
return /\b(?:Chrome|Chromium)\/\d+/u.test(browserIdentity.userAgent)
}
-/** 只有需要稳定 fixed 背板的 Chromium 磨砂标准布局使用直接壁纸采样。 */
+/** Chromium 磨砂 fixed 层使用稳定壁纸背板,避免读取随页面重绘的 document backdrop。 */
export function shouldUseGlassFixedShellBackplate(options: GlassFixedShellBackplateEligibility) {
return (
options.isAuthenticated &&
options.needsStableFixedBackdrop &&
options.themeName === 'glass' &&
options.appearance === 'frosted' &&
- options.quality === 'css' &&
options.hasWallpaper
)
}
diff --git a/src/composables/useGlassOpticalRenderer.ts b/src/composables/useGlassOpticalRenderer.ts
index 5771c1ad..6f8db45f 100644
--- a/src/composables/useGlassOpticalRenderer.ts
+++ b/src/composables/useGlassOpticalRenderer.ts
@@ -1399,6 +1399,8 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
let resumePromise: Promise | null = null
let resumeVersion = 0
const presentationSpace = options.surfaceSpace ?? 'fixed'
+ const usesDynamicsOnly = () =>
+ presentationSpace === 'scroll' || (presentationSpace === 'fixed' && toValue(options.appearance) === 'frosted')
const wallpaperSourceCache = options.wallpaperSourceCache ?? createGlassWallpaperSourceCache()
/** 滚动期间由原生 backdrop 接管壁纸;稳定态恢复完整纹理折射与流体反馈。 */
@@ -3664,7 +3666,7 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
uBackgroundVisibility: { value: materialResponse.backgroundVisibility },
uCoverScale: { value: new three.Vector2(1, 1) },
uDeformationStrength: { value: getDeformationStrengthScale() },
- uDynamicsOnly: { value: presentationSpace === 'scroll' ? 1 : 0 },
+ uDynamicsOnly: { value: usesDynamicsOnly() ? 1 : 0 },
uFlowTexture: { value: null },
uFlowStrength: { value: getFlowStrengthScale() },
uHasFlowTexture: { value: 0 },
@@ -3818,6 +3820,7 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
)
resources.uniforms.uAppearance.value = getGlassAppearanceUniformValue(appearance)
resources.uniforms.uBackgroundVisibility.value = materialResponse.backgroundVisibility
+ resources.uniforms.uDynamicsOnly.value = usesDynamicsOnly() ? 1 : 0
resources.uniforms.uFrostDetailLevel.value = materialResponse.frostDetailLevel
resources.uniforms.uSurfaceDensity.value = materialResponse.surfaceDensity
resources.uniforms.uTintDensity.value = materialResponse.tintDensity
diff --git a/src/styles/__tests__/glassOverlayMaterial.spec.ts b/src/styles/__tests__/glassOverlayMaterial.spec.ts
index e4e83e9f..276084d8 100644
--- a/src/styles/__tests__/glassOverlayMaterial.spec.ts
+++ b/src/styles/__tests__/glassOverlayMaterial.spec.ts
@@ -33,7 +33,7 @@ describe('glass overlay material styles', () => {
)
})
- it('keeps frosted CSS fixed shells on the stable wallpaper backplate', () => {
+ it('keeps Chromium frosted fixed shells on the stable wallpaper backplate', () => {
const styles = readFileSync(resolve(cwd(), 'src/styles/themes/glass.scss'), 'utf8')
const backplate = readFileSync(resolve(cwd(), 'src/components/theme/GlassFixedShellBackplate.vue'), 'utf8')
@@ -41,6 +41,12 @@ describe('glass overlay material styles', () => {
expect(styles).toMatch(
/&\[data-glass-appearance='frosted'\]\[data-glass-quality='css'\][\s\S]*?\.layout-wrapper\.layout-fixed-shell-backplate-active \.layout-vertical-nav::before,[\s\S]*?\.layout-wrapper\.layout-fixed-shell-backplate-active \.layout-navbar,[\s\S]*?backdrop-filter:\s*none\s*!important;/,
)
+ expect(styles).toMatch(
+ /\[data-glass-appearance='frosted'\]\[data-glass-quality='balanced'\]\s*\{[\s\S]*?--glass-fixed-shell-backplate-filter:\s*var\(--glass-native-surface-backdrop-filter\);/,
+ )
+ expect(styles).toMatch(
+ /\[data-glass-appearance='frosted'\]\[data-glass-quality='high'\]\s*\{[\s\S]*?--glass-fixed-shell-backplate-filter:\s*var\(--glass-native-surface-backdrop-filter\);/,
+ )
expect(backplate).toContain('filter: var(--glass-fixed-shell-backplate-filter)')
expect(backplate).not.toContain('backdrop-filter')
expect(backplate).toContain('var(--glass-fixed-shell-nav-inline-size) 100%')
@@ -121,6 +127,9 @@ describe('glass overlay material styles', () => {
expect(styles).toMatch(
/\[data-glass-renderer-state='ready'\][\s\S]*?\.app-hover-lift-card:not\(\.media-card--image-loaded\)[\s\S]*?backdrop-filter:\s*var\(--glass-native-surface-backdrop-filter\)\s*!important;/,
)
+ expect(styles).toMatch(
+ /\.layout-wrapper:not\(\.layout-fixed-shell-backplate-active\) \.layout-vertical-nav::before,[\s\S]*?backdrop-filter:\s*var\(--glass-native-surface-backdrop-filter\)\s*!important;/,
+ )
expect(styles).toMatch(
/\.settings-section-card\.app-grouped-list\s*\{[\s\S]*?backdrop-filter:\s*var\(--glass-native-surface-backdrop-filter\)\s*!important;/,
)
diff --git a/src/styles/themes/glass.scss b/src/styles/themes/glass.scss
index c2efc7dc..f7c5dc8d 100644
--- a/src/styles/themes/glass.scss
+++ b/src/styles/themes/glass.scss
@@ -1,6 +1,8 @@
/* stylelint-disable no-descending-specificity */
/* stylelint-disable scss/at-rule-no-unknown */
+@use '@configured-variables' as variables;
+
// 材质只覆盖统一表面 token;质量档只替换光学层,不改变业务组件契约。
html[data-theme='glass'] {
--glass-surface: rgba(11, 19, 34, calc(0.02 + var(--glass-surface-density, 0.62) * 0.3));
@@ -462,7 +464,7 @@ html[data-theme='glass'] {
}
}
- // Chromium 磨砂标准直接渲染稳定壁纸背板,固定功能层不得读取随页面重绘的 document backdrop。
+ // Chromium 磨砂 fixed 层直接渲染稳定壁纸背板,固定功能层不得读取随页面重绘的 document backdrop。
&[data-glass-appearance='frosted'][data-glass-quality='css'] body[data-theme='glass'] {
.layout-wrapper.layout-fixed-shell-backplate-active .layout-vertical-nav::before,
.layout-wrapper.layout-fixed-shell-backplate-active .layout-navbar,
@@ -1193,6 +1195,7 @@ html[data-theme='glass'] {
.glass-optical-layer--fixed {
position: fixed;
+ z-index: variables.$layout-vertical-nav-layout-navbar-z-index;
}
// 文档表面与该呈现层共享滚动坐标,canvas backing buffer 仍由 renderer 的质量预算约束。
@@ -1256,6 +1259,12 @@ html[data-glass-appearance='frosted']:is(
backdrop-filter: none !important;
}
+ .layout-wrapper:not(.layout-fixed-shell-backplate-active) .layout-vertical-nav::before,
+ .layout-wrapper:not(.layout-fixed-shell-backplate-active) .layout-navbar {
+ -webkit-backdrop-filter: var(--glass-native-surface-backdrop-filter) !important;
+ backdrop-filter: var(--glass-native-surface-backdrop-filter) !important;
+ }
+
.layout-wrapper.window-scrolled.layout-navbar-fixed .layout-navbar,
.layout-wrapper.layout-horizontal-nav-active.layout-horizontal-nav-scrolled.layout-navbar-fixed .layout-navbar {
-webkit-backdrop-filter: var(--glass-navbar-scrolled-backdrop-filter) !important;
@@ -1280,12 +1289,14 @@ html[data-glass-appearance='frosted'][data-glass-quality='balanced'] {
--glass-dashboard-backdrop-filter: var(--glass-native-surface-backdrop-filter);
--glass-native-surface-backdrop-filter: blur(calc(16px * var(--glass-frost-blur-scale, 1))) saturate(162%)
brightness(var(--glass-transmission-brightness));
+ --glass-fixed-shell-backplate-filter: var(--glass-native-surface-backdrop-filter);
}
html[data-glass-appearance='frosted'][data-glass-quality='high'] {
--glass-dashboard-backdrop-filter: var(--glass-native-surface-backdrop-filter);
--glass-native-surface-backdrop-filter: blur(calc(10px * var(--glass-frost-blur-scale, 1))) saturate(154%)
brightness(var(--glass-transmission-brightness));
+ --glass-fixed-shell-backplate-filter: var(--glass-native-surface-backdrop-filter);
}
// 异步滚动期间只暂停 GPU 动态层;原生壁纸基底不发生材质切换。