跳到主要内容

给Teek配置go壁纸api

最后更新于:

date: 2025-09-25 18:07:27 title: 给Teek配置go壁纸api categories:

  • 博客 tags:
  • Teek permalink: /teek/knl7a

给Teek配置go壁纸api

image-20250925184754837

目录

[[toc]]

[toc]

需求

📌需求:给宇宙最美博客-Teek 接入一个壁纸api

📌具体描述:

目前,宇宙最美博客-Teek的壁纸 已具备 每隔8s随机从壁纸库 显示一张新壁纸功能了,且壁纸有动态放大缩小效果,很美观👏。

此时,我想实现一个 壁纸api 的需求:

我们对用户只需要暴漏一个域名地址,例如imgapi.onedayxyy.cn,然后在浏览器打开这个域名后,就会出现一张壁纸,每刷新一次,就会随机出现一张新壁纸。

那么该如何接入Teek使用呢?

方案1:Teek修改代码,让自己适配这个壁纸api,博客自己 每隔8s就会 强制 请求壁纸api ,让其显示一张新壁纸

方案2:壁纸api 自己实现“每隔8s随机从 壁纸api 显示一张新壁纸”功能,但teek接入后,也需要能正常每隔8s随机从 壁纸api 显示一张新壁纸才行;

这样做的好处就是:

  1. 我们后期只需要 在壁纸api 源源不断地新增漂亮的风景照/好看的妹纸照片。我们无需修改teek的代码,而我们的网站就可以出现新增的漂亮照片;
  2. 其它Teek道友或者类型的博客也可以使用我们的壁纸api;

说明:

1不管怎么实现,越简单越好,能实现就好。
2
3首先,壁纸api自身的2个功能不能变:
4
5例如壁纸api
61. imgapi.onedayxyy.cn在自己浏览器打开,会显示一张图片,刷新一次,会切换到另一张图片
72.间隔数s后,会随机切换到下一张图片
8
9然后在次基础上,不管是改teek的代码来适配壁纸api,还是改壁纸api的代码来适配teek,或者两个都改,由开发者自己选择最优解。

背景

经过Teek群里多次讨论,余温-go大佬 终于把这个功能给开发好了。💖💖💖

余温-go大佬 把这个 go壁纸api 项目开发完成了,那么接下来就出一个手把手文章吧。😜

image-20250925180838124

效果

2025年9月25日测试。

环境

自己在《Teek-one开源库:https://cnb.cool/onedayxyy/vitepress-theme-teek-one-public》的6f82dbb03134b88284d5e15bd8c2dce72a202589 版本上测试成功的。(后续版本已经集成这个代码了。)

Teek@1.4.6-2025.8.31

配置

(1)编辑docs/.vitepress/ConfigHyde/Wallaper.ts文件,将其全部替换为如下内容:

 1// 首页壁纸 - 动态获取本地图片服务器的图片
 2
 3// 壁纸服务配置 - 统一配置服务地址,一处修改全局生效
 4export const WALLPAPER_SERVICE_CONFIG = {
 5  baseUrl: 'https://imgapi.onedayxyy.cn',
 6  // baseUrl: 'https://imgapi.onedayxyy.cn',
 7  apiEndpoint: '/api/images',
 8  get fullUrl() {
 9    return `${this.baseUrl}${this.apiEndpoint}`
10  }
11}
12
13// 备用图片列表(当本地服务不可用时使用)
14const fallbackImages = [
15  "https://img.onedayxyy.cn/images/Teek/TeekBg/1.avif", 
16  // "https://img.onedayxyy.cn/images/Teek/TeekBg/2.avif", 
17  // "https://img.onedayxyy.cn/images/Teek/TeekBg/3.avif", 
18  // "https://img.onedayxyy.cn/images/Teek/TeekBg/4.avif", 
19  // "https://img.onedayxyy.cn/images/Teek/TeekBg/5.avif", 
20  // "https://img.onedayxyy.cn/images/Teek/TeekBg/6.avif", 
21  // "https://img.onedayxyy.cn/images/Teek/TeekBg/7.avif", 
22  // "https://img.onedayxyy.cn/images/Teek/TeekBg/8.avif", 
23  // "https://img.onedayxyy.cn/images/Teek/TeekBg/9.avif", 
24  // "https://img.onedayxyy.cn/images/Teek/TeekBg/10.avif", 
25  // "https://img.onedayxyy.cn/images/Teek/TeekBg/11.avif", 
26  // "https://img.onedayxyy.cn/images/Teek/TeekBg/12.avif", 
27  // "https://img.onedayxyy.cn/images/Teek/TeekBg/13.avif", 
28  // "https://img.onedayxyy.cn/images/Teek/TeekBg/14.avif", 
29  // "https://img.onedayxyy.cn/images/Teek/TeekBg/15.avif", 
30  // "https://img.onedayxyy.cn/images/Teek/TeekBg/16.avif",
31  // "https://img.onedayxyy.cn/images/Teek/TeekBg/17.avif",
32  // "https://img.onedayxyy.cn/images/Teek/TeekBg/18.avif",
33  // "https://img.onedayxyy.cn/images/Teek/TeekBg/19.avif",
34];
35// 动态获取图片列表的函数
36async function fetchDynamicWallpapers(): Promise<string[]> {
37  try {
38    // 使用统一配置的图片服务API
39    const response = await fetch(WALLPAPER_SERVICE_CONFIG.fullUrl, {
40      method: 'GET',
41      headers: {
42        'Accept': 'application/json',
43      },
44    });
45    
46    if (!response.ok) {
47      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
48    }
49    
50    const data = await response.json();
51    const images = data.images || [];
52    
53    // 将相对路径转换为完整的服务器URL
54    const wallpapers = images.map((imagePath: string) => `${WALLPAPER_SERVICE_CONFIG.baseUrl}${imagePath}`);
55    
56    // 如果获取到图片,返回动态图片列表,否则返回备用图片
57    return wallpapers.length > 0 ? wallpapers : wallpapers;
58    
59  } catch (error) {
60    console.warn('无法获取动态壁纸,使用备用图片:', error);
61    return fallbackImages;
62  }
63}
64
65// 创建一个Promise来获取壁纸
66let wallpaperPromise: Promise<string[]> | null = null;
67
68// 获取壁纸的函数
69function getWallpapers(): Promise<string[]> {
70  if (!wallpaperPromise) {
71    wallpaperPromise = fetchDynamicWallpapers();
72  }
73  return wallpaperPromise;
74}
75
76// 导出的Wallpaper数组 - 在服务端渲染时使用备用图片,客户端动态加载
77export const Wallpaper: string[] = fallbackImages;
78
79// 导出动态获取函数供主题使用
80export { getWallpapers, fetchDynamicWallpapers };

(2)创建docs/.vitepress/theme/components/DynamicWallpaperManager.vue文件:

   1<template>
   2  <!-- 这是一个无渲染组件仅用于管理动态壁纸 -->
   3</template>
   4
   5<script setup lang="ts">
   6import { onMounted, onUnmounted, nextTick, watch } from 'vue'
   7import { useRoute } from 'vitepress'
   8import { fetchDynamicWallpapers, WALLPAPER_SERVICE_CONFIG } from '../../ConfigHyde/Wallaper'
   9
  10// 路由检测 - 只在首页启用动态壁纸
  11const route = useRoute()
  12let isHomePage = false
  13
  14// 检查是否为首页
  15function checkIsHomePage(): boolean {
  16  const path = route.path
  17  return path === '/' || path === '/index.html' || path === ''
  18}
  19
  20// 停止所有动态壁纸相关功能
  21function stopWallpaperSystem() {
  22  if (fetchLibraryIntervalId) {
  23    clearInterval(fetchLibraryIntervalId)
  24    fetchLibraryIntervalId = null
  25  }
  26  if (switchImageIntervalId) {
  27    clearInterval(switchImageIntervalId)
  28    switchImageIntervalId = null
  29  }
  30  stopServiceMonitoring()
  31
  32  // 清理呼吸动画类
  33  const backgroundElements = document.querySelectorAll('.wallpaper-breathing')
  34  backgroundElements.forEach(element => {
  35    element.classList.remove('wallpaper-breathing')
  36  })
  37
  38  console.log('🖼️ 动态壁纸系统已停止(非首页)')
  39}
  40
  41// 启动动态壁纸系统(仅首页)
  42function startWallpaperSystem() {
  43  if (!isHomePage) return
  44
  45  console.log('🖼️ 动态壁纸系统启动(首页)')
  46  // 壁纸系统启动逻辑将在 onMounted 中调用
  47}
  48
  49// 定义时间间隔配置
  50const FETCH_LIBRARY_INTERVAL = 60 * 1000 // 60秒请求一次图集服务器获取图库
  51const SWITCH_IMAGE_INTERVAL = 10 * 1000 // 10秒从图库中随机选择一张图片展示
  52const SERVICE_CHECK_INTERVAL = 15 * 1000 // 15秒检测一次服务状态(当使用备用图片时)
  53
  54let fetchLibraryIntervalId: number | null = null // 图库请求定时器
  55let switchImageIntervalId: number | null = null // 图片切换定时器
  56let serviceCheckIntervalId: number | null = null // 服务检测定时器
  57let currentImages: string[] = [] // 当前图库
  58let currentDisplayImage: string = '' // 当前展示的图片
  59let isUsingFallback: boolean = false // 是否正在使用备用图片
  60let lastSuccessfulFetch: number = 0 // 上次成功获取图库的时间
  61
  62// 双图层状态管理
  63let currentActiveLayer: 'A' | 'B' = 'A' // 当前活动图层
  64let isTransitioning: boolean = false // 是否正在切换中
  65let layerHistory: Array<{ layer: 'A' | 'B', image: string, timestamp: number }> = [] // 图层历史记录
  66let pendingSwitchQueue: Array<{ image: string, timestamp: number }> = [] // 待处理的切换队列
  67
  68// 缓存相关
  69const CACHE_KEY = 'dynamic-wallpaper-cache'
  70const LAST_IMAGE_KEY = 'last-wallpaper-image'
  71
  72// 保存图库到缓存
  73function saveImagesToCache(images: string[]) {
  74  try {
  75    // 检查是否在客户端环境
  76    if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
  77      return
  78    }
  79    localStorage.setItem(CACHE_KEY, JSON.stringify(images))
  80  } catch (error) {
  81    console.warn('保存图库缓存失败:', error)
  82  }
  83}
  84
  85// 从缓存加载图库
  86function loadImagesFromCache(): string[] {
  87  try {
  88    // 检查是否在客户端环境
  89    if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
  90      return []
  91    }
  92    const cached = localStorage.getItem(CACHE_KEY)
  93    return cached ? JSON.parse(cached) : []
  94  } catch (error) {
  95    console.warn('加载图库缓存失败:', error)
  96    return []
  97  }
  98}
  99
 100// 保存最后显示的图片
 101function saveLastImage(imageSrc: string) {
 102  try {
 103    // 检查是否在客户端环境
 104    if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
 105      return
 106    }
 107    localStorage.setItem(LAST_IMAGE_KEY, imageSrc)
 108  } catch (error) {
 109    console.warn('保存最后图片失败:', error)
 110  }
 111}
 112
 113// 获取最后显示的图片
 114function getLastImage(): string | null {
 115  try {
 116    // 检查是否在客户端环境
 117    if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
 118      return null
 119    }
 120    return localStorage.getItem(LAST_IMAGE_KEY)
 121  } catch (error) {
 122    console.warn('获取最后图片失败:', error)
 123    return null
 124  }
 125}
 126
 127// 预加载图片以避免加载闪烁,检测图片是否可用
 128function preloadImage(src: string): Promise<boolean> {
 129  return new Promise((resolve) => {
 130    const img = new Image()
 131
 132    // 设置3秒超时
 133    const timeout = setTimeout(() => {
 134      img.onload = null
 135      img.onerror = null
 136      resolve(false)
 137    }, 3000)
 138
 139    img.onload = () => {
 140      clearTimeout(timeout)
 141      resolve(true) // 加载成功
 142    }
 143    img.onerror = () => {
 144      clearTimeout(timeout)
 145      resolve(false) // 加载失败
 146    }
 147    img.src = src
 148  })
 149}
 150
 151// 获取图层状态信息
 152function getLayerStatus(): { currentLayer: 'A' | 'B', isTransitioning: boolean, history: typeof layerHistory } {
 153  return {
 154    currentLayer: currentActiveLayer,
 155    isTransitioning,
 156    history: [...layerHistory] // 返回副本
 157  }
 158}
 159
 160// 记录图层状态到历史
 161function recordLayerState(layer: 'A' | 'B', image: string) {
 162  const now = Date.now()
 163  layerHistory.push({ layer, image, timestamp: now })
 164
 165  // 保持历史记录在合理范围内(最多保留10条)
 166  if (layerHistory.length > 10) {
 167    layerHistory.shift()
 168  }
 169
 170  console.log('📝 图层状态记录:', { layer, image, timestamp: now })
 171}
 172
 173// 清理过期的待处理队列
 174function cleanPendingQueue() {
 175  const now = Date.now()
 176  const maxAge = 5000 // 5秒过期
 177
 178  const beforeLength = pendingSwitchQueue.length
 179  pendingSwitchQueue = pendingSwitchQueue.filter(item => now - item.timestamp < maxAge)
 180
 181  if (beforeLength !== pendingSwitchQueue.length) {
 182    console.log('🧹 清理过期的待处理队列:', beforeLength - pendingSwitchQueue.length, '个项目')
 183  }
 184}
 185
 186// 处理待处理的切换队列
 187async function processPendingQueue() {
 188  if (isTransitioning || pendingSwitchQueue.length === 0) {
 189    return
 190  }
 191
 192  // 清理过期项目
 193  cleanPendingQueue()
 194
 195  // 获取最早的待处理项目
 196  const nextSwitch = pendingSwitchQueue.shift()
 197  if (nextSwitch) {
 198    console.log('📋 处理待切换队列:', nextSwitch.image)
 199    await updateBannerBackgroundWithCSS(nextSwitch.image)
 200  }
 201}
 202
 203// 队列化切换请求
 204function queueSwitch(imageSrc: string) {
 205  const now = Date.now()
 206
 207  // 如果队列中已有相同图片,移除旧的
 208  pendingSwitchQueue = pendingSwitchQueue.filter(item => item.image !== imageSrc)
 209
 210  // 添加新的请求
 211  pendingSwitchQueue.push({ image: imageSrc, timestamp: now })
 212
 213  console.log('📥 切换请求已加入队列:', imageSrc, '队列长度:', pendingSwitchQueue.length)
 214
 215  // 尝试立即处理队列
 216  setTimeout(processPendingQueue, 100)
 217}
 218
 219// 添加过渡动画样式
 220function addTransitionStyles(element: HTMLElement) {
 221  if (!element.style.transition || !element.style.transition.includes('opacity')) {
 222    element.style.transition = element.style.transition 
 223      ? `${element.style.transition}, opacity 0.3s ease-in-out`
 224      : 'opacity 0.3s ease-in-out'
 225  }
 226}
 227
 228// 简化的内容层稳定性函数(CSS变量方案下不再需要复杂的DOM操作)
 229function ensureBannerContentStability() {
 230  // 仅确保banner容器有正确的基本设置
 231  const bannerEl = document.querySelector('.tk-banner') as HTMLElement
 232  if (bannerEl) {
 233    bannerEl.style.overflow = 'hidden' // 隐藏背景溢出,避免滚动条
 234    bannerEl.style.isolation = 'isolate'
 235
 236    // 在生产环境中,立即隐藏可能的默认背景图片防止闪现
 237    const isProduction = import.meta.env.PROD
 238    if (isProduction) {
 239      // 立即隐藏所有可能的背景元素,防止默认图片闪现
 240      const allBgElements = document.querySelectorAll('.tk-banner__bg, .tk-banner-bg, [class*="banner"][class*="bg"], .banner-bg')
 241      allBgElements.forEach(el => {
 242        const element = el as HTMLElement
 243        element.style.opacity = '0'
 244        element.style.visibility = 'hidden'
 245        element.style.zIndex = '-1000'
 246      })
 247    }
 248
 249    // 彻底清理可能冲突的旧背景元素样式
 250    const oldBgElements = bannerEl.querySelectorAll('.tk-banner__bg, .tk-banner-bg, [class*="banner"][class*="bg"]')
 251    oldBgElements.forEach(el => {
 252      const element = el as HTMLElement
 253      // 完全隐藏旧的背景元素避免格子问题
 254      element.style.display = 'none'
 255      element.style.backgroundImage = 'none'
 256      element.style.background = 'none'
 257      element.style.opacity = '0'
 258      element.style.zIndex = '-1000'
 259    })
 260
 261    // 清理banner本身可能的背景样式
 262    bannerEl.style.backgroundImage = 'none'
 263    bannerEl.style.background = 'transparent'
 264  }
 265}
 266
 267// CSS变量方案下,不再需要复杂的保护和调试函数
 268// 保留简单的调试函数用于开发
 269function debugBannerState() {
 270  const bannerEl = document.querySelector('.tk-banner') as HTMLElement
 271  if (bannerEl) {
 272    const bgImage = bannerEl.style.getPropertyValue('--banner-bg-image')
 273    const bgOpacity = bannerEl.style.getPropertyValue('--banner-bg-opacity')
 274    console.log('🔍 Banner状态:', { bgImage, bgOpacity })
 275  }
 276}
 277
 278// 双图层初始化Banner背景
 279function initBannerBackground(imageSrc: string): boolean {
 280  const bannerEl = document.querySelector('.tk-banner') as HTMLElement
 281
 282  if (bannerEl) {
 283    // 立即隐藏可能的默认Banner背景,防止闪现
 284    const oldBgElements = bannerEl.querySelectorAll('.tk-banner__bg, .tk-banner-bg, [class*="banner"][class*="bg"]')
 285    oldBgElements.forEach(el => {
 286      const element = el as HTMLElement
 287      element.style.opacity = '0'
 288      element.style.visibility = 'hidden'
 289    })
 290
 291    // 使用主题背景色避免蓝色闪烁,与blogger-fix.scss保持一致
 292    bannerEl.style.background = 'var(--vp-c-bg)'
 293    bannerEl.style.backgroundColor = 'var(--vp-c-bg)'
 294
 295    // 立即设置图层A并显示,避免透明状态
 296    bannerEl.style.setProperty('--layer-a-bg-image', `url("${imageSrc}")`)
 297    bannerEl.style.setProperty('--layer-a-opacity', '1') // 立即显示,不从0开始
 298    bannerEl.style.setProperty('--layer-b-opacity', '0')
 299
 300    // 立即显示图层A类
 301    bannerEl.classList.remove('has-layer-b', 'dual-layer')
 302    bannerEl.classList.add('has-layer-a')
 303    currentActiveLayer = 'A'
 304
 305    // 预加载图片用于验证
 306    const img = new Image()
 307    img.onload = () => {
 308      console.log('🎯 双图层初始化 - 图片验证成功:', imageSrc)
 309      // 图片加载成功后添加背景加载完成的类,移除预设背景色
 310      setTimeout(() => {
 311        bannerEl.classList.add('background-loaded')
 312      }, 300) // 确保图片已显示后再移除背景色
 313    }
 314    img.onerror = () => {
 315      console.warn('❌ 双图层初始化图片验证失败,保持预设背景:', imageSrc)
 316      // 图片加载失败时保持预设背景色,不添加background-loaded类
 317    }
 318
 319    console.log('🔄 双图层系统 - 立即显示背景并验证图片:', imageSrc)
 320    img.src = imageSrc
 321    return true
 322  }
 323
 324  console.warn('❌ 未找到.tk-banner元素')
 325  return false
 326}
 327
 328// 双图层无缝切换函数 - 解决白屏问题
 329async function updateBannerBackgroundWithCSS(imageSrc: string): Promise<boolean> {
 330  const bannerEl = document.querySelector('.tk-banner') as HTMLElement
 331
 332  if (!bannerEl) {
 333    console.warn('❌ 未找到.tk-banner元素')
 334    return false
 335  }
 336
 337  // 如果正在切换中,将请求加入队列
 338  if (isTransitioning) {
 339    console.log('⏭️ 正在切换中,将请求加入队列:', imageSrc)
 340    queueSwitch(imageSrc)
 341    return false
 342  }
 343
 344  try {
 345    // 预加载图片确保切换时不会有闪烁
 346    const preloadSuccess = await preloadImage(imageSrc)
 347
 348    if (!preloadSuccess) {
 349      console.warn('❌ 双图层切换失败,图片预加载失败:', imageSrc)
 350      return false
 351    }
 352
 353    console.log('🔄 双图层无缝切换开始 - 当前活动图层:', currentActiveLayer)
 354
 355    isTransitioning = true
 356    const oldActiveLayer = currentActiveLayer
 357    const targetLayer = currentActiveLayer === 'A' ? 'B' : 'A'
 358
 359    // 记录切换开始状态
 360    recordLayerState(oldActiveLayer, currentDisplayImage || '当前图片')
 361
 362    // 1. 在非活动图层预加载新图片
 363    if (targetLayer === 'A') {
 364      bannerEl.style.setProperty('--layer-a-bg-image', `url("${imageSrc}")`)
 365      bannerEl.style.setProperty('--layer-a-opacity', '0')
 366      bannerEl.classList.add('has-layer-a', 'dual-layer') // 启用双图层显示
 367    } else {
 368      bannerEl.style.setProperty('--layer-b-bg-image', `url("${imageSrc}")`)
 369      bannerEl.style.setProperty('--layer-b-opacity', '0')
 370      bannerEl.classList.add('has-layer-b', 'dual-layer') // 启用双图层显示
 371    }
 372
 373    // 2. 新图层淡入(此时两个图层都显示,无白屏)
 374    await new Promise<void>(resolve => {
 375      setTimeout(() => {
 376        if (targetLayer === 'A') {
 377          bannerEl.style.setProperty('--layer-a-opacity', '1')
 378        } else {
 379          bannerEl.style.setProperty('--layer-b-opacity', '1')
 380        }
 381        console.log('🔄 新图层开始淡入:', targetLayer, '透明度设为1')
 382        resolve()
 383      }, 100) // 短暂延迟确保CSS生效
 384    })
 385
 386    // 3. 等待新图层完全显示后,隐藏旧图层
 387    await new Promise<void>(resolve => {
 388      // 等待新图层过渡动画完全结束再隐藏旧图层
 389      setTimeout(() => {
 390        console.log('🔄 新图层已完全可见,开始隐藏旧图层:', oldActiveLayer)
 391
 392        // 现在开始隐藏旧图层
 393        if (oldActiveLayer === 'A') {
 394          bannerEl.style.setProperty('--layer-a-opacity', '0')
 395        } else {
 396          bannerEl.style.setProperty('--layer-b-opacity', '0')
 397        }
 398
 399        // 等待旧图层完全隐藏后再清理类
 400        setTimeout(() => {
 401          // 移除旧图层的显示类
 402          if (oldActiveLayer === 'A') {
 403            bannerEl.classList.remove('has-layer-a')
 404          } else {
 405            bannerEl.classList.remove('has-layer-b')
 406          }
 407
 408          // 4. 切换完成,更新状态
 409          currentActiveLayer = targetLayer
 410          bannerEl.classList.remove('dual-layer') // 关闭双图层模式
 411          bannerEl.classList.add('background-loaded') // 确保移除预设背景
 412
 413          // 记录新的图层状态
 414          recordLayerState(targetLayer, imageSrc)
 415
 416          isTransitioning = false
 417          console.log('✅ 双图层无缝切换完成 - 新活动图层:', currentActiveLayer, '图片:', imageSrc)
 418
 419          // 保存当前显示的图片到缓存
 420          saveLastImage(imageSrc)
 421
 422          resolve()
 423        }, 2050) // 缩短等待时间(2s + 50ms 缓冲)
 424      }, 1800) // 缩短新图层等待时间(1.8s)
 425    })
 426
 427    // 处理队列中可能的待处理请求
 428    setTimeout(processPendingQueue, 100)
 429
 430    return true
 431
 432  } catch (error) {
 433    console.error('❌ 双图层切换过程中发生错误:', error)
 434    isTransitioning = false
 435    return false
 436  }
 437}
 438
 439// 确保双图层呼吸动画同步
 440function ensureBreathingAnimationSync() {
 441  const bannerEl = document.querySelector('.tk-banner') as HTMLElement
 442
 443  if (bannerEl) {
 444    // 通过重置animation触发同步
 445    const resetAnimation = () => {
 446      bannerEl.style.setProperty('--animation-state', 'paused')
 447
 448      // 使用requestAnimationFrame确保DOM更新
 449      requestAnimationFrame(() => {
 450        bannerEl.style.setProperty('--animation-state', 'running')
 451        console.log('🔄 双图层呼吸动画已同步')
 452      })
 453    }
 454
 455    // 在双图层模式时重置动画确保同步
 456    if (bannerEl.classList.contains('dual-layer')) {
 457      resetAnimation()
 458    }
 459  }
 460}
 461
 462// 添加呼吸动画到所有Banner背景元素(向后兼容)
 463function addBreathingAnimation() {
 464  console.log('💨 呼吸动画已通过CSS伪元素自动启用')
 465
 466  // 现在呼吸动画主要通过::before和::after伪元素实现
 467  // 这个函数保留用于向后兼容旧的背景元素
 468  const selectors = [
 469    '.tk-banner .tk-banner__bg',
 470    '.tk-banner .tk-banner-bg',
 471    '.tk-banner [class*="banner"][class*="bg"]',
 472    '.banner-bg',
 473    '.tk-banner-bg'
 474  ]
 475
 476  selectors.forEach(selector => {
 477    const elements = document.querySelectorAll(selector)
 478    elements.forEach(element => {
 479      if (!element.classList.contains('wallpaper-breathing')) {
 480        element.classList.add('wallpaper-breathing')
 481        console.log('🌟 添加补充呼吸动画到:', selector)
 482      }
 483    })
 484  })
 485
 486  // 确保伪元素动画同步
 487  ensureBreathingAnimationSync()
 488}
 489
 490// 旧的双图层函数已被CSS变量方案替代
 491// 保留用于向后兼容或作为降级方案
 492function updateBannerBackground(imageSrc: string): boolean {
 493  console.warn('⚠️ 使用了旧的双图层方法,建议使用updateBannerBackgroundWithCSS')
 494  // 降级到CSS变量方案
 495  return updateBannerBackgroundWithCSS(imageSrc)
 496}
 497
 498// 从当前图库中随机选择一张图片展示(带去重逻辑)
 499async function displayRandomImage() {
 500  // 检查图库是否为空
 501  if (!currentImages || currentImages.length === 0) {
 502    console.warn('图库为空,使用备用图片')
 503    currentImages = getFallbackImages()
 504    isUsingFallback = true
 505  }
 506  
 507  let availableImages = currentImages
 508  
 509  // 如果图库中有多张图片,排除当前正在展示的图片
 510  if (currentImages.length > 1 && currentDisplayImage) {
 511    availableImages = currentImages.filter(img => img !== currentDisplayImage)
 512  }
 513  
 514  // 如果过滤后没有图片了,使用全部图片
 515  if (availableImages.length === 0) {
 516    availableImages = currentImages
 517  }
 518  
 519  const randomImg = availableImages[Math.floor(Math.random() * availableImages.length)]
 520  
 521  // 如果选中的图片和当前展示的相同,直接返回(避免不必要的更新)
 522  if (randomImg === currentDisplayImage) return
 523  
 524  try {
 525    // 预加载图片并检测是否成功
 526    const preloadSuccess = await preloadImage(randomImg)
 527    
 528    // 如果图片加载失败且当前使用的是动态图库,切换到备用图片
 529    if (!preloadSuccess && !isUsingFallback) {
 530      console.warn('🔌 动态图片加载失败,服务可能已停止,切换到备用图片')
 531      currentImages = getFallbackImages()
 532      isUsingFallback = true
 533      
 534      // 启动服务监控
 535      startServiceMonitoring()
 536      
 537      // 重新从备用图库选择图片
 538      const fallbackImg = currentImages[Math.floor(Math.random() * currentImages.length)]
 539      const fallbackPreloadSuccess = await preloadImage(fallbackImg)
 540      
 541      if (fallbackPreloadSuccess) {
 542        currentDisplayImage = fallbackImg
 543        updateBannerBackground(fallbackImg)
 544        return
 545      } else {
 546        console.error('备用图片也加载失败')
 547        return
 548      }
 549    } else if (!preloadSuccess) {
 550      // 备用图片也加载失败,跳过此次更新
 551      console.warn('图片加载失败,跳过此次更新')
 552      return
 553    }
 554    
 555    // 更新当前展示图片记录
 556    currentDisplayImage = randomImg
 557
 558    // 使用新的CSS变量方法更新Banner背景
 559    const success = updateBannerBackgroundWithCSS(randomImg)
 560
 561    if (!success) {
 562      // 如果立即更新失败,等待DOM更新后再试
 563      await nextTick()
 564      updateBannerBackgroundWithCSS(randomImg)
 565    }
 566    
 567    // CSS变量方案下,呼吸动画通过伪元素自动应用,无需手动添加
 568    setTimeout(() => {
 569      debugBannerState() // 调试新的CSS变量状态
 570    }, 100)
 571    
 572    // 设置全局变量供其他组件使用
 573    ;(window as any).dynamicWallpapers = currentImages
 574    
 575    // 触发自定义事件
 576    window.dispatchEvent(new CustomEvent('wallpaper-updated', {
 577      detail: { images: currentImages, currentImage: randomImg, layerStatus: getLayerStatus() }
 578    }))
 579
 580    // 开发环境下的状态检查
 581    if (typeof window !== 'undefined' && (window as any).location.hostname === 'localhost') {
 582      setTimeout(() => {
 583        const layerStatus = getLayerStatus()
 584        console.log('🔍 壁纸切换后状态检查:', {
 585          currentImage: randomImg,
 586          layerStatus,
 587          timestamp: new Date().toLocaleTimeString()
 588        })
 589      }, 2000) // 等待切换完全完成后检查
 590    }
 591    
 592    const imageType = isUsingFallback ? '备用' : '动态'
 593    console.log(`🎨 已切换${imageType}壁纸:`, randomImg)
 594    
 595  } catch (error) {
 596    console.warn('切换壁纸失败:', error)
 597  }
 598}
 599
 600
 601// 检测图集服务是否可用(快速检测,用于初始加载)
 602async function checkServiceAvailability(): Promise<boolean> {
 603  try {
 604    const controller = new AbortController()
 605    const timeoutId = setTimeout(() => controller.abort(), 2000) // 2秒快速超时,避免影响页面加载
 606    
 607    const response = await fetch(WALLPAPER_SERVICE_CONFIG.fullUrl, {
 608      method: 'GET',
 609      headers: {
 610        'Accept': 'application/json',
 611      },
 612      signal: controller.signal
 613    })
 614    
 615    clearTimeout(timeoutId)
 616    
 617    if (!response.ok) {
 618      return false
 619    }
 620    
 621    const data = await response.json()
 622    if (!data.images || !Array.isArray(data.images) || data.images.length === 0) {
 623      return false
 624    }
 625    
 626    return true
 627  } catch (error) {
 628    return false
 629  }
 630}
 631
 632// 服务状态监控(仅在使用备用图片时运行)
 633async function monitorServiceStatus() {
 634  if (!isUsingFallback) {
 635    // 如果不是使用备用图片,停止监控
 636    stopServiceMonitoring()
 637    return
 638  }
 639  
 640  console.log('🔍 检查图集服务状态...')
 641  const isServiceAvailable = await checkServiceAvailability()
 642  
 643  if (isServiceAvailable) {
 644    console.log('✅ 图集服务已恢复!切换回动态图库')
 645    
 646    // 停止服务监控
 647    stopServiceMonitoring()
 648    
 649    // 重新获取动态图库
 650    await fetchImageLibrary()
 651    
 652    // 立即切换到动态图片
 653    if (!isUsingFallback && currentImages.length > 0) {
 654      await displayRandomImage()
 655    }
 656  }
 657}
 658
 659// 停止服务监控
 660function stopServiceMonitoring() {
 661  if (serviceCheckIntervalId) {
 662    clearInterval(serviceCheckIntervalId)
 663    serviceCheckIntervalId = null
 664    console.log('📴 停止图集服务监控')
 665  }
 666}
 667
 668// 启动服务监控(仅在切换到备用图片时启动)
 669function startServiceMonitoring() {
 670  if (!serviceCheckIntervalId) {
 671    serviceCheckIntervalId = window.setInterval(monitorServiceStatus, SERVICE_CHECK_INTERVAL)
 672    console.log('👁️ 启动图集服务监控(15秒检测一次)')
 673  }
 674}
 675
 676// 获取备用图片列表
 677function getFallbackImages(): string[] {
 678  // 从 Wallaper.ts 导入的备用图片
 679  const fallbackImages = [
 680    "https://img.xxdevops.cn/blog/wallpaper/bg01.avif", 
 681    "https://img.xxdevops.cn/blog/wallpaper/bg02.avif", 
 682    "https://img.xxdevops.cn/blog/wallpaper/bg03.avif", 
 683    "https://img.xxdevops.cn/blog/wallpaper/bg04.avif", 
 684    "https://img.xxdevops.cn/blog/wallpaper/bg05.avif", 
 685    "https://img.xxdevops.cn/blog/wallpaper/bg06.avif", 
 686    "https://img.xxdevops.cn/blog/wallpaper/bg07.avif", 
 687    "https://img.xxdevops.cn/blog/wallpaper/bg08.avif", 
 688    "https://img.xxdevops.cn/blog/wallpaper/bg09.avif", 
 689    "https://img.xxdevops.cn/blog/wallpaper/bg10.avif", 
 690    "https://img.xxdevops.cn/blog/wallpaper/bg11.avif", 
 691    "https://img.xxdevops.cn/blog/wallpaper/bg12.avif",
 692    "https://img.xxdevops.cn/blog/wallpaper/bg13.avif",
 693    "https://img.xxdevops.cn/blog/wallpaper/bg14.avif",
 694    "https://img.xxdevops.cn/blog/wallpaper/bg15.avif",
 695    "https://img.xxdevops.cn/blog/wallpaper/bg16.avif",
 696    "https://img.xxdevops.cn/blog/wallpaper/bg17.avif",
 697    "https://img.xxdevops.cn/blog/wallpaper/bg18.avif",
 698    "https://img.xxdevops.cn/blog/wallpaper/bg19.avif",
 699    "https://img.xxdevops.cn/blog/wallpaper/bg20.avif"
 700  ]
 701  return fallbackImages
 702}
 703
 704// 页面刷新时从API获取新壁纸
 705async function fetchFreshWallpaperOnRefresh(): Promise<string | null> {
 706  try {
 707    console.log('🔄 页面刷新,从API获取新壁纸...')
 708    const images = await fetchDynamicWallpapers()
 709
 710    // 检查是否获取到有效的动态图片(非备用图片)
 711    const fallbackImages = getFallbackImages()
 712    const isDynamicImages = images.some(img => !fallbackImages.includes(img))
 713
 714    if (isDynamicImages && images.length > 0) {
 715      // 成功获取到动态图库
 716      currentImages = images
 717      lastSuccessfulFetch = Date.now()
 718      isUsingFallback = false
 719
 720      // 保存图库到缓存
 721      saveImagesToCache(images)
 722
 723      // 随机选择一张新壁纸,排除缓存的上一张
 724      const lastImage = getLastImage()
 725      let availableImages = images
 726
 727      // 如果有上次的图片且图库有多张图,排除上次的图片
 728      if (lastImage && images.length > 1) {
 729        availableImages = images.filter(img => img !== lastImage)
 730      }
 731
 732      // 如果过滤后没有图片了,使用全部图片
 733      if (availableImages.length === 0) {
 734        availableImages = images
 735      }
 736
 737      const randomImg = availableImages[Math.floor(Math.random() * availableImages.length)]
 738      console.log(`🎨 刷新获取新壁纸成功: ${randomImg}`)
 739
 740      return randomImg
 741    } else {
 742      throw new Error('未获取到有效的动态图片')
 743    }
 744  } catch (error) {
 745    console.warn('刷新获取新壁纸失败,使用备用图片:', error)
 746
 747    // 使用备用图片
 748    const fallbackImages = getFallbackImages()
 749    currentImages = fallbackImages
 750    isUsingFallback = true
 751
 752    // 保存备用图库到缓存
 753    saveImagesToCache(fallbackImages)
 754
 755    // 启动服务监控
 756    startServiceMonitoring()
 757
 758    // 从备用图片中随机选择
 759    const lastImage = getLastImage()
 760    let availableImages = fallbackImages
 761
 762    if (lastImage && fallbackImages.length > 1) {
 763      availableImages = fallbackImages.filter(img => img !== lastImage)
 764    }
 765
 766    if (availableImages.length === 0) {
 767      availableImages = fallbackImages
 768    }
 769
 770    const randomImg = availableImages[Math.floor(Math.random() * availableImages.length)]
 771    console.log(`🔄 使用备用壁纸: ${randomImg}`)
 772
 773    return randomImg
 774  }
 775}
 776
 777// 从图集服务器获取图库列表
 778async function fetchImageLibrary() {
 779  try {
 780    const images = await fetchDynamicWallpapers()
 781
 782    // 检查是否获取到有效的动态图片(非备用图片)
 783    const fallbackImages = getFallbackImages()
 784    const isDynamicImages = images.some(img => !fallbackImages.includes(img))
 785
 786    if (isDynamicImages && images.length > 0) {
 787      // 成功获取到动态图库
 788      currentImages = images
 789      lastSuccessfulFetch = Date.now()
 790
 791      // 保存图库到缓存
 792      saveImagesToCache(images)
 793
 794      if (isUsingFallback) {
 795        console.log('✅ 图集服务已恢复,切换回动态图库')
 796        isUsingFallback = false
 797        // 停止服务监控
 798        stopServiceMonitoring()
 799      }
 800
 801      console.log(`📚 动态图库已更新: ${images.length} 张图片`)
 802    } else {
 803      // 没有获取到有效的动态图片,使用备用图片
 804      if (!isUsingFallback) {
 805        console.log('⚠️ 图集服务不可用,切换到备用图片')
 806        currentImages = fallbackImages
 807        isUsingFallback = true
 808        // 启动服务监控
 809        startServiceMonitoring()
 810      }
 811    }
 812  } catch (error) {
 813    console.warn('获取图库失败,使用备用图片:', error)
 814
 815    if (!isUsingFallback) {
 816      currentImages = getFallbackImages()
 817      isUsingFallback = true
 818      // 启动服务监控
 819      startServiceMonitoring()
 820    }
 821  }
 822}
 823
 824onMounted(async () => {
 825  // 检查是否为首页
 826  isHomePage = checkIsHomePage()
 827  console.log('🖼️ 动态壁纸管理器启动 - 当前页面:', route.path, '是否首页:', isHomePage)
 828
 829  if (!isHomePage) {
 830    console.log('⏹️ 非首页,动态壁纸系统不启动')
 831    return
 832  }
 833
 834  // 在生产构建时,增加延迟确保DOM完全ready
 835  const isProduction = import.meta.env.PROD
 836  const mountDelay = isProduction ? 100 : 0
 837  
 838  // 使用setTimeout确保在hydration完成后再执行
 839  setTimeout(async () => {
 840    // 确保banner容器正确设置
 841    ensureBannerContentStability()
 842
 843    // 显示主题背景色作为加载状态,避免空白
 844    const bannerEl = document.querySelector('.tk-banner') as HTMLElement
 845    if (bannerEl) {
 846      bannerEl.style.background = 'var(--vp-c-bg)'
 847      bannerEl.style.backgroundColor = 'var(--vp-c-bg)'
 848      console.log('🎨 设置加载背景色,避免空白显示')
 849    }
 850
 851    // 优先从API获取新的壁纸
 852    try {
 853      const freshWallpaper = await fetchFreshWallpaperOnRefresh()
 854      if (freshWallpaper) {
 855        currentDisplayImage = freshWallpaper
 856        // 使用新获取的壁纸立即显示
 857        const success = initBannerBackground(freshWallpaper)
 858        if (success) {
 859          console.log('🎯 页面刷新成功显示新壁纸:', freshWallpaper)
 860        } else {
 861          throw new Error('壁纸显示失败')
 862        }
 863      } else {
 864        throw new Error('未获取到新壁纸')
 865      }
 866    } catch (error) {
 867      console.warn('从API获取新壁纸失败,使用降级方案:', error)
 868
 869      // 降级方案:使用缓存或备用壁纸
 870      const lastImage = getLastImage()
 871      const cachedImages = loadImagesFromCache()
 872
 873      if (lastImage) {
 874        console.log('📦 降级:使用缓存的上次壁纸:', lastImage)
 875        currentDisplayImage = lastImage
 876        initBannerBackground(lastImage)
 877
 878        if (cachedImages.length > 0) {
 879          currentImages = cachedImages
 880        } else {
 881          currentImages = getFallbackImages()
 882        }
 883      } else if (cachedImages.length > 0) {
 884        console.log('📦 降级:使用缓存图库随机壁纸')
 885        currentImages = cachedImages
 886        const randomImg = cachedImages[Math.floor(Math.random() * cachedImages.length)]
 887        currentDisplayImage = randomImg
 888        initBannerBackground(randomImg)
 889      } else {
 890        console.log('📦 降级:使用备用壁纸')
 891        currentImages = getFallbackImages()
 892        const randomImg = currentImages[Math.floor(Math.random() * currentImages.length)]
 893        currentDisplayImage = randomImg
 894        initBannerBackground(randomImg)
 895      }
 896    }
 897  }, mountDelay)
 898
 899  // 页面可见性变化监听 - 优化性能
 900  const handleVisibilityChange = () => {
 901    const bannerEl = document.querySelector('.tk-banner') as HTMLElement
 902
 903    if (bannerEl) {
 904      if (document.hidden) {
 905        // 页面隐藏时暂停伪元素动画
 906        bannerEl.classList.add('paused')
 907      } else {
 908        // 页面可见时恢复伪元素动画
 909        bannerEl.classList.remove('paused')
 910      }
 911    }
 912
 913    // 同时处理其他可能的背景元素
 914    const backgroundElements = document.querySelectorAll('.wallpaper-breathing')
 915    backgroundElements.forEach(element => {
 916      if (document.hidden) {
 917        element.classList.add('paused')
 918      } else {
 919        element.classList.remove('paused')
 920      }
 921    })
 922  }
 923
 924  document.addEventListener('visibilitychange', handleVisibilityChange)
 925  
 926  // 等待一个切换间隔后开始异步检测和切换
 927  setTimeout(async () => {
 928    console.log('🔍 开始检测图集服务状态并更新图库...')
 929
 930    try {
 931      const controller = new AbortController()
 932      const timeoutId = setTimeout(() => controller.abort(), 2000) // 2秒检测
 933
 934      const response = await fetch(WALLPAPER_SERVICE_CONFIG.fullUrl, {
 935        method: 'GET',
 936        headers: { 'Accept': 'application/json' },
 937        signal: controller.signal
 938      })
 939
 940      clearTimeout(timeoutId)
 941
 942      if (response.ok) {
 943        // 服务可用:获取最新图库
 944        console.log('✅ 图集服务可用,更新动态图库')
 945        await fetchImageLibrary()
 946
 947        // 设置定时器:每60秒更新图库
 948        fetchLibraryIntervalId = window.setInterval(fetchImageLibrary, FETCH_LIBRARY_INTERVAL)
 949      } else {
 950        throw new Error('服务响应异常')
 951      }
 952    } catch (error) {
 953      // 服务不可用:确保使用备用图片并启动监控
 954      console.log('❌ 图集服务不可用,确保使用备用壁纸')
 955      if (!isUsingFallback) {
 956        currentImages = getFallbackImages()
 957        isUsingFallback = true
 958        saveImagesToCache(currentImages) // 保存备用图库到缓存
 959      }
 960
 961      // 启动服务监控,等待服务恢复
 962      startServiceMonitoring()
 963    }
 964  }, isProduction ? 1500 : 1000) // 生产环境增加延迟,确保缓存壁纸已显示
 965  
 966  // 设置定时器:每10秒切换图片(确保统一的切换间隔)
 967  switchImageIntervalId = window.setInterval(displayRandomImage, SWITCH_IMAGE_INTERVAL)
 968  console.log(`⏰ 壁纸切换定时器已启动,间隔: ${SWITCH_IMAGE_INTERVAL / 1000}秒`)
 969
 970  // 初始化基本设置(CSS变量方案下大大简化)
 971  setTimeout(() => {
 972    ensureBannerContentStability()
 973    console.log('🌟 CSS变量背景系统初始化完成')
 974  }, 500)
 975
 976  // 监听路由变化
 977  watch(() => route.path, (newPath) => {
 978    const newIsHomePage = checkIsHomePage()
 979    console.log('🧭 路由变化:', newPath, '是否首页:', newIsHomePage)
 980
 981    if (newIsHomePage !== isHomePage) {
 982      isHomePage = newIsHomePage
 983
 984      if (isHomePage) {
 985        console.log('🏠 切换到首页,启动动态壁纸系统')
 986        // 如果切换回首页,重新启动系统(但此时组件应该重新挂载)
 987        location.reload() // 简单重载确保干净状态
 988      } else {
 989        console.log('🚪 离开首页,停止动态壁纸系统')
 990        stopWallpaperSystem()
 991      }
 992    }
 993  })
 994})
 995
 996onUnmounted(() => {
 997  // 清理图库获取定时器
 998  if (fetchLibraryIntervalId) {
 999    clearInterval(fetchLibraryIntervalId)
1000    fetchLibraryIntervalId = null
1001  }
1002  
1003  // 清理图片切换定时器  
1004  if (switchImageIntervalId) {
1005    clearInterval(switchImageIntervalId)
1006    switchImageIntervalId = null
1007  }
1008  
1009  // 清理服务检测定时器
1010  stopServiceMonitoring()
1011
1012  // 清理页面可见性监听器
1013  document.removeEventListener('visibilitychange', () => {})
1014
1015  console.log('🖼️ 动态壁纸管理器已停止')
1016})
1017</script>
1018
1019<style>
1020/* 壁纸呼吸动画效果 - 增强可见性版本 */
1021@keyframes wallpaper-breathing {
1022  0%, 100% {
1023    transform: scale(1) translateZ(0);
1024    filter: brightness(1) contrast(1) saturate(1);
1025  }
1026  50% {
1027    transform: scale(1.025) translateZ(0); /* 增加缩放幅度让效果更明显 */
1028    filter: brightness(1.12) contrast(1.05) saturate(1.05); /* 增强滤镜效果 */
1029  }
1030}
1031
1032/* 呼吸动画类 - 限制为首页专用,避免影响其他页面 */
1033.VPHome .wallpaper-breathing,
1034.VPHome .tk-banner .wallpaper-breathing,
1035.VPHome .tk-banner .tk-banner__bg.wallpaper-breathing,
1036.VPHome .tk-banner .tk-banner-bg.wallpaper-breathing,
1037.VPHome [style*="background-image"].wallpaper-breathing {
1038  animation: wallpaper-breathing 6s ease-in-out infinite !important;
1039  transform-origin: center center !important;
1040  will-change: transform, filter;
1041}
1042
1043/* 壁纸淡入淡出过渡样式 - 确保背景图片层次正确 */
1044.tk-banner .tk-banner__bg,
1045.tk-banner .tk-banner-bg,
1046.tk-banner [class*="banner"][class*="bg"],
1047.banner-bg,
1048.tk-banner-bg,
1049.background-image {
1050  background-blend-mode: normal;
1051  /* 确保多层背景正确显示 */
1052  /* 添加硬件加速以获得更流畅的过渡 */
1053  backface-visibility: hidden;
1054  -webkit-backface-visibility: hidden;
1055  transform: translateZ(0);
1056  /* 预设过渡效果,使用最佳的缓动函数 */
1057  transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1058  /* 确保图片切换时不会有空白背景 */
1059  background-color: transparent;
1060  /* 优化渲染性能 */
1061  will-change: opacity, transform;
1062}
1063
1064/* 确保背景图片容器有足够的层级 */
1065[style*="background-image"] {
1066  position: relative;
1067  overflow: hidden;
1068  /* 添加硬件加速 */
1069  transform: translateZ(0);
1070  backface-visibility: hidden;
1071  -webkit-backface-visibility: hidden;
1072  /* 优化过渡效果 */
1073  transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1);
1074  /* 确保无背景色干扰 */
1075  background-color: transparent;
1076  /* 优化渲染性能 */
1077  will-change: opacity;
1078}
1079
1080/* 双图层无缝切换背景系统 - 限制为首页专用 */
1081.VPHome .tk-banner {
1082  position: relative;
1083  z-index: 0;
1084  overflow: hidden; /* 隐藏背景溢出,避免产生滚动条 */
1085  /* 创建独立的层叠上下文但不影响子元素 */
1086  isolation: isolate;
1087
1088  /* 使用主题背景色避免蓝色闪烁,与blogger-fix.scss保持一致 */
1089  background: var(--vp-c-bg);
1090  background-color: var(--vp-c-bg);
1091
1092  /* 图层A的CSS变量 */
1093  --layer-a-bg-image: none;
1094  --layer-a-opacity: 0;
1095
1096  /* 图层B的CSS变量 */
1097  --layer-b-bg-image: none;
1098  --layer-b-opacity: 0;
1099
1100  /* 通用控制变量 */
1101  --bg-transition-duration: 2s;
1102}
1103
1104/* 生产环境防闪现优化 - 立即隐藏可能的默认背景元素 */
1105.VPHome .tk-banner .tk-banner__bg,
1106.VPHome .tk-banner .tk-banner-bg,
1107.VPHome .tk-banner [class*="banner"][class*="bg"],
1108.VPHome .banner-bg {
1109  opacity: 0 !important;
1110  visibility: hidden !important;
1111  z-index: -1000 !important;
1112  transition: none !important;
1113}
1114
1115/* 图层A - 使用::before伪元素 - 限制为首页专用 */
1116.VPHome .tk-banner::before {
1117  content: '';
1118  position: absolute;
1119  top: 0;
1120  left: 0;
1121  right: 0;
1122  bottom: 0;
1123  background-image: var(--layer-a-bg-image);
1124  background-size: cover;
1125  background-position: center center;
1126  background-repeat: no-repeat;
1127  background-color: transparent;
1128  opacity: var(--layer-a-opacity);
1129  transition: opacity var(--bg-transition-duration) cubic-bezier(0.4, 0, 0.2, 1);
1130  z-index: -2; /* 在图层B下方 */
1131  pointer-events: none;
1132  /* 硬件加速 */
1133  will-change: opacity, transform, filter;
1134  backface-visibility: hidden;
1135  transform: translateZ(0);
1136  /* 呼吸动画 - 图层A,同步时机 */
1137  animation: wallpaper-breathing 8s ease-in-out infinite;
1138  animation-delay: 0s;
1139  transform-origin: center center;
1140}
1141
1142/* 图层B - 使用::after伪元素 - 限制为首页专用 */
1143.VPHome .tk-banner::after {
1144  content: '';
1145  position: absolute;
1146  top: 0;
1147  left: 0;
1148  right: 0;
1149  bottom: 0;
1150  background-image: var(--layer-b-bg-image);
1151  background-size: cover;
1152  background-position: center center;
1153  background-repeat: no-repeat;
1154  background-color: transparent;
1155  opacity: var(--layer-b-opacity);
1156  transition: opacity var(--bg-transition-duration) cubic-bezier(0.4, 0, 0.2, 1);
1157  z-index: -1; /* 在图层A上方 */
1158  pointer-events: none;
1159  /* 硬件加速 */
1160  will-change: opacity, transform, filter;
1161  backface-visibility: hidden;
1162  transform: translateZ(0);
1163  /* 呼吸动画 - 图层B,与图层A完全同步 */
1164  animation: wallpaper-breathing 8s ease-in-out infinite;
1165  animation-delay: 0s;
1166  transform-origin: center center;
1167}
1168
1169/* 图层显示控制 - 限制为首页专用 */
1170/* 默认状态下两个图层都显示但透明 */
1171.VPHome .tk-banner::before,
1172.VPHome .tk-banner::after {
1173  display: block;
1174  opacity: 0;
1175}
1176
1177/* 有背景时显示对应图层 */
1178.VPHome .tk-banner.has-layer-a::before {
1179  display: block;
1180  opacity: var(--layer-a-opacity);
1181}
1182
1183.VPHome .tk-banner.has-layer-b::after {
1184  display: block;
1185  opacity: var(--layer-b-opacity);
1186}
1187
1188/* 图层加载完成后移除预设背景 */
1189.VPHome .tk-banner.has-layer-a.background-loaded,
1190.VPHome .tk-banner.has-layer-b.background-loaded {
1191  background: none !important;
1192  background-color: transparent !important;
1193}
1194
1195/* 双图层同时显示时用于无缝切换 */
1196.VPHome .tk-banner.dual-layer::before,
1197.VPHome .tk-banner.dual-layer::after {
1198  display: block;
1199}
1200
1201/* 页面隐藏时暂停两个图层的动画 */
1202.VPHome .tk-banner.paused::before,
1203.VPHome .tk-banner.paused::after {
1204  animation-play-state: paused;
1205}
1206
1207/* 动画时机优化 - 确保图层可见时播放动画 */
1208.VPHome .tk-banner.has-layer-a::before {
1209  animation-play-state: running;
1210}
1211
1212.VPHome .tk-banner.has-layer-b::after {
1213  animation-play-state: running;
1214}
1215
1216/* 默认情况下也允许动画运行,避免动画被意外暂停 */
1217.VPHome .tk-banner::before,
1218.VPHome .tk-banner::after {
1219  animation-play-state: running;
1220}
1221
1222/* 双图层模式下确保两个动画同步 */
1223.VPHome .tk-banner.dual-layer::before,
1224.VPHome .tk-banner.dual-layer::after {
1225  animation-play-state: running;
1226  /* 确保两个图层的动画开始时间一致 */
1227  animation-delay: 0s;
1228}
1229
1230/* 移动端优化呼吸动画 - 应用到双图层 - 限制为首页专用 */
1231@media (max-width: 768px) {
1232  .VPHome .tk-banner::before,
1233  .VPHome .tk-banner::after {
1234    animation-duration: 12s;
1235    /* 移动端减少性能消耗 */
1236    will-change: opacity;
1237  }
1238
1239  /* 移动端呼吸动画重定义 */
1240  @keyframes wallpaper-breathing {
1241    0%, 100% {
1242      transform: scale(1) translateZ(0);
1243      filter: brightness(1);
1244    }
1245    50% {
1246      transform: scale(1.008) translateZ(0); /* 移动端进一步减少缩放 */
1247      filter: brightness(1.05);
1248    }
1249  }
1250}
1251
1252/* 保留旧背景元素的动画支持,但优先使用伪元素 - 限制为首页专用 */
1253.VPHome .tk-banner .tk-banner__bg,
1254.VPHome .tk-banner .tk-banner-bg,
1255.VPHome .tk-banner [class*="banner"][class*="bg"] {
1256  animation: wallpaper-breathing 8s ease-in-out infinite;
1257  transform-origin: center center;
1258  will-change: transform, filter;
1259}
1260
1261.VPHome .tk-banner .wallpaper-breathing {
1262  animation-duration: 10s;
1263}
1264
1265/* 移动端旧背景元素呼吸动画优化 */
1266@media (max-width: 768px) {
1267  .VPHome .wallpaper-breathing {
1268    animation-duration: 12s; /* 移动端使用更缓慢的呼吸频率 */
1269  }
1270}
1271
1272/* 暂停动画的类(用于页面切换时) - 限制为首页专用 */
1273.VPHome .wallpaper-breathing.paused {
1274  animation-play-state: paused;
1275}
1276
1277/* 临时图层样式(现在主要用于向后兼容) */
1278.wallpaper-temp-layer {
1279  backface-visibility: hidden;
1280  transform: translateZ(-1px);
1281  will-change: opacity;
1282  z-index: -50;
1283  position: absolute;
1284  pointer-events: none;
1285}
1286
1287/* 确保过渡期间不会有闪烁 */
1288.tk-banner .tk-banner__bg,
1289.tk-banner .tk-banner-bg,
1290.tk-banner [class*="banner"][class*="bg"] {
1291  /* 防止在切换时显示空白背景 */
1292  min-height: 100%;
1293  background-attachment: local;
1294}
1295
1296/* 简化的内容层保护 - 基于CSS变量的稳定方案 */
1297.tk-banner-content,
1298.tk-banner .tk-banner-content,
1299.banner-content {
1300  position: relative;
1301  z-index: 10;
1302  /* 确保内容层稳定显示 */
1303  isolation: isolate;
1304  /* 确保内容区域不被隐藏的背景overflow影响 */
1305  overflow: visible;
1306}
1307
1308/* 所有banner内容子元素的基本保护 */
1309.tk-banner-content *,
1310.tk-banner .tk-banner-content * {
1311  position: relative;
1312  z-index: 11;
1313}
1314
1315/* 确保所有banner内部内容都不受背景overflow:hidden影响 */
1316.tk-banner > *:not(::before):not(::after) {
1317  position: relative;
1318  z-index: 5;
1319  /* 确保内容层有自己的显示空间 */
1320  overflow: visible;
1321}
1322
1323/* 特别保护常见的banner内容元素 */
1324.tk-banner .tk-banner-title,
1325.tk-banner .tk-banner-subtitle,
1326.tk-banner .tk-banner-description,
1327.tk-banner .tk-banner-buttons,
1328.tk-banner .banner-content,
1329.tk-banner .banner-text {
1330  position: relative;
1331  z-index: 15;
1332  overflow: visible;
1333}
1334
1335/* 确保旧的背景元素正确定位 */
1336.tk-banner .tk-banner__bg,
1337.tk-banner .tk-banner-bg {
1338  position: absolute;
1339  top: 0;
1340  left: 0;
1341  right: 0;
1342  bottom: 0;
1343  z-index: -1;
1344  pointer-events: none;
1345}
1346
1347/* 确保banner的层级结构正确 - 限制为首页专用 */
1348.VPHome .tk-banner {
1349  /* 确保banner容器有合适的层级上下文 */
1350  z-index: 1;
1351}
1352
1353.VPHome .tk-banner > *:not(.tk-banner__bg):not(.tk-banner-bg):not([class*="banner"][class*="bg"]) {
1354  /* 确保所有非背景的banner子元素都在背景之上 */
1355  position: relative;
1356  z-index: 2;
1357}
1358</style>

(3)修改docs\index.md文件:

添加如下2行内容:

1import DynamicWallpaperManager from "./.vitepress/theme/components/DynamicWallpaperManager.vue"; //导入壁纸api组件
2
3
4<!-- 首页专用动态壁纸组件 -->
5<DynamicWallpaperManager />

image-20250925183551395

image-20250925183527972

(4)创建docs\.vitepress\theme\style\blogger-fix.scss文件:

 1/* 修复博主信息卡片背景图层级问题 */
 2
 3/* 覆盖壁纸加载时的蓝色背景闪烁 */
 4.tk-banner {
 5  /* 只在非首页移除背景,首页保留动态壁纸功能 */
 6  background: transparent !important;
 7  background-color: transparent !important;
 8}
 9
10/* 首页动态壁纸专用样式 - 优先级更高 */
11.VPHome .tk-banner {
12  background: var(--vp-c-bg) !important;
13  background-color: var(--vp-c-bg) !important;
14}
15
16/* 壁纸加载完成后移除预设背景 */
17.VPHome .tk-banner.background-loaded {
18  background: none !important;
19  background-color: transparent !important;
20}
21
22/* 确保背景图在最底层 */
23.tk-my__avatar__circle--bg {
24  position: absolute !important;
25  z-index: 1 !important;
26  top: 0;
27  left: 0;
28  width: 100%;
29  height: 100%;
30  background-size: cover !important;
31  background-position: center !important;
32  background-repeat: no-repeat !important;
33}
34
35/* 确保头像在背景图之上 */
36.tk-my__avatar {
37  position: relative !important;
38  z-index: 2 !important;
39}
40
41/* 确保博主信息文字在背景图之上 */
42.tk-my__blogger {
43  position: relative !important;
44  z-index: 2 !important;
45  color: #fff !important;
46}
47
48/* 确保社交图标在背景图之上 */
49.tk-my__icons {
50  position: relative !important;
51  z-index: 2 !important;
52}
53
54/* 确保整个博主信息卡片有正确的层级结构 */
55.tk-my.is-circle-bg {
56  position: relative !important;
57  overflow: hidden;
58}
59
60/* 优化文字可读性 */
61.tk-my__blogger .name {
62  color: #fff !important;
63  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
64  font-weight: bold;
65}
66
67.tk-my__blogger .slogan {
68  color: rgba(255, 255, 255, 0.9) !important;
69  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
70}

再编辑下docs\.vitepress\theme\style\index.scss文件:(使用下刚才创建的scss文件)

1@use "./blogger-fix.scss" as *; // 修复博主信息卡片背景图层级问题

(5)验证。

运行项目,验证效果。pnpm docs:dev

效果见上文。(完美😜)

总结

以上我们的Go壁纸api 就接入Teek了,此时你只需要有空把好看的妹纸、风景照片存到 壁纸api后台,Teek首页就会立马看到你的好看照片了哦。😜(快去试试吧)

https://zdir.onedayxyy.cn/#/

image-20250925185654812

扩展

关于我的 go壁纸api 项目是如何部署的,请看开源链接《img-server

image-20250925185428289

结束。

最新文章

文档导航