mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-08-10 16:13:28 +08:00
fix(glass): scope fixed shell backplate (#603)
This commit is contained in:
@@ -76,6 +76,8 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return () => {
|
||||
const hasFixedShellBackplate = fixedShellBackplate.layers.value.length > 0
|
||||
|
||||
// 👉 Vertical nav
|
||||
const verticalNav = h(
|
||||
VerticalNav,
|
||||
@@ -88,15 +90,14 @@ export default defineComponent({
|
||||
},
|
||||
)
|
||||
|
||||
const fixedShellBackplateNode =
|
||||
fixedShellBackplate.layers.value.length > 0
|
||||
? h(GlassFixedShellBackplate, {
|
||||
isOverlayNav: mdAndDown.value,
|
||||
isOverlayNavActive: isOverlayNavActive.value,
|
||||
layers: fixedShellBackplate.layers.value,
|
||||
transitionDurationMs: fixedShellBackplate.transitionDurationMs,
|
||||
})
|
||||
: null
|
||||
const fixedShellBackplateNode = hasFixedShellBackplate
|
||||
? h(GlassFixedShellBackplate, {
|
||||
isOverlayNav: mdAndDown.value,
|
||||
isOverlayNavActive: isOverlayNavActive.value,
|
||||
layers: fixedShellBackplate.layers.value,
|
||||
transitionDurationMs: fixedShellBackplate.transitionDurationMs,
|
||||
})
|
||||
: null
|
||||
|
||||
// 👉 Navbar
|
||||
const navbar = h(
|
||||
@@ -158,6 +159,7 @@ export default defineComponent({
|
||||
isCollapsedLayout.value && 'layout-vertical-nav-collapsed',
|
||||
isHorizontalLayout.value && 'layout-horizontal-nav-active',
|
||||
isHorizontalLayout.value && isNavbarScrolled && 'layout-horizontal-nav-scrolled',
|
||||
hasFixedShellBackplate && 'layout-fixed-shell-backplate-active',
|
||||
route.meta.layoutWrapperClasses,
|
||||
!isHorizontalLayout.value && isNavbarScrolled && 'window-scrolled',
|
||||
],
|
||||
|
||||
@@ -29,6 +29,7 @@ import { useGlobalOfflineStatus, type ConnectionFailureReason } from '@/composab
|
||||
import { useAppActivityLifecycle } from '@/composables/useAppActivityLifecycle'
|
||||
import { useGlassWallpaperTransaction } from '@/composables/useGlassWallpaperTransaction'
|
||||
import {
|
||||
isChromiumFixedShellBackplateBrowser,
|
||||
provideGlassFixedShellBackplate,
|
||||
shouldUseGlassFixedShellBackplate,
|
||||
type GlassFixedShellBackplateLayer,
|
||||
@@ -308,6 +309,7 @@ function getBackgroundLayerStyle(layer: LoginBackgroundLayer) {
|
||||
}
|
||||
}
|
||||
|
||||
const needsStableFixedBackdrop = isChromiumFixedShellBackplateBrowser()
|
||||
const fixedShellBackplateLayers = computed<readonly GlassFixedShellBackplateLayer[]>(() => {
|
||||
const hasWallpaper = renderedBackgroundLayers.value.some(layer => Boolean(layer.url))
|
||||
if (
|
||||
@@ -315,6 +317,7 @@ const fixedShellBackplateLayers = computed<readonly GlassFixedShellBackplateLaye
|
||||
appearance: effectiveGlassSettings.value.glassAppearance,
|
||||
hasWallpaper,
|
||||
isAuthenticated: Boolean(isLogin.value),
|
||||
needsStableFixedBackdrop,
|
||||
quality: effectiveGlassSettings.value.glassQuality,
|
||||
themeName: globalTheme.name.value,
|
||||
})
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { shouldUseGlassFixedShellBackplate } from '@/composables/useGlassFixedShellBackplate'
|
||||
import {
|
||||
isChromiumFixedShellBackplateBrowser,
|
||||
shouldUseGlassFixedShellBackplate,
|
||||
} from '@/composables/useGlassFixedShellBackplate'
|
||||
|
||||
describe('shouldUseGlassFixedShellBackplate', () => {
|
||||
const eligible = {
|
||||
appearance: 'frosted' as const,
|
||||
hasWallpaper: true,
|
||||
isAuthenticated: true,
|
||||
needsStableFixedBackdrop: true,
|
||||
quality: 'css' as const,
|
||||
themeName: 'glass',
|
||||
}
|
||||
|
||||
it('enables the direct wallpaper backplate only for authenticated frosted CSS rendering', () => {
|
||||
it('enables the direct wallpaper backplate only for affected authenticated Chromium rendering', () => {
|
||||
expect(shouldUseGlassFixedShellBackplate(eligible)).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)
|
||||
@@ -21,3 +26,58 @@ describe('shouldUseGlassFixedShellBackplate', () => {
|
||||
expect(shouldUseGlassFixedShellBackplate({ ...eligible, hasWallpaper: false })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isChromiumFixedShellBackplateBrowser', () => {
|
||||
it('uses the Chromium engine brand for Chrome and Edge', () => {
|
||||
expect(
|
||||
isChromiumFixedShellBackplateBrowser({
|
||||
userAgent: 'Mozilla/5.0 Chrome/140.0.0.0 Safari/537.36',
|
||||
userAgentData: {
|
||||
brands: [{ brand: 'Chromium' }, { brand: 'Google Chrome' }],
|
||||
},
|
||||
}),
|
||||
).toBe(true)
|
||||
expect(
|
||||
isChromiumFixedShellBackplateBrowser({
|
||||
userAgent: 'Mozilla/5.0 Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0',
|
||||
userAgentData: {
|
||||
brands: [{ brand: 'Chromium' }, { brand: 'Microsoft Edge' }],
|
||||
},
|
||||
}),
|
||||
).toBe(true)
|
||||
expect(
|
||||
isChromiumFixedShellBackplateBrowser({
|
||||
userAgent: 'Mozilla/5.0 Version/26.4 Safari/605.1.15',
|
||||
userAgentData: {
|
||||
brands: [{ brand: 'Safari' }],
|
||||
},
|
||||
}),
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it.each([
|
||||
['Chrome', 'Mozilla/5.0 Chrome/140.0.0.0 Safari/537.36'],
|
||||
['Edge', 'Mozilla/5.0 Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0'],
|
||||
['Opera', 'Mozilla/5.0 Chrome/140.0.0.0 Safari/537.36 OPR/121.0.0.0'],
|
||||
['Vivaldi', 'Mozilla/5.0 Chrome/140.0.0.0 Safari/537.36 Vivaldi/7.6.0.0'],
|
||||
['Yandex Browser', 'Mozilla/5.0 Chrome/140.0.0.0 Safari/537.36 YaBrowser/25.8.0.0'],
|
||||
['Samsung Internet', 'Mozilla/5.0 Chrome/140.0.0.0 Mobile Safari/537.36 SamsungBrowser/28.0'],
|
||||
['Android WebView', 'Mozilla/5.0; wv) Version/4.0 Chrome/140.0.0.0 Mobile Safari/537.36'],
|
||||
['Chromium', 'Mozilla/5.0 Chromium/140.0.0.0 Safari/537.36'],
|
||||
])('falls back to Chromium UA tokens for %s', (_browser, userAgent) => {
|
||||
expect(isChromiumFixedShellBackplateBrowser({ userAgent })).toBe(true)
|
||||
})
|
||||
|
||||
it('leaves Safari and iOS Chrome on the native backdrop path', () => {
|
||||
expect(
|
||||
isChromiumFixedShellBackplateBrowser({
|
||||
userAgent: 'Mozilla/5.0 Version/26.4 Safari/605.1.15',
|
||||
}),
|
||||
).toBe(false)
|
||||
expect(
|
||||
isChromiumFixedShellBackplateBrowser({
|
||||
userAgent: 'Mozilla/5.0 CriOS/140.0.0.0 Mobile/15E148 Safari/604.1',
|
||||
}),
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -21,12 +21,25 @@ interface GlassFixedShellBackplateEligibility {
|
||||
hasWallpaper: boolean
|
||||
/** 当前页面是否处于认证后的主布局。 */
|
||||
isAuthenticated: boolean
|
||||
/** 当前浏览器是否需要绕开 Chromium fixed document backdrop 的重合成路径。 */
|
||||
needsStableFixedBackdrop: boolean
|
||||
/** 当前玻璃渲染质量。 */
|
||||
quality: ThemeCustomizerGlassQuality
|
||||
/** Vuetify 当前解析后的主题名。 */
|
||||
themeName: string
|
||||
}
|
||||
|
||||
interface GlassFixedShellBrowserIdentity {
|
||||
/** User-Agent Client Hints 暴露的浏览器品牌。 */
|
||||
userAgentData?: {
|
||||
brands?: readonly {
|
||||
brand: string
|
||||
}[]
|
||||
}
|
||||
/** Client Hints 不可用时使用的传统浏览器标识。 */
|
||||
userAgent: string
|
||||
}
|
||||
|
||||
const GLASS_FIXED_SHELL_BACKPLATE_KEY: InjectionKey<GlassFixedShellBackplateContext> =
|
||||
Symbol('glass-fixed-shell-backplate')
|
||||
const EMPTY_FIXED_SHELL_LAYERS = shallowRef<readonly GlassFixedShellBackplateLayer[]>([])
|
||||
@@ -35,10 +48,19 @@ const EMPTY_FIXED_SHELL_CONTEXT: GlassFixedShellBackplateContext = {
|
||||
transitionDurationMs: 0,
|
||||
}
|
||||
|
||||
/** 只有磨砂标准使用直接壁纸背板;其他材质和 GPU 质量继续走既有呈现路径。 */
|
||||
/** Chromium 使用独立 fixed 背板规避 document backdrop 重合成;WebKit 保留原生采样路径。 */
|
||||
export function isChromiumFixedShellBackplateBrowser(browserIdentity: GlassFixedShellBrowserIdentity = navigator) {
|
||||
const brands = browserIdentity.userAgentData?.brands
|
||||
if (brands?.length) return brands.some(({ brand }) => brand === 'Chromium')
|
||||
|
||||
return /\b(?:Chrome|Chromium)\/\d+/u.test(browserIdentity.userAgent)
|
||||
}
|
||||
|
||||
/** 只有需要稳定 fixed 背板的 Chromium 磨砂标准布局使用直接壁纸采样。 */
|
||||
export function shouldUseGlassFixedShellBackplate(options: GlassFixedShellBackplateEligibility) {
|
||||
return (
|
||||
options.isAuthenticated &&
|
||||
options.needsStableFixedBackdrop &&
|
||||
options.themeName === 'glass' &&
|
||||
options.appearance === 'frosted' &&
|
||||
options.quality === 'css' &&
|
||||
|
||||
@@ -39,7 +39,7 @@ describe('glass overlay material styles', () => {
|
||||
|
||||
expect(styles).toContain('--glass-fixed-shell-backplate-filter: blur(min(var(--glass-blur-raised), 60px))')
|
||||
expect(styles).toMatch(
|
||||
/&\[data-glass-appearance='frosted'\]\[data-glass-quality='css'\][\s\S]*?\.layout-vertical-nav::before,[\s\S]*?\.layout-navbar,[\s\S]*?backdrop-filter:\s*none\s*!important;/,
|
||||
/&\[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(backplate).toContain('filter: var(--glass-fixed-shell-backplate-filter)')
|
||||
expect(backplate).not.toContain('backdrop-filter')
|
||||
|
||||
@@ -462,11 +462,12 @@ html[data-theme='glass'] {
|
||||
}
|
||||
}
|
||||
|
||||
// 磨砂标准直接渲染稳定壁纸背板,固定功能层不得读取随页面重绘的 document backdrop。
|
||||
// Chromium 磨砂标准直接渲染稳定壁纸背板,固定功能层不得读取随页面重绘的 document backdrop。
|
||||
&[data-glass-appearance='frosted'][data-glass-quality='css'] body[data-theme='glass'] {
|
||||
.layout-vertical-nav::before,
|
||||
.layout-navbar,
|
||||
.layout-wrapper.layout-horizontal-nav-active.layout-horizontal-nav-scrolled.layout-navbar-fixed .layout-navbar {
|
||||
.layout-wrapper.layout-fixed-shell-backplate-active .layout-vertical-nav::before,
|
||||
.layout-wrapper.layout-fixed-shell-backplate-active .layout-navbar,
|
||||
.layout-wrapper.layout-fixed-shell-backplate-active.layout-horizontal-nav-active.layout-horizontal-nav-scrolled.layout-navbar-fixed
|
||||
.layout-navbar {
|
||||
-webkit-backdrop-filter: none !important;
|
||||
backdrop-filter: none !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user