diff --git a/src/App.vue b/src/App.vue index 883cd7b0..2b9d2489 100644 --- a/src/App.vue +++ b/src/App.vue @@ -154,6 +154,7 @@ const globalSettingsStore = useGlobalSettingsStore() const backgroundImages = ref([]) const backgroundLayers = ref(createLoginBackgroundLayers()) const backgroundDisplayImages = ref>({}) +const backgroundCorsReady = ref>({}) const backgroundToneProfiles = ref>({}) const activeImageIndex = ref(0) const previousImageIndex = ref(null) @@ -302,13 +303,25 @@ function getBackgroundLayerStyle(layer: LoginBackgroundLayer) { const appearance = effectiveGlassSettings.value.glassAppearance const materialExposure = appearance === 'frosted' ? 0.82 : appearance === 'tinted' ? 0.85 : 0.86 const displayUrl = isGlassTheme.value ? getPreparedBackgroundImage(layer.url) : layer.url + const usesCorsImageElement = isGlassTheme.value && Object.hasOwn(backgroundDisplayImages.value, layer.url) return { - 'backgroundImage': displayUrl ? `url(${displayUrl})` : undefined, + 'backgroundImage': !usesCorsImageElement && displayUrl ? `url(${displayUrl})` : undefined, '--glass-wallpaper-brightness': String(materialExposure * profile.exposure), } } +/** 玻璃可见层与 tone/WebGL 使用同一图片请求模式,避免 CSS 再创建无 Origin 的缓存变体。 */ +function getBackgroundLayerImageSource(layer: LoginBackgroundLayer) { + if (!isGlassTheme.value || !Object.hasOwn(backgroundDisplayImages.value, layer.url)) return '' + + return getPreparedBackgroundImage(layer.url) +} + +function getBackgroundLayerCrossOrigin(layer: LoginBackgroundLayer) { + return backgroundCorsReady.value[layer.url] ? 'anonymous' : undefined +} + const needsStableFixedBackdrop = isChromiumFixedShellBackplateBrowser() const fixedShellBackplateLayers = computed(() => { const hasWallpaper = renderedBackgroundLayers.value.some(layer => Boolean(layer.url)) @@ -327,6 +340,8 @@ const fixedShellBackplateLayers = computed ({ ...layer, + crossOrigin: getBackgroundLayerCrossOrigin(layer), + src: getBackgroundLayerImageSource(layer), style: getBackgroundLayerStyle(layer), })) }) @@ -711,6 +726,10 @@ async function preloadBackgroundCandidate(imageUrl: string) { ...backgroundDisplayImages.value, [imageUrl]: opticalUrl, } + backgroundCorsReady.value = { + ...backgroundCorsReady.value, + [imageUrl]: true, + } recordGlassLaunchTiming('wallpaper-source-ready', imageUrl) return true } @@ -719,6 +738,10 @@ async function preloadBackgroundCandidate(imageUrl: string) { ...backgroundDisplayImages.value, [imageUrl]: imageUrl, } + backgroundCorsReady.value = { + ...backgroundCorsReady.value, + [imageUrl]: false, + } const ready = await preloadImage(imageUrl) recordGlassLaunchTiming(ready ? 'wallpaper-source-ready' : 'wallpaper-source-failed', imageUrl) @@ -1137,7 +1160,17 @@ onUnmounted(() => { class="background-image" :class="layer.role" :style="getBackgroundLayerStyle(layer)" - /> + > + +
@@ -1238,6 +1271,16 @@ onUnmounted(() => { } } +.background-image__source { + position: absolute; + display: block; + block-size: 100%; + inline-size: 100%; + inset: 0; + object-fit: cover; + pointer-events: none; +} + .background-container.is-transparent-theme .background-image.active { opacity: var(--transparent-background-poster-opacity, 1); } diff --git a/src/components/cards/PlayingBackdropCard.vue b/src/components/cards/PlayingBackdropCard.vue index 92aba7e6..abeb5379 100644 --- a/src/components/cards/PlayingBackdropCard.vue +++ b/src/components/cards/PlayingBackdropCard.vue @@ -103,6 +103,7 @@ async function goPlay() { @keyup.enter="goPlay" @keyup.space="goPlay" > + + ({ :class="`is-${layer.role}`" :data-backplate-slot="layer.key" > -
+
+ +
@@ -51,7 +61,17 @@ const transitionStyle = computed(() => ({ :class="`is-${layer.role}`" :data-backplate-slot="layer.key" > -
+
+ +
@@ -160,6 +180,16 @@ const transitionStyle = computed(() => ({ } } +.glass-fixed-shell-backplate__source { + position: absolute; + display: block; + block-size: 100%; + inline-size: 100%; + inset: 0; + object-fit: cover; + pointer-events: none; +} + @media (prefers-reduced-motion: reduce) { .glass-fixed-shell-backplate, .glass-fixed-shell-backplate__layer { diff --git a/src/components/theme/__tests__/GlassFixedShellBackplate.spec.ts b/src/components/theme/__tests__/GlassFixedShellBackplate.spec.ts index 07790189..d41a5d04 100644 --- a/src/components/theme/__tests__/GlassFixedShellBackplate.spec.ts +++ b/src/components/theme/__tests__/GlassFixedShellBackplate.spec.ts @@ -7,8 +7,9 @@ const initialLayers: readonly GlassFixedShellBackplateLayer[] = [ { key: 'front', role: 'active', + crossOrigin: 'anonymous', + src: '/wallpaper-current.jpg', style: { - 'backgroundImage': 'url(/wallpaper-current.jpg)', '--glass-wallpaper-brightness': '0.82', }, url: '/wallpaper-current.jpg', @@ -16,8 +17,9 @@ const initialLayers: readonly GlassFixedShellBackplateLayer[] = [ { key: 'back', role: 'standby', + crossOrigin: 'anonymous', + src: '/wallpaper-next.jpg', style: { - 'backgroundImage': 'url(/wallpaper-next.jpg)', '--glass-wallpaper-brightness': '0.76', }, url: '/wallpaper-next.jpg', @@ -38,9 +40,10 @@ describe('GlassFixedShellBackplate', () => { expect(wrapper.findAll('[data-backplate-surface="main"] [data-backplate-slot]')).toHaveLength(2) expect(wrapper.find('[data-backplate-slot="front"]').classes()).toContain('is-active') expect(wrapper.find('[data-backplate-slot="back"]').classes()).toContain('is-standby') - expect( - (wrapper.find('[data-backplate-slot="front"] > div').element as HTMLElement).style.backgroundImage, - ).toContain('/wallpaper-current.jpg') + expect(wrapper.find('[data-backplate-slot="front"] img').attributes()).toMatchObject({ + crossorigin: 'anonymous', + src: '/wallpaper-current.jpg', + }) expect(wrapper.find('[data-backplate-surface="main"]').attributes('style')).toContain( '--glass-fixed-shell-transition-duration: 1500ms', ) diff --git a/src/composables/useGlassFixedShellBackplate.ts b/src/composables/useGlassFixedShellBackplate.ts index 39667798..16ba849e 100644 --- a/src/composables/useGlassFixedShellBackplate.ts +++ b/src/composables/useGlassFixedShellBackplate.ts @@ -3,7 +3,11 @@ import type { ThemeCustomizerGlassAppearance, ThemeCustomizerGlassQuality } from import type { LoginBackgroundLayer } from '@/utils/loginPresentation' export interface GlassFixedShellBackplateLayer extends LoginBackgroundLayer { - /** 当前槽位直接采样壁纸所需的背景图与色调变量。 */ + /** 与 tone/WebGL 一致的图片请求模式;CORS 不可用时省略并使用普通图片回退。 */ + crossOrigin?: 'anonymous' + /** 已完成首图准备、可直接进入稳定背板槽位的图片地址。 */ + src: string + /** 当前槽位直接采样壁纸时叠加的色调变量。 */ style: StyleValue }