From ccc9839d173dbe80f0a59b2a3d69b31a82b2e031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E4=BA=9A=E6=98=95?= <1549469775@qq.com> Date: Thu, 14 Aug 2025 17:33:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E6=A0=8F=E6=A0=B7=E5=BC=8F=EF=BC=8C=E4=BC=98=E5=8C=96=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=B8=83=E5=B1=80=EF=BC=8C=E5=A2=9E=E5=BC=BA=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/lib/bg-change.js | 383 +++++++++++++++++++++++----------------------- public/styles.css | 4 +- src/views/htmx/navbar.pug | 81 ++++++++++ 3 files changed, 277 insertions(+), 191 deletions(-) diff --git a/public/lib/bg-change.js b/public/lib/bg-change.js index db5de67..168a16a 100644 --- a/public/lib/bg-change.js +++ b/public/lib/bg-change.js @@ -1,134 +1,136 @@ - class BgSwitcher { constructor(images, options = {}) { - this.images = images; + this.images = images // 从localStorage中读取保存的索引 - const savedIndex = localStorage.getItem('bgSwitcherIndex'); + const savedIndex = localStorage.getItem("bgSwitcherIndex") if (savedIndex !== null && !isNaN(savedIndex)) { - this.index = parseInt(savedIndex); + this.index = parseInt(savedIndex) } else { - this.index = 0; + this.index = 0 } - this.container = options.container || document.body; - this.interval = options.interval || 3000; - this.effect = options.effect || BgSwitcher.fadeEffect; - this.timer = null; - this.apiTimer = null; - this.apiUrl = null; - this.apiInterval = 30000; - this.startTime = 0; + this.container = options.container || document.body + this.interval = options.interval || 3000 + this.effect = options.effect || BgSwitcher.fadeEffect + this.timer = null + this.apiTimer = null + this.apiUrl = null + this.apiInterval = 30000 + this.startTime = 0 // 从localStorage中读取保存的剩余时间 - const savedRemainingTime = localStorage.getItem('bgSwitcherRemainingTime'); - this.remainingTime = savedRemainingTime !== null && !isNaN(savedRemainingTime) && savedRemainingTime >= 0 ? parseInt(savedRemainingTime) : this.interval; - this.bgLayer = document.createElement('div'); - this.isInitialLoad = true; + const savedRemainingTime = localStorage.getItem("bgSwitcherRemainingTime") + this.remainingTime = + savedRemainingTime !== null && !isNaN(savedRemainingTime) && savedRemainingTime >= 0 + ? parseInt(savedRemainingTime) + : this.interval + this.bgLayer = document.createElement("div") + this.isInitialLoad = true // 从localStorage中读取API模式状态 - const isApiMode = localStorage.getItem('bgSwitcherIsApiMode') === 'true'; + const isApiMode = localStorage.getItem("bgSwitcherIsApiMode") === "true" if (isApiMode) { - this.apiUrl = localStorage.getItem('bgSwitcherApiUrl') || null; - const savedApiInterval = localStorage.getItem('bgSwitcherApiInterval'); - this.apiInterval = savedApiInterval !== null && !isNaN(savedApiInterval) ? parseInt(savedApiInterval) : 30000; + this.apiUrl = localStorage.getItem("bgSwitcherApiUrl") || null + const savedApiInterval = localStorage.getItem("bgSwitcherApiInterval") + this.apiInterval = savedApiInterval !== null && !isNaN(savedApiInterval) ? parseInt(savedApiInterval) : 30000 } // 监听页面可见性变化 - this.handleVisibilityChange = this.handleVisibilityChange.bind(this); - document.addEventListener('visibilitychange', this.handleVisibilityChange); + this.handleVisibilityChange = this.handleVisibilityChange.bind(this) + document.addEventListener("visibilitychange", this.handleVisibilityChange) // 监听页面卸载事件,确保保存状态 - this.handleBeforeUnload = this.handleBeforeUnload.bind(this); - window.addEventListener('beforeunload', this.handleBeforeUnload); - this.bgLayer.style.position = 'fixed'; - this.bgLayer.style.top = 0; - this.bgLayer.style.left = 0; - this.bgLayer.style.width = '100vw'; - this.bgLayer.style.height = '100vh'; - this.bgLayer.style.zIndex = '-1'; - this.bgLayer.style.transition = 'opacity 1s'; - this.bgLayer.style.opacity = 1; - this.bgLayer.style.backgroundSize = 'cover'; - this.bgLayer.style.backgroundColor = '#000000'; - this.bgLayer.style.backgroundPosition = 'center'; - this.bgLayer.style.filter = 'brightness(0.68)' - this.container.style.backgroundColor = '#000000'; + this.handleBeforeUnload = this.handleBeforeUnload.bind(this) + window.addEventListener("beforeunload", this.handleBeforeUnload) + this.bgLayer.style.position = "fixed" + this.bgLayer.style.top = 0 + this.bgLayer.style.left = 0 + this.bgLayer.style.width = "100vw" + this.bgLayer.style.height = "100vh" + this.bgLayer.style.zIndex = "-1" + this.bgLayer.style.transition = "opacity 1s" + this.bgLayer.style.opacity = 1 + this.bgLayer.style.backgroundSize = "cover" + this.bgLayer.style.backgroundColor = "#000000" + this.bgLayer.style.backgroundPosition = "center" + this.bgLayer.style.filter = "brightness(0.68)" + this.container.style.backgroundColor = "#000000" } setBg(url) { // 切换时先预加载目标图片,加载完成后再切换显示 - const img = new Image(); + const img = new Image() img.onload = () => { if (this.isInitialLoad) { // 初始加载时,先设置背景图再添加到页面 - this.bgLayer.style.backgroundImage = `url(${url})`; - this.container.appendChild(this.bgLayer); - this.isInitialLoad = false; + this.bgLayer.style.backgroundImage = `url(${url})` + this.container.appendChild(this.bgLayer) + this.isInitialLoad = false } else { - this.effect(this.bgLayer, url); + this.effect(this.bgLayer, url) } - if(!this.isApiMode) { - this.scheduleNext(); + if (!this.isApiMode) { + this.scheduleNext() } - }; + } img.onerror = () => { // 加载失败时处理 - console.warn('背景图片加载失败:', url); + console.warn("背景图片加载失败:", url) if (this.isInitialLoad) { // 初始加载失败时,也添加背景层到页面 - this.container.appendChild(this.bgLayer); - this.isInitialLoad = false; + this.container.appendChild(this.bgLayer) + this.isInitialLoad = false } - }; - img.src = url; + } + img.src = url } next() { - const nextIndex = (this.index + 1) % this.images.length; - const nextUrl = this.images[nextIndex]; + const nextIndex = (this.index + 1) % this.images.length + const nextUrl = this.images[nextIndex] // 切换前先预加载 - const img = new Image(); + const img = new Image() img.onload = () => { - this.index = nextIndex; + this.index = nextIndex // 保存索引到localStorage - localStorage.setItem('bgSwitcherIndex', this.index); - this.effect(this.bgLayer, nextUrl); - this.scheduleNext(); - }; + localStorage.setItem("bgSwitcherIndex", this.index) + this.effect(this.bgLayer, nextUrl) + this.scheduleNext() + } img.onerror = () => { // 加载失败时跳过 - console.warn('背景图片加载失败:', nextUrl); - this.scheduleNext(); - }; - img.src = nextUrl; + console.warn("背景图片加载失败:", nextUrl) + this.scheduleNext() + } + img.src = nextUrl } prev() { - const prevIndex = (this.index - 1 + this.images.length) % this.images.length; - const prevUrl = this.images[prevIndex]; + const prevIndex = (this.index - 1 + this.images.length) % this.images.length + const prevUrl = this.images[prevIndex] // 切换前先预加载 - const img = new Image(); + const img = new Image() img.onload = () => { - this.index = prevIndex; + this.index = prevIndex // 保存索引到localStorage - localStorage.setItem('bgSwitcherIndex', this.index); - this.effect(this.bgLayer, prevUrl); - this.scheduleNext(); - }; + localStorage.setItem("bgSwitcherIndex", this.index) + this.effect(this.bgLayer, prevUrl) + this.scheduleNext() + } img.onerror = () => { // 加载失败时跳过 - console.warn('背景图片加载失败:', prevUrl); - this.scheduleNext(); - }; - img.src = prevUrl; + console.warn("背景图片加载失败:", prevUrl) + this.scheduleNext() + } + img.src = prevUrl } start() { - if (this.timer || this.apiTimer) return; + if (this.timer || this.apiTimer) return // 如果处于API模式,启动API请求 if (this.apiUrl) { - this.fetchRandomWallpaper(); + this.fetchRandomWallpaper() } else { // 否则使用默认轮播 - this.setBg(this.images[this.index]); + this.setBg(this.images[this.index]) } } @@ -137,21 +139,21 @@ class BgSwitcher { */ scheduleNext() { if (this.timer) { - clearTimeout(this.timer); + clearTimeout(this.timer) } // 记录开始时间 - this.startTime = Date.now(); + this.startTime = Date.now() // 使用剩余时间或默认间隔 - const timeToWait = this.remainingTime > 0 ? this.remainingTime : this.interval; + const timeToWait = this.remainingTime > 0 ? this.remainingTime : this.interval this.timer = setTimeout(() => { - this.remainingTime = this.interval; // 重置剩余时间 + this.remainingTime = this.interval // 重置剩余时间 // 保存剩余时间到localStorage - localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime); - this.next(); - }, timeToWait); + localStorage.setItem("bgSwitcherRemainingTime", this.remainingTime) + this.next() + }, timeToWait) } /** @@ -161,30 +163,30 @@ class BgSwitcher { if (document.hidden) { // 页面不可见时,暂停计时器并计算剩余时间 if (this.timer) { - const elapsedTime = Date.now() - this.startTime; - this.remainingTime = Math.max(0, this.remainingTime - elapsedTime); + const elapsedTime = Date.now() - this.startTime + this.remainingTime = Math.max(0, this.remainingTime - elapsedTime) // 保存剩余时间到localStorage - localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime); - clearTimeout(this.timer); - this.timer = null; + localStorage.setItem("bgSwitcherRemainingTime", this.remainingTime) + clearTimeout(this.timer) + this.timer = null } // 暂停API计时器 if (this.apiTimer) { - const elapsedTime = Date.now() - this.startTime; - this.remainingTime = Math.max(0, this.remainingTime - elapsedTime); + const elapsedTime = Date.now() - this.startTime + this.remainingTime = Math.max(0, this.remainingTime - elapsedTime) // 保存剩余时间到localStorage - localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime); - clearTimeout(this.apiTimer); - this.apiTimer = null; + localStorage.setItem("bgSwitcherRemainingTime", this.remainingTime) + clearTimeout(this.apiTimer) + this.apiTimer = null } } else { // 页面可见时,恢复计时器 if (!this.timer && !this.apiTimer && !this.apiUrl) { // 如果没有活跃的计时器,使用默认的轮播 - this.scheduleNext(); + this.scheduleNext() } else if (this.apiTimer === null && this.apiUrl) { // 如果处于API模式但计时器未运行,恢复API请求 - this.scheduleNextApiRequest(); + this.scheduleNextApiRequest() } } } @@ -194,32 +196,32 @@ class BgSwitcher { */ handleBeforeUnload() { // 保存当前索引 - localStorage.setItem('bgSwitcherIndex', this.index); + localStorage.setItem("bgSwitcherIndex", this.index) // 保存API模式状态 - localStorage.setItem('bgSwitcherIsApiMode', !!this.apiUrl); + localStorage.setItem("bgSwitcherIsApiMode", !!this.apiUrl) if (this.apiUrl) { - localStorage.setItem('bgSwitcherApiUrl', this.apiUrl); - localStorage.setItem('bgSwitcherApiInterval', this.apiInterval); + localStorage.setItem("bgSwitcherApiUrl", this.apiUrl) + localStorage.setItem("bgSwitcherApiInterval", this.apiInterval) } // 如果计时器在运行,计算并保存剩余时间 if (this.timer || this.apiTimer) { - const elapsedTime = Date.now() - this.startTime; - this.remainingTime = Math.max(0, this.remainingTime - elapsedTime); - localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime); + const elapsedTime = Date.now() - this.startTime + this.remainingTime = Math.max(0, this.remainingTime - elapsedTime) + localStorage.setItem("bgSwitcherRemainingTime", this.remainingTime) } } stop() { if (this.timer) { - clearTimeout(this.timer); - this.timer = null; + clearTimeout(this.timer) + this.timer = null } // 重置剩余时间 - this.remainingTime = this.interval; + this.remainingTime = this.interval // 保存剩余时间到localStorage - localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime); + localStorage.setItem("bgSwitcherRemainingTime", this.remainingTime) } /** @@ -228,15 +230,15 @@ class BgSwitcher { * @param {number} interval - 请求间隔时间(毫秒) */ startRandomApiSwitch(apiUrl, interval = 30000) { - this.stop(); // 停止当前的轮播 - this.apiInterval = interval; - this.apiUrl = apiUrl; + this.stop() // 停止当前的轮播 + this.apiInterval = interval + this.apiUrl = apiUrl // 创建专用的API计时器 - this.apiTimer = null; + this.apiTimer = null // 立即请求一次 - this.fetchRandomWallpaper(); + this.fetchRandomWallpaper() } /** @@ -244,57 +246,56 @@ class BgSwitcher { */ fetchRandomWallpaper() { // 记录开始时间,用于计算剩余时间 - this.startTime = Date.now(); - this.remainingTime = this.apiInterval; + this.startTime = Date.now() + this.remainingTime = this.apiInterval fetch(this.apiUrl) .then(response => { - console.log(response); - + console.log(response) + if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + throw new Error(`HTTP error! status: ${response.status}`) } - return response.json(); + return response.json() }) .then(data => { // 假设API返回的数据格式为 { wallpaperUrl: '图片地址' } - const wallpaperUrl = data.wallpaperUrl || data.url || data.image; + const wallpaperUrl = data.wallpaperUrl || data.url || data.image if (wallpaperUrl) { // 预加载图片 - const img = new Image(); + const img = new Image() img.onload = () => { if (this.isInitialLoad) { // 初始加载时,先设置背景图再添加到页面 - this.container.appendChild(this.bgLayer); - this.isInitialLoad = false; + this.container.appendChild(this.bgLayer) + this.isInitialLoad = false } // 保存当前索引(使用-1标记这是API获取的图片) - this.index = -1; - localStorage.setItem('bgSwitcherIndex', -1); - this.effect(this.bgLayer, wallpaperUrl); - this.scheduleNextApiRequest(); - }; + this.index = -1 + localStorage.setItem("bgSwitcherIndex", -1) + this.effect(this.bgLayer, wallpaperUrl) + this.scheduleNextApiRequest() + } img.onerror = () => { - console.warn('API返回的壁纸加载失败:', wallpaperUrl); - this.scheduleNextApiRequest(); - }; - img.src = wallpaperUrl; - + console.warn("API返回的壁纸加载失败:", wallpaperUrl) + this.scheduleNextApiRequest() + } + img.src = wallpaperUrl } else { - console.warn('背景图片加载失败:', url); + console.warn("背景图片加载失败:", url) if (this.isInitialLoad) { - console.warn('API返回的数据格式不正确,未找到壁纸地址'); + console.warn("API返回的数据格式不正确,未找到壁纸地址") // 初始加载失败时,也添加背景层到页面 - this.container.appendChild(this.bgLayer); - this.isInitialLoad = false; + this.container.appendChild(this.bgLayer) + this.isInitialLoad = false } - this.scheduleNextApiRequest(); + this.scheduleNextApiRequest() } }) .catch(error => { - console.error('获取随机壁纸失败:', error); - this.scheduleNextApiRequest(); - }); + console.error("获取随机壁纸失败:", error) + this.scheduleNextApiRequest() + }) } /** @@ -302,17 +303,17 @@ class BgSwitcher { */ scheduleNextApiRequest() { if (this.apiTimer) { - clearTimeout(this.apiTimer); + clearTimeout(this.apiTimer) } // 使用剩余时间或默认间隔 - const timeToWait = this.remainingTime > 0 ? this.remainingTime : this.apiInterval; + const timeToWait = this.remainingTime > 0 ? this.remainingTime : this.apiInterval this.apiTimer = setTimeout(() => { - this.remainingTime = this.apiInterval; // 重置剩余时间 - localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime); - this.fetchRandomWallpaper(); - }, timeToWait); + this.remainingTime = this.apiInterval // 重置剩余时间 + localStorage.setItem("bgSwitcherRemainingTime", this.remainingTime) + this.fetchRandomWallpaper() + }, timeToWait) } /** @@ -320,22 +321,22 @@ class BgSwitcher { */ stopRandomApiSwitch() { if (this.apiTimer) { - clearTimeout(this.apiTimer); - this.apiTimer = null; + clearTimeout(this.apiTimer) + this.apiTimer = null } this.apiUrl = null // 重置剩余时间 - this.remainingTime = this.interval; - localStorage.setItem('bgSwitcherRemainingTime', this.remainingTime); + this.remainingTime = this.interval + localStorage.setItem("bgSwitcherRemainingTime", this.remainingTime) } static fadeEffect(layer, url) { - layer.style.transition = 'opacity 1s'; - layer.style.opacity = 0; + layer.style.transition = "opacity 1s" + layer.style.opacity = 0 setTimeout(() => { - layer.style.backgroundImage = `url(${url})`; - layer.style.opacity = 1; - }, 500); + layer.style.backgroundImage = `url(${url})` + layer.style.opacity = 1 + }, 500) } } @@ -351,67 +352,71 @@ class BgSwitcher { // 2. 随机API壁纸示例 // 创建一个新的BgSwitcher实例用于API模式 -const apiBgSwitcher = new BgSwitcher([], { interval: 5000 }); // API模式不需要本地图片列表 +let apiBgSwitcher = new BgSwitcher([], { interval: 5000 }) // API模式不需要本地图片列表 // 模拟API函数,实际使用时替换为真实API地址 function createMockWallpaperApi() { // 模拟壁纸地址库 - const mockWallpapers = [ - '/static/bg2.webp', - '/static/bg.jpg', - ]; + const mockWallpapers = ["/static/bg2.webp", "/static/bg.jpg"] // 创建一个简单的服务器端点 if (window.mockWallpaperServer) { - clearInterval(window.mockWallpaperServer); + clearInterval(window.mockWallpaperServer) } // 模拟API响应 window.fetchRandomWallpaper = function () { - return new Promise((resolve) => { + return new Promise(resolve => { setTimeout(() => { - const randomIndex = Math.floor(Math.random() * mockWallpapers.length); + const randomIndex = Math.floor(Math.random() * mockWallpapers.length) resolve({ ok: true, json() { return { - wallpaperUrl: mockWallpapers[randomIndex] + wallpaperUrl: mockWallpapers[randomIndex], } - } - }); - }, 500); - }); - }; + }, + }) + }, 500) + }) + } // 替换原生fetch以模拟API调用 - window.originalFetch = window.fetch; + window.originalFetch = window.fetch window.fetch = function (url) { - if (url === '/api/random-wallpaper') { - return window.fetchRandomWallpaper(); + if (url === "/api/random-wallpaper") { + return window.fetchRandomWallpaper() } - return window.originalFetch(url); - }; + return window.originalFetch(url) + } - console.log('模拟壁纸API已启动'); + console.log("模拟壁纸API已启动") } // 初始化模拟API -createMockWallpaperApi(); +createMockWallpaperApi() // 启动API模式的随机壁纸切换(每10秒请求一次) -apiBgSwitcher.startRandomApiSwitch('/api/random-wallpaper', 10000); - -fetch("https://pic.xieyaxin.top/random.php") - .then(response => { - if(response.body instanceof ReadableStream) { - return response.blob() - } - return response.json() - }) - .then(data => { - console.log(URL.createObjectURL(data)); +apiBgSwitcher.startRandomApiSwitch("/api/random-wallpaper", 10000) - }) +window.addEventListener("pageshow", function (event) { + if (event.persisted) { + apiBgSwitcher = new BgSwitcher([], { interval: 5000 }) + apiBgSwitcher.startRandomApiSwitch("/api/random-wallpaper", 10000) + } +}) + +// fetch("https://pic.xieyaxin.top/random.php") +// .then(response => { +// if(response.body instanceof ReadableStream) { +// return response.blob() +// } +// return response.json() +// }) +// .then(data => { +// console.log(URL.createObjectURL(data)); + +// }) // 要停止API模式,使用 // apiBgSwitcher.stopRandomApiSwitch(); diff --git a/public/styles.css b/public/styles.css index d59f079..b53c58e 100644 --- a/public/styles.css +++ b/public/styles.css @@ -30,12 +30,12 @@ body::after { z-index: -2; } */ -.navbar { +/* .navbar { height: 49px; display: flex; align-items: center; box-shadow: 1px 1px 3px #e4e4e4; -} +} */ .title { font-size: 1.5em; diff --git a/src/views/htmx/navbar.pug b/src/views/htmx/navbar.pug index e69de29..2dbb894 100644 --- a/src/views/htmx/navbar.pug +++ b/src/views/htmx/navbar.pug @@ -0,0 +1,81 @@ +style. + .navbar { + height: 60px; + border-radius: 12px; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(12px); + color: #fff; + &::after { + display: table; + clear: both; + content: ''; + } + } + .navbar .site { + float: left; + height: 100%; + display: flex; + align-items: center; + padding: 0 20px; + cursor: pointer; + font-size: 20px; + &:hover { + background: rgba(255, 255, 255, 0.1); + } + } + .menu { + height: 100%; + margin-left: 20px; + .menu-item { + height: 100%; + display: flex; + align-items: center; + padding: 0 10px; + cursor: pointer; + &+.menu-item { + margin-left: 5px; + } + &:hover { + background: rgba(255, 255, 255, 0.1); + } + } + } + .menu.left { + float: left; + .menu-item { + float: left; + } + } + .right.menu { + float: right; + .menu-item { + padding: 0 20px; + float: right; + } + } +script. + window.addEventListener('pageshow', function(event) { + // event.persisted 为 true 表示页面从缓存中恢复 + if (event.persisted) { + // 执行需要更新的操作,例如: + console.log('页面从缓存加载,需要更新数据'); + + // 1. 刷新页面(简单直接的方式) + //- window.location.reload(); + + // 2. 重新请求数据(更优雅的方式) + //- fetchData(); // 假设这是你的数据请求函数 + + // 3. 更新页面状态 + //- updatePageState(); // 假设这是你的状态更新函数 + } + }); + +.navbar + .site #{$site.site_title} + .left.menu + a.menu-item(href="/about") 明月照佳人 + a.menu-item(href="/about") 岁月催人老 + .right.menu + a.menu-item(href="/login") 登录 + a.menu-item(href="/register") 注册 \ No newline at end of file