fix(glass): reuse CORS wallpaper sources (#640)

This commit is contained in:
InfinityPacer
2026-08-04 16:35:17 +08:00
committed by GitHub
parent a545f88b3c
commit afc95ad990
6 changed files with 92 additions and 10 deletions
+44 -1
View File
@@ -154,6 +154,7 @@ const globalSettingsStore = useGlobalSettingsStore()
const backgroundImages = ref<string[]>([]) const backgroundImages = ref<string[]>([])
const backgroundLayers = ref(createLoginBackgroundLayers()) const backgroundLayers = ref(createLoginBackgroundLayers())
const backgroundDisplayImages = ref<Record<string, string>>({}) const backgroundDisplayImages = ref<Record<string, string>>({})
const backgroundCorsReady = ref<Record<string, boolean>>({})
const backgroundToneProfiles = ref<Record<string, GlassWallpaperToneProfile>>({}) const backgroundToneProfiles = ref<Record<string, GlassWallpaperToneProfile>>({})
const activeImageIndex = ref(0) const activeImageIndex = ref(0)
const previousImageIndex = ref<number | null>(null) const previousImageIndex = ref<number | null>(null)
@@ -302,13 +303,25 @@ function getBackgroundLayerStyle(layer: LoginBackgroundLayer) {
const appearance = effectiveGlassSettings.value.glassAppearance const appearance = effectiveGlassSettings.value.glassAppearance
const materialExposure = appearance === 'frosted' ? 0.82 : appearance === 'tinted' ? 0.85 : 0.86 const materialExposure = appearance === 'frosted' ? 0.82 : appearance === 'tinted' ? 0.85 : 0.86
const displayUrl = isGlassTheme.value ? getPreparedBackgroundImage(layer.url) : layer.url const displayUrl = isGlassTheme.value ? getPreparedBackgroundImage(layer.url) : layer.url
const usesCorsImageElement = isGlassTheme.value && Object.hasOwn(backgroundDisplayImages.value, layer.url)
return { return {
'backgroundImage': displayUrl ? `url(${displayUrl})` : undefined, 'backgroundImage': !usesCorsImageElement && displayUrl ? `url(${displayUrl})` : undefined,
'--glass-wallpaper-brightness': String(materialExposure * profile.exposure), '--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 needsStableFixedBackdrop = isChromiumFixedShellBackplateBrowser()
const fixedShellBackplateLayers = computed<readonly GlassFixedShellBackplateLayer[]>(() => { const fixedShellBackplateLayers = computed<readonly GlassFixedShellBackplateLayer[]>(() => {
const hasWallpaper = renderedBackgroundLayers.value.some(layer => Boolean(layer.url)) const hasWallpaper = renderedBackgroundLayers.value.some(layer => Boolean(layer.url))
@@ -327,6 +340,8 @@ const fixedShellBackplateLayers = computed<readonly GlassFixedShellBackplateLaye
return renderedBackgroundLayers.value.map(layer => ({ return renderedBackgroundLayers.value.map(layer => ({
...layer, ...layer,
crossOrigin: getBackgroundLayerCrossOrigin(layer),
src: getBackgroundLayerImageSource(layer),
style: getBackgroundLayerStyle(layer), style: getBackgroundLayerStyle(layer),
})) }))
}) })
@@ -711,6 +726,10 @@ async function preloadBackgroundCandidate(imageUrl: string) {
...backgroundDisplayImages.value, ...backgroundDisplayImages.value,
[imageUrl]: opticalUrl, [imageUrl]: opticalUrl,
} }
backgroundCorsReady.value = {
...backgroundCorsReady.value,
[imageUrl]: true,
}
recordGlassLaunchTiming('wallpaper-source-ready', imageUrl) recordGlassLaunchTiming('wallpaper-source-ready', imageUrl)
return true return true
} }
@@ -719,6 +738,10 @@ async function preloadBackgroundCandidate(imageUrl: string) {
...backgroundDisplayImages.value, ...backgroundDisplayImages.value,
[imageUrl]: imageUrl, [imageUrl]: imageUrl,
} }
backgroundCorsReady.value = {
...backgroundCorsReady.value,
[imageUrl]: false,
}
const ready = await preloadImage(imageUrl) const ready = await preloadImage(imageUrl)
recordGlassLaunchTiming(ready ? 'wallpaper-source-ready' : 'wallpaper-source-failed', imageUrl) recordGlassLaunchTiming(ready ? 'wallpaper-source-ready' : 'wallpaper-source-failed', imageUrl)
@@ -1137,7 +1160,17 @@ onUnmounted(() => {
class="background-image" class="background-image"
:class="layer.role" :class="layer.role"
:style="getBackgroundLayerStyle(layer)" :style="getBackgroundLayerStyle(layer)"
>
<img
v-if="getBackgroundLayerImageSource(layer)"
class="background-image__source"
:crossorigin="getBackgroundLayerCrossOrigin(layer)"
:src="getBackgroundLayerImageSource(layer)"
alt=""
aria-hidden="true"
draggable="false"
/> />
</div>
<!-- 全局磨砂层 --> <!-- 全局磨砂层 -->
<div v-if="shouldRenderGlobalBlurLayer" class="global-blur-layer"></div> <div v-if="shouldRenderGlobalBlurLayer" class="global-blur-layer"></div>
</div> </div>
@@ -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 { .background-container.is-transparent-theme .background-image.active {
opacity: var(--transparent-background-poster-opacity, 1); opacity: var(--transparent-background-poster-opacity, 1);
} }
@@ -103,6 +103,7 @@ async function goPlay() {
@keyup.enter="goPlay" @keyup.enter="goPlay"
@keyup.space="goPlay" @keyup.space="goPlay"
> >
<!-- 媒体服务器图片统一采用匿名 CORS避免同一代理资源分裂为两种请求与缓存模式Safari 普通 dev 可能重复请求PWA 下由图片缓存路由统一处理 -->
<VImg <VImg
:src="imageUrl" :src="imageUrl"
crossorigin="anonymous" crossorigin="anonymous"
+1
View File
@@ -63,6 +63,7 @@ async function goPlay(isHovering: boolean | null = false) {
'ring-1': isImageLoaded, 'ring-1': isImageLoaded,
}" }"
> >
<!-- 媒体服务器图片统一采用匿名 CORS避免同一代理资源分裂为两种请求与缓存模式Safari 普通 dev 可能重复请求PWA 下由图片缓存路由统一处理 -->
<VImg <VImg
aspect-ratio="2/3" aspect-ratio="2/3"
:src="getImgUrl" :src="getImgUrl"
@@ -32,7 +32,17 @@ const transitionStyle = computed(() => ({
:class="`is-${layer.role}`" :class="`is-${layer.role}`"
:data-backplate-slot="layer.key" :data-backplate-slot="layer.key"
> >
<div class="glass-fixed-shell-backplate__wallpaper" :style="layer.style" /> <div class="glass-fixed-shell-backplate__wallpaper" :style="layer.style">
<img
v-if="layer.src"
class="glass-fixed-shell-backplate__source"
:crossorigin="layer.crossOrigin"
:src="layer.src"
alt=""
aria-hidden="true"
draggable="false"
/>
</div>
</div> </div>
</div> </div>
@@ -51,7 +61,17 @@ const transitionStyle = computed(() => ({
:class="`is-${layer.role}`" :class="`is-${layer.role}`"
:data-backplate-slot="layer.key" :data-backplate-slot="layer.key"
> >
<div class="glass-fixed-shell-backplate__wallpaper" :style="layer.style" /> <div class="glass-fixed-shell-backplate__wallpaper" :style="layer.style">
<img
v-if="layer.src"
class="glass-fixed-shell-backplate__source"
:crossorigin="layer.crossOrigin"
:src="layer.src"
alt=""
aria-hidden="true"
draggable="false"
/>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -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) { @media (prefers-reduced-motion: reduce) {
.glass-fixed-shell-backplate, .glass-fixed-shell-backplate,
.glass-fixed-shell-backplate__layer { .glass-fixed-shell-backplate__layer {
@@ -7,8 +7,9 @@ const initialLayers: readonly GlassFixedShellBackplateLayer[] = [
{ {
key: 'front', key: 'front',
role: 'active', role: 'active',
crossOrigin: 'anonymous',
src: '/wallpaper-current.jpg',
style: { style: {
'backgroundImage': 'url(/wallpaper-current.jpg)',
'--glass-wallpaper-brightness': '0.82', '--glass-wallpaper-brightness': '0.82',
}, },
url: '/wallpaper-current.jpg', url: '/wallpaper-current.jpg',
@@ -16,8 +17,9 @@ const initialLayers: readonly GlassFixedShellBackplateLayer[] = [
{ {
key: 'back', key: 'back',
role: 'standby', role: 'standby',
crossOrigin: 'anonymous',
src: '/wallpaper-next.jpg',
style: { style: {
'backgroundImage': 'url(/wallpaper-next.jpg)',
'--glass-wallpaper-brightness': '0.76', '--glass-wallpaper-brightness': '0.76',
}, },
url: '/wallpaper-next.jpg', url: '/wallpaper-next.jpg',
@@ -38,9 +40,10 @@ describe('GlassFixedShellBackplate', () => {
expect(wrapper.findAll('[data-backplate-surface="main"] [data-backplate-slot]')).toHaveLength(2) 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="front"]').classes()).toContain('is-active')
expect(wrapper.find('[data-backplate-slot="back"]').classes()).toContain('is-standby') expect(wrapper.find('[data-backplate-slot="back"]').classes()).toContain('is-standby')
expect( expect(wrapper.find('[data-backplate-slot="front"] img').attributes()).toMatchObject({
(wrapper.find('[data-backplate-slot="front"] > div').element as HTMLElement).style.backgroundImage, crossorigin: 'anonymous',
).toContain('/wallpaper-current.jpg') src: '/wallpaper-current.jpg',
})
expect(wrapper.find('[data-backplate-surface="main"]').attributes('style')).toContain( expect(wrapper.find('[data-backplate-surface="main"]').attributes('style')).toContain(
'--glass-fixed-shell-transition-duration: 1500ms', '--glass-fixed-shell-transition-duration: 1500ms',
) )
@@ -3,7 +3,11 @@ import type { ThemeCustomizerGlassAppearance, ThemeCustomizerGlassQuality } from
import type { LoginBackgroundLayer } from '@/utils/loginPresentation' import type { LoginBackgroundLayer } from '@/utils/loginPresentation'
export interface GlassFixedShellBackplateLayer extends LoginBackgroundLayer { export interface GlassFixedShellBackplateLayer extends LoginBackgroundLayer {
/** 当前槽位直接采样壁纸所需的背景图与色调变量。 */ /** 与 tone/WebGL 一致的图片请求模式;CORS 不可用时省略并使用普通图片回退。 */
crossOrigin?: 'anonymous'
/** 已完成首图准备、可直接进入稳定背板槽位的图片地址。 */
src: string
/** 当前槽位直接采样壁纸时叠加的色调变量。 */
style: StyleValue style: StyleValue
} }