mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-08 17:26:41 +08:00
fix(glass): 收敛壁纸预加载与失效恢复 (#592)
* fix(glass): streamline wallpaper loading * docs(glass): remove stale compatibility comment * docs(glass): remove obsolete texture fallback note
This commit is contained in:
@@ -54,9 +54,6 @@
|
|||||||
"@typescript-eslint/no-unused-vars": {
|
"@typescript-eslint/no-unused-vars": {
|
||||||
"count": 1
|
"count": 1
|
||||||
},
|
},
|
||||||
"no-useless-catch": {
|
|
||||||
"count": 1
|
|
||||||
},
|
|
||||||
"sonarjs/no-ignored-exceptions": {
|
"sonarjs/no-ignored-exceptions": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-57
@@ -29,9 +29,8 @@ import { useGlobalOfflineStatus, type ConnectionFailureReason } from '@/composab
|
|||||||
import { useAppActivityLifecycle } from '@/composables/useAppActivityLifecycle'
|
import { useAppActivityLifecycle } from '@/composables/useAppActivityLifecycle'
|
||||||
import {
|
import {
|
||||||
BACKGROUND_ROTATION_GRACE_MS,
|
BACKGROUND_ROTATION_GRACE_MS,
|
||||||
commitPreloadedBackgroundRotation,
|
findFirstAvailableBackground,
|
||||||
preloadBackgroundRotationImages,
|
preloadBackgroundRotationImages,
|
||||||
preloadBackgroundSequence,
|
|
||||||
shouldAllowBackgroundRotation,
|
shouldAllowBackgroundRotation,
|
||||||
} from '@/utils/backgroundRotation'
|
} from '@/utils/backgroundRotation'
|
||||||
import {
|
import {
|
||||||
@@ -163,7 +162,6 @@ const wallpaperRequestMode = computed(() => getLoginWallpaperRequestMode(loginVi
|
|||||||
const activeBackgroundImage = computed(() => backgroundImages.value[activeImageIndex.value] ?? '')
|
const activeBackgroundImage = computed(() => backgroundImages.value[activeImageIndex.value] ?? '')
|
||||||
const renderedBackgroundLayers = computed(() => backgroundLayers.value)
|
const renderedBackgroundLayers = computed(() => backgroundLayers.value)
|
||||||
const getOpticalBackgroundImage = (imageUrl: string) => getDisplayImageUrl(imageUrl, Boolean(isLogin.value))
|
const getOpticalBackgroundImage = (imageUrl: string) => getDisplayImageUrl(imageUrl, Boolean(isLogin.value))
|
||||||
// 玻璃 profile 使用同源 catalog;旧后端返回外链时仍由 renderer 的纹理失败路径安全回退。
|
|
||||||
const activeOpticalBackgroundImage = computed(() => getOpticalBackgroundImage(activeBackgroundImage.value))
|
const activeOpticalBackgroundImage = computed(() => getOpticalBackgroundImage(activeBackgroundImage.value))
|
||||||
const previousOpticalBackgroundImage = computed(() => {
|
const previousOpticalBackgroundImage = computed(() => {
|
||||||
const previousIndex = previousImageIndex.value
|
const previousIndex = previousImageIndex.value
|
||||||
@@ -195,8 +193,9 @@ let backgroundCrossfadeTimer: number | null = null
|
|||||||
let pendingOpticalWallpaperTimer: number | null = null
|
let pendingOpticalWallpaperTimer: number | null = null
|
||||||
let pendingOpticalWallpaperResolve: ((ready: boolean) => void) | null = null
|
let pendingOpticalWallpaperResolve: ((ready: boolean) => void) | null = null
|
||||||
let authenticatedStateTimer: number | null = null
|
let authenticatedStateTimer: number | null = null
|
||||||
|
let backgroundLoadVersion = 0
|
||||||
|
let backgroundRecoveryAttemptedVersion = -1
|
||||||
let backgroundRotationVersion = 0
|
let backgroundRotationVersion = 0
|
||||||
let backgroundPreloadVersion = 0
|
|
||||||
|
|
||||||
// 读取并同步透明主题背景设置到根组件响应式状态。
|
// 读取并同步透明主题背景设置到根组件响应式状态。
|
||||||
function applyTransparentBackgroundSettings() {
|
function applyTransparentBackgroundSettings() {
|
||||||
@@ -480,66 +479,65 @@ function activateBackgroundImage(nextIndex: number) {
|
|||||||
}, BACKGROUND_CROSSFADE_DURATION_MS)
|
}, BACKGROUND_CROSSFADE_DURATION_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取背景图片
|
// 获取背景图片列表;只有选出实际可用的首图后才提交到可见状态。
|
||||||
async function fetchBackgroundImages(requestMode: LoginWallpaperRequestMode) {
|
async function fetchBackgroundImages(requestMode: LoginWallpaperRequestMode) {
|
||||||
|
backgroundRequestController?.abort()
|
||||||
|
const controller = new AbortController()
|
||||||
|
backgroundRequestController = controller
|
||||||
try {
|
try {
|
||||||
backgroundRequestController?.abort()
|
return await api.get<string[], string[]>(`/login/wallpapers`, {
|
||||||
backgroundRequestController = new AbortController()
|
|
||||||
backgroundImages.value = await api.get(`/login/wallpapers`, {
|
|
||||||
params: requestMode === 'same-origin' ? { same_origin: true } : undefined,
|
params: requestMode === 'same-origin' ? { same_origin: true } : undefined,
|
||||||
signal: backgroundRequestController.signal,
|
signal: controller.signal,
|
||||||
})
|
})
|
||||||
activeImageIndex.value = 0
|
} finally {
|
||||||
resetBackgroundCrossfade()
|
if (backgroundRequestController === controller) backgroundRequestController = null
|
||||||
} catch (e) {
|
|
||||||
throw e
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 仅提前加载当前图的下一项,不建立全目录预载队列。 */
|
||||||
|
function preloadNextBackgroundImage() {
|
||||||
|
if (!allowsBackgroundRotation.value || backgroundImages.value.length <= 1) return
|
||||||
|
const nextIndex = (activeImageIndex.value + 1) % backgroundImages.value.length
|
||||||
|
void preloadImage(backgroundImages.value[nextIndex])
|
||||||
|
}
|
||||||
|
|
||||||
// 背景图片轮换函数
|
// 背景图片轮换函数
|
||||||
function rotateBackgroundImage() {
|
async function rotateBackgroundImage() {
|
||||||
if (!allowsBackgroundRotation.value) return
|
if (!allowsBackgroundRotation.value || backgroundImages.value.length <= 1) return
|
||||||
|
|
||||||
if (backgroundImages.value.length > 1) {
|
const requestVersion = ++backgroundRotationVersion
|
||||||
// 计算下一个图片索引
|
const activeIndex = activeImageIndex.value
|
||||||
const nextIndex = (activeImageIndex.value + 1) % backgroundImages.value.length
|
for (let offset = 1; offset < backgroundImages.value.length; offset += 1) {
|
||||||
const requestVersion = ++backgroundRotationVersion
|
if (!allowsBackgroundRotation.value || requestVersion !== backgroundRotationVersion) return
|
||||||
|
|
||||||
|
const nextIndex = (activeIndex + offset) % backgroundImages.value.length
|
||||||
const nextImage = backgroundImages.value[nextIndex]
|
const nextImage = backgroundImages.value[nextIndex]
|
||||||
const opticalImage = shouldRenderGlassOpticalLayer.value ? getOpticalBackgroundImage(nextImage) : undefined
|
const opticalImage = shouldRenderGlassOpticalLayer.value ? getOpticalBackgroundImage(nextImage) : undefined
|
||||||
|
const imagesReady = await preloadBackgroundRotationImages({
|
||||||
void commitPreloadedBackgroundRotation({
|
displayUrl: nextImage,
|
||||||
canCommit: () => allowsBackgroundRotation.value && requestVersion === backgroundRotationVersion,
|
opticalUrl: opticalImage,
|
||||||
commit: () => activateBackgroundImage(nextIndex),
|
preload: preloadImage,
|
||||||
preload: () =>
|
|
||||||
preloadBackgroundRotationImages({
|
|
||||||
displayUrl: nextImage,
|
|
||||||
opticalUrl: opticalImage,
|
|
||||||
preload: preloadImage,
|
|
||||||
}).then(imagesReady => (imagesReady && opticalImage ? prepareOpticalWallpaper(opticalImage) : imagesReady)),
|
|
||||||
})
|
})
|
||||||
|
if (!imagesReady) continue
|
||||||
|
if (opticalImage && !(await prepareOpticalWallpaper(opticalImage))) continue
|
||||||
|
if (!allowsBackgroundRotation.value || requestVersion !== backgroundRotationVersion) return
|
||||||
|
|
||||||
|
activateBackgroundImage(nextIndex)
|
||||||
|
preloadNextBackgroundImage()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestVersion === backgroundRotationVersion && backgroundRecoveryAttemptedVersion !== backgroundLoadVersion) {
|
||||||
|
stopBackgroundRotation()
|
||||||
|
const recoveryVersion = ++backgroundLoadVersion
|
||||||
|
backgroundRecoveryAttemptedVersion = recoveryVersion
|
||||||
|
void loadBackgroundImages(wallpaperRequestMode.value, recoveryVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 当前图稳定后按轮播顺序串行预加载其余壁纸,队列失效时不再发起新请求。 */
|
// 停止轮询并使已经发起的下一图准备失效,避免非活动状态收到迟到提交。
|
||||||
async function preloadRemainingBackgroundImages() {
|
|
||||||
if (backgroundImages.value.length <= 1) return
|
|
||||||
|
|
||||||
const version = ++backgroundPreloadVersion
|
|
||||||
const orderedImages = backgroundImages.value
|
|
||||||
.slice(activeImageIndex.value + 1)
|
|
||||||
.concat(backgroundImages.value.slice(0, activeImageIndex.value))
|
|
||||||
|
|
||||||
await preloadBackgroundSequence({
|
|
||||||
canContinue: () => version === backgroundPreloadVersion && shouldLoadBackgroundImages.value,
|
|
||||||
preload: preloadImage,
|
|
||||||
urls: orderedImages,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 停止轮询并使已经发起的壁纸预加载失效,避免非活动状态收到迟到提交。
|
|
||||||
function stopBackgroundRotation() {
|
function stopBackgroundRotation() {
|
||||||
backgroundRotationVersion += 1
|
backgroundRotationVersion += 1
|
||||||
backgroundPreloadVersion += 1
|
|
||||||
removeBackgroundTimer('background-rotation')
|
removeBackgroundTimer('background-rotation')
|
||||||
settlePendingOpticalWallpaper(false)
|
settlePendingOpticalWallpaper(false)
|
||||||
}
|
}
|
||||||
@@ -567,12 +565,11 @@ function startBackgroundRotation() {
|
|||||||
stopBackgroundRotation()
|
stopBackgroundRotation()
|
||||||
|
|
||||||
if (allowsBackgroundRotation.value && backgroundImages.value.length > 1) {
|
if (allowsBackgroundRotation.value && backgroundImages.value.length > 1) {
|
||||||
// 宽限期结束会使预载队列失效;恢复活动状态时从当前图继续补齐剩余壁纸。
|
preloadNextBackgroundImage()
|
||||||
void preloadRemainingBackgroundImages()
|
|
||||||
// 隐藏页面也允许在有界宽限期内轮换,回调自身会再次核对生命周期。
|
// 隐藏页面也允许在有界宽限期内轮换,回调自身会再次核对生命周期。
|
||||||
addBackgroundTimer(
|
addBackgroundTimer(
|
||||||
'background-rotation',
|
'background-rotation',
|
||||||
rotateBackgroundImage,
|
() => void rotateBackgroundImage(),
|
||||||
10000, // 每10秒切换一次
|
10000, // 每10秒切换一次
|
||||||
{
|
{
|
||||||
runInBackground: true,
|
runInBackground: true,
|
||||||
@@ -614,6 +611,7 @@ watch(allowsBackgroundRotation, allowsRotation => {
|
|||||||
|
|
||||||
// 停止登录页、透明主题或玻璃主题背景图加载、重试和轮播。
|
// 停止登录页、透明主题或玻璃主题背景图加载、重试和轮播。
|
||||||
function stopBackgroundLoading() {
|
function stopBackgroundLoading() {
|
||||||
|
backgroundLoadVersion += 1
|
||||||
backgroundRequestController?.abort()
|
backgroundRequestController?.abort()
|
||||||
backgroundRequestController = null
|
backgroundRequestController = null
|
||||||
|
|
||||||
@@ -710,21 +708,43 @@ async function removeLoadingWithStateCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 加载背景图片
|
// 加载背景图片
|
||||||
async function loadBackgroundImages(requestMode: LoginWallpaperRequestMode, retryCount = 0) {
|
async function loadBackgroundImages(requestMode: LoginWallpaperRequestMode, loadVersion: number, retryCount = 0) {
|
||||||
const maxRetries = 3
|
const maxRetries = 3
|
||||||
try {
|
try {
|
||||||
await fetchBackgroundImages(requestMode)
|
const images = await fetchBackgroundImages(requestMode)
|
||||||
const activeImage = activeBackgroundImage.value
|
if (loadVersion !== backgroundLoadVersion) return
|
||||||
if (activeImage && !(await preloadImage(activeImage))) throw new Error('登录壁纸首图预加载失败')
|
|
||||||
|
const firstAvailableIndex = await findFirstAvailableBackground({
|
||||||
|
urls: images,
|
||||||
|
canContinue: () => loadVersion === backgroundLoadVersion,
|
||||||
|
preload: preloadImage,
|
||||||
|
})
|
||||||
|
if (firstAvailableIndex === null) throw new Error('没有可用的登录壁纸')
|
||||||
|
if (loadVersion !== backgroundLoadVersion) return
|
||||||
|
|
||||||
|
const currentImage = activeBackgroundImage.value
|
||||||
|
const currentIndex = images.indexOf(currentImage)
|
||||||
|
if (currentImage && currentIndex < 0) {
|
||||||
|
backgroundImages.value = [currentImage, ...images]
|
||||||
|
activeImageIndex.value = 0
|
||||||
|
} else {
|
||||||
|
backgroundImages.value = images
|
||||||
|
activeImageIndex.value = currentIndex >= 0 ? currentIndex : firstAvailableIndex
|
||||||
|
}
|
||||||
|
backgroundRecoveryAttemptedVersion = -1
|
||||||
|
resetBackgroundCrossfade()
|
||||||
startBackgroundRotation()
|
startBackgroundRotation()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
if (loadVersion !== backgroundLoadVersion) return
|
||||||
const isAbortError = error.name === 'AbortError' || error.code === 'ERR_CANCELED'
|
const isAbortError = error.name === 'AbortError' || error.code === 'ERR_CANCELED'
|
||||||
if (retryCount < maxRetries) {
|
if (retryCount < maxRetries) {
|
||||||
const baseDelay = isAbortError ? 1000 : 3000
|
const baseDelay = isAbortError ? 1000 : 3000
|
||||||
const retryDelay = Math.min(baseDelay * Math.pow(2, retryCount), 10000)
|
const retryDelay = Math.min(baseDelay * Math.pow(2, retryCount), 10000)
|
||||||
backgroundRetryTimer = window.setTimeout(() => {
|
backgroundRetryTimer = window.setTimeout(() => {
|
||||||
backgroundRetryTimer = null
|
backgroundRetryTimer = null
|
||||||
loadBackgroundImages(requestMode, retryCount + 1)
|
if (loadVersion === backgroundLoadVersion) {
|
||||||
|
void loadBackgroundImages(requestMode, loadVersion, retryCount + 1)
|
||||||
|
}
|
||||||
}, retryDelay)
|
}, retryDelay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -771,10 +791,12 @@ onMounted(async () => {
|
|||||||
// 背景范围或玻璃同源能力变化时重新加载;登录前后玻璃主题保持同一请求模式和活动壁纸。
|
// 背景范围或玻璃同源能力变化时重新加载;登录前后玻璃主题保持同一请求模式和活动壁纸。
|
||||||
watch(
|
watch(
|
||||||
() => [shouldLoadBackgroundImages.value, wallpaperRequestMode.value] as const,
|
() => [shouldLoadBackgroundImages.value, wallpaperRequestMode.value] as const,
|
||||||
([shouldLoad, requestMode]) => {
|
([shouldLoad, requestMode], previous) => {
|
||||||
|
if (previous && shouldLoad === previous[0] && requestMode === previous[1]) return
|
||||||
|
|
||||||
stopBackgroundLoading()
|
stopBackgroundLoading()
|
||||||
if (shouldLoad) {
|
if (shouldLoad) {
|
||||||
loadBackgroundImages(requestMode)
|
void loadBackgroundImages(requestMode, backgroundLoadVersion)
|
||||||
} else if (!isBackdropTheme.value) {
|
} else if (!isBackdropTheme.value) {
|
||||||
backgroundImages.value = []
|
backgroundImages.value = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
BACKGROUND_ROTATION_GRACE_MS,
|
BACKGROUND_ROTATION_GRACE_MS,
|
||||||
commitPreloadedBackgroundRotation,
|
findFirstAvailableBackground,
|
||||||
preloadBackgroundRotationImages,
|
preloadBackgroundRotationImages,
|
||||||
preloadBackgroundSequence,
|
|
||||||
shouldAllowBackgroundRotation,
|
shouldAllowBackgroundRotation,
|
||||||
} from '@/utils/backgroundRotation'
|
} from '@/utils/backgroundRotation'
|
||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
@@ -28,56 +27,6 @@ describe('background rotation lifecycle', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('commitPreloadedBackgroundRotation', () => {
|
|
||||||
it('drops a successful preload when the rotation becomes inactive before completion', async () => {
|
|
||||||
const preload = deferred<boolean>()
|
|
||||||
const commit = vi.fn()
|
|
||||||
let active = true
|
|
||||||
const result = commitPreloadedBackgroundRotation({
|
|
||||||
canCommit: () => active,
|
|
||||||
commit,
|
|
||||||
preload: () => preload.promise,
|
|
||||||
})
|
|
||||||
|
|
||||||
active = false
|
|
||||||
preload.resolve(true)
|
|
||||||
|
|
||||||
await expect(result).resolves.toBe(false)
|
|
||||||
expect(commit).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('commits a successful preload while the request remains current', async () => {
|
|
||||||
const commit = vi.fn()
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
commitPreloadedBackgroundRotation({
|
|
||||||
canCommit: () => true,
|
|
||||||
commit,
|
|
||||||
preload: async () => true,
|
|
||||||
}),
|
|
||||||
).resolves.toBe(true)
|
|
||||||
expect(commit).toHaveBeenCalledOnce()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('drops an obsolete preload even when decorative motion becomes active again', async () => {
|
|
||||||
const preload = deferred<boolean>()
|
|
||||||
const commit = vi.fn()
|
|
||||||
const requestVersion = 1
|
|
||||||
let currentVersion = requestVersion
|
|
||||||
const result = commitPreloadedBackgroundRotation({
|
|
||||||
canCommit: () => requestVersion === currentVersion,
|
|
||||||
commit,
|
|
||||||
preload: () => preload.promise,
|
|
||||||
})
|
|
||||||
|
|
||||||
currentVersion += 1
|
|
||||||
preload.resolve(true)
|
|
||||||
|
|
||||||
await expect(result).resolves.toBe(false)
|
|
||||||
expect(commit).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('preloadBackgroundRotationImages', () => {
|
describe('preloadBackgroundRotationImages', () => {
|
||||||
it('does not let an unused optical texture block the visible wallpaper', async () => {
|
it('does not let an unused optical texture block the visible wallpaper', async () => {
|
||||||
const preload = vi.fn(async (url: string) => url === 'display.jpg')
|
const preload = vi.fn(async (url: string) => url === 'display.jpg')
|
||||||
@@ -106,37 +55,34 @@ describe('preloadBackgroundRotationImages', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('preloadBackgroundSequence', () => {
|
describe('findFirstAvailableBackground', () => {
|
||||||
it('preloads remaining wallpapers sequentially in rotation order', async () => {
|
it('skips invalid entries without changing the original sequence', async () => {
|
||||||
const calls: string[] = []
|
const preload = vi.fn(async (url: string) => url === 'two.jpg')
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
preloadBackgroundSequence({
|
findFirstAvailableBackground({
|
||||||
canContinue: () => true,
|
|
||||||
preload: async url => {
|
|
||||||
calls.push(url)
|
|
||||||
return url !== 'two.jpg'
|
|
||||||
},
|
|
||||||
urls: ['one.jpg', 'two.jpg', 'three.jpg'],
|
urls: ['one.jpg', 'two.jpg', 'three.jpg'],
|
||||||
|
canContinue: () => true,
|
||||||
|
preload,
|
||||||
}),
|
}),
|
||||||
).resolves.toEqual([true, false, true])
|
).resolves.toBe(1)
|
||||||
expect(calls).toEqual(['one.jpg', 'two.jpg', 'three.jpg'])
|
expect(preload.mock.calls.map(([url]) => url)).toEqual(['one.jpg', 'two.jpg'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('stops an obsolete queue before starting the next image', async () => {
|
it('drops an obsolete batch before trying another image', async () => {
|
||||||
let active = true
|
const pending = deferred<boolean>()
|
||||||
const calls: string[] = []
|
let current = true
|
||||||
|
const preload = vi.fn(() => pending.promise)
|
||||||
await preloadBackgroundSequence({
|
const result = findFirstAvailableBackground({
|
||||||
canContinue: () => active,
|
|
||||||
preload: async url => {
|
|
||||||
calls.push(url)
|
|
||||||
active = false
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
urls: ['one.jpg', 'two.jpg'],
|
urls: ['one.jpg', 'two.jpg'],
|
||||||
|
canContinue: () => current,
|
||||||
|
preload,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(calls).toEqual(['one.jpg'])
|
current = false
|
||||||
|
pending.resolve(true)
|
||||||
|
|
||||||
|
await expect(result).resolves.toBeNull()
|
||||||
|
expect(preload).toHaveBeenCalledOnce()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,15 +8,6 @@ export function shouldAllowBackgroundRotation(state: AppActivityState, graceActi
|
|||||||
return !reducedMotion && (state === 'active' || graceActive)
|
return !reducedMotion && (state === 'active' || graceActive)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PreloadedBackgroundRotationOptions {
|
|
||||||
/** 提交前重新判断当前生命周期和请求版本是否仍允许切换。 */
|
|
||||||
canCommit: () => boolean
|
|
||||||
/** 将已经完成预加载的壁纸切换为活动背景。 */
|
|
||||||
commit: () => void
|
|
||||||
/** 预加载目标壁纸,并以布尔值表示是否可安全显示。 */
|
|
||||||
preload: () => Promise<boolean>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BackgroundRotationImagePreloadOptions {
|
interface BackgroundRotationImagePreloadOptions {
|
||||||
/** 外层背景实际显示的壁纸地址。 */
|
/** 外层背景实际显示的壁纸地址。 */
|
||||||
displayUrl: string
|
displayUrl: string
|
||||||
@@ -26,25 +17,25 @@ interface BackgroundRotationImagePreloadOptions {
|
|||||||
preload: (url: string) => Promise<boolean>
|
preload: (url: string) => Promise<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BackgroundSequencePreloadOptions {
|
interface FirstAvailableBackgroundOptions {
|
||||||
/** 每张图片完成后重新判断队列是否仍属于当前页面与请求代次。 */
|
/** 保持后端返回顺序的候选壁纸。 */
|
||||||
canContinue: () => boolean
|
|
||||||
/** 按实际轮播顺序排列的待预加载地址。 */
|
|
||||||
urls: string[]
|
urls: string[]
|
||||||
|
/** 当前加载批次仍可提交时返回 true。 */
|
||||||
|
canContinue: () => boolean
|
||||||
/** 执行单张图片预加载并返回可用状态。 */
|
/** 执行单张图片预加载并返回可用状态。 */
|
||||||
preload: (url: string) => Promise<boolean>
|
preload: (url: string) => Promise<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 按来源顺序寻找首张可用壁纸,单项失败或过期批次不会提交可见状态。 */
|
||||||
* 将壁纸预加载与最终提交分离,确保异步加载期间失效的轮换请求不会改变可见背景。
|
export async function findFirstAvailableBackground(options: FirstAvailableBackgroundOptions) {
|
||||||
*/
|
for (let index = 0; index < options.urls.length; index += 1) {
|
||||||
export async function commitPreloadedBackgroundRotation(options: PreloadedBackgroundRotationOptions) {
|
if (!options.canContinue()) return null
|
||||||
const succeeded = await options.preload()
|
const available = await options.preload(options.urls[index])
|
||||||
|
if (!options.canContinue()) return null
|
||||||
|
if (available) return index
|
||||||
|
}
|
||||||
|
|
||||||
if (!succeeded || !options.canCommit()) return false
|
return null
|
||||||
|
|
||||||
options.commit()
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,15 +50,3 @@ export async function preloadBackgroundRotationImages(options: BackgroundRotatio
|
|||||||
|
|
||||||
return results.every(Boolean)
|
return results.every(Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 当前壁纸稳定后串行预加载剩余轮播项,避免并发争抢首屏带宽。 */
|
|
||||||
export async function preloadBackgroundSequence(options: BackgroundSequencePreloadOptions) {
|
|
||||||
const results: boolean[] = []
|
|
||||||
|
|
||||||
for (const url of options.urls) {
|
|
||||||
if (!options.canContinue()) break
|
|
||||||
results.push(await options.preload(url))
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ export interface GlassOpticalRenderProfile {
|
|||||||
springFrequency: number
|
springFrequency: number
|
||||||
/** 活动壁纸进入 GPU 前的最长边限制。 */
|
/** 活动壁纸进入 GPU 前的最长边限制。 */
|
||||||
textureLimit: number
|
textureLimit: number
|
||||||
/** 登录页优先使用可读纹理,跨域外链自动退回程序化高光。 */
|
|
||||||
textureSource: 'auto' | 'procedural' | 'wallpaper'
|
textureSource: 'auto' | 'procedural' | 'wallpaper'
|
||||||
/** 参与液态方向计算的最近输入采样数量。 */
|
/** 参与液态方向计算的最近输入采样数量。 */
|
||||||
trailCount: number
|
trailCount: number
|
||||||
|
|||||||
Reference in New Issue
Block a user