mirror of
https://git.fightbot.fun/hxuanyu/BingPaper.git
synced 2026-03-08 07:29:32 +08:00
Compare commits
2 Commits
5e3defc63d
...
e428f5bddb
| Author | SHA1 | Date | |
|---|---|---|---|
| e428f5bddb | |||
| c32cb8da3f |
@@ -1,18 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fixed inset-0 bg-black z-50 overflow-hidden">
|
<div class="fixed inset-0 bg-black z-50 overflow-hidden">
|
||||||
<!-- 加载状态 -->
|
<!-- 加载状态(动画过渡中不显示) -->
|
||||||
<div v-if="loading" class="absolute inset-0 flex items-center justify-center">
|
<div v-if="loading && !imageTransitioning" class="absolute inset-0 flex items-center justify-center">
|
||||||
<div class="w-16 h-16 border-4 border-white/20 border-t-white rounded-full animate-spin"></div>
|
<div class="w-16 h-16 border-4 border-white/20 border-t-white rounded-full animate-spin"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 主要内容 -->
|
<!-- 主要内容 -->
|
||||||
<div v-else-if="image" class="relative h-full w-full">
|
<div v-else-if="image || imageTransitioning" class="relative h-full w-full">
|
||||||
<!-- 全屏图片 -->
|
<!-- 全屏图片 -->
|
||||||
<div class="absolute inset-0 flex items-center justify-center">
|
<div class="absolute inset-0 flex items-center justify-center">
|
||||||
<img
|
<img
|
||||||
:src="getFullImageUrl()"
|
:src="getFullImageUrl()"
|
||||||
:alt="image.title || 'Bing Image'"
|
:alt="image?.title || 'Bing Image'"
|
||||||
class="max-w-full max-h-full object-contain"
|
class="max-w-full max-h-full object-contain transition-opacity duration-500 ease-in-out"
|
||||||
|
:style="{ opacity: imageOpacity }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -34,14 +35,14 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="text-white/80 text-sm">
|
<div class="text-white/80 text-sm">
|
||||||
{{ formatDate(image.date) }}
|
{{ formatDate(image?.date) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 信息悬浮层(类似 Windows 聚焦) -->
|
<!-- 信息悬浮层(类似 Windows 聚焦) -->
|
||||||
<div
|
<div
|
||||||
v-if="showInfo && !showCalendar"
|
v-if="showInfo && !showCalendar && image"
|
||||||
ref="infoPanel"
|
ref="infoPanel"
|
||||||
class="fixed w-[90%] max-w-md bg-black/40 backdrop-blur-lg rounded-xl p-4 z-20 select-none"
|
class="fixed w-[90%] max-w-md bg-black/40 backdrop-blur-lg rounded-xl p-4 z-20 select-none"
|
||||||
:class="{
|
:class="{
|
||||||
@@ -62,16 +63,16 @@
|
|||||||
></div>
|
></div>
|
||||||
|
|
||||||
<h2 class="text-lg font-bold text-white mb-2 mt-2">
|
<h2 class="text-lg font-bold text-white mb-2 mt-2">
|
||||||
{{ image.title || '未命名' }}
|
{{ image?.title || '未命名' }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p v-if="image.copyright" class="text-white/80 text-xs mb-3 leading-relaxed">
|
<p v-if="image?.copyright" class="text-white/80 text-xs mb-3 leading-relaxed">
|
||||||
{{ image.copyright }}
|
{{ image.copyright }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- 版权详情链接 -->
|
<!-- 版权详情链接 -->
|
||||||
<a
|
<a
|
||||||
v-if="image.copyrightlink"
|
v-if="image?.copyrightlink"
|
||||||
:href="image.copyrightlink"
|
:href="image.copyrightlink"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="inline-flex items-center gap-2 px-3 py-1.5 bg-white/15 hover:bg-white/25 text-white rounded-lg text-xs font-medium transition-all group"
|
class="inline-flex items-center gap-2 px-3 py-1.5 bg-white/15 hover:bg-white/25 text-white rounded-lg text-xs font-medium transition-all group"
|
||||||
@@ -204,6 +205,8 @@ const currentDate = ref(route.params.date as string)
|
|||||||
const showInfo = ref(true)
|
const showInfo = ref(true)
|
||||||
const showCalendar = ref(getInitialCalendarState())
|
const showCalendar = ref(getInitialCalendarState())
|
||||||
const navigating = ref(false)
|
const navigating = ref(false)
|
||||||
|
const imageOpacity = ref(1)
|
||||||
|
const imageTransitioning = ref(false)
|
||||||
|
|
||||||
// 前后日期可用性
|
// 前后日期可用性
|
||||||
const hasPreviousDay = ref(true)
|
const hasPreviousDay = ref(true)
|
||||||
@@ -415,6 +418,63 @@ const getFullImageUrl = () => {
|
|||||||
return bingPaperApi.getImageUrlByDate(currentDate.value, 'UHD', 'jpg')
|
return bingPaperApi.getImageUrlByDate(currentDate.value, 'UHD', 'jpg')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 预加载图片
|
||||||
|
const preloadImage = (url: string): Promise<void> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image()
|
||||||
|
img.onload = () => resolve()
|
||||||
|
img.onerror = () => reject(new Error('Failed to load image'))
|
||||||
|
img.src = url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预加载图片和数据
|
||||||
|
const preloadImageAndData = async (date: string): Promise<void> => {
|
||||||
|
try {
|
||||||
|
// 并行预加载图片和数据
|
||||||
|
const imageUrl = bingPaperApi.getImageUrlByDate(date, 'UHD', 'jpg')
|
||||||
|
await Promise.all([
|
||||||
|
preloadImage(imageUrl),
|
||||||
|
bingPaperApi.getImageMetaByDate(date)
|
||||||
|
])
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to preload image or data:', error)
|
||||||
|
// 即使预加载失败也继续
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换日期并带动画
|
||||||
|
const switchToDate = async (newDate: string) => {
|
||||||
|
if (imageTransitioning.value) return
|
||||||
|
|
||||||
|
imageTransitioning.value = true
|
||||||
|
|
||||||
|
// 1. 淡出当前图片的同时预加载新图片和数据
|
||||||
|
imageOpacity.value = 0
|
||||||
|
const preloadPromise = preloadImageAndData(newDate)
|
||||||
|
|
||||||
|
// 2. 等待淡出动画完成(500ms)
|
||||||
|
await Promise.all([
|
||||||
|
new Promise(resolve => setTimeout(resolve, 500)),
|
||||||
|
preloadPromise
|
||||||
|
])
|
||||||
|
|
||||||
|
// 3. 更新日期(此时图片和数据已经预加载完成)
|
||||||
|
currentDate.value = newDate
|
||||||
|
router.replace(`/image/${newDate}`)
|
||||||
|
|
||||||
|
// 4. 等待一个微任务,确保 DOM 更新
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
|
||||||
|
// 5. 淡入新图片
|
||||||
|
imageOpacity.value = 1
|
||||||
|
|
||||||
|
// 6. 等待淡入完成
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
|
|
||||||
|
imageTransitioning.value = false
|
||||||
|
}
|
||||||
|
|
||||||
// copyrightlink 现在是完整的 URL,无需额外处理
|
// copyrightlink 现在是完整的 URL,无需额外处理
|
||||||
|
|
||||||
// 返回首页
|
// 返回首页
|
||||||
@@ -423,37 +483,31 @@ const goBack = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 前一天
|
// 前一天
|
||||||
const previousDay = () => {
|
const previousDay = async () => {
|
||||||
if (navigating.value || !hasPreviousDay.value) return
|
if (navigating.value || !hasPreviousDay.value || imageTransitioning.value) return
|
||||||
|
|
||||||
navigating.value = true
|
navigating.value = true
|
||||||
const date = new Date(currentDate.value)
|
const date = new Date(currentDate.value)
|
||||||
date.setDate(date.getDate() - 1)
|
date.setDate(date.getDate() - 1)
|
||||||
const newDate = date.toISOString().split('T')[0]
|
const newDate = date.toISOString().split('T')[0]
|
||||||
|
|
||||||
currentDate.value = newDate
|
await switchToDate(newDate)
|
||||||
router.replace(`/image/${newDate}`)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
navigating.value = false
|
||||||
navigating.value = false
|
|
||||||
}, 500)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 后一天
|
// 后一天
|
||||||
const nextDay = () => {
|
const nextDay = async () => {
|
||||||
if (navigating.value || !hasNextDay.value) return
|
if (navigating.value || !hasNextDay.value || imageTransitioning.value) return
|
||||||
|
|
||||||
navigating.value = true
|
navigating.value = true
|
||||||
const date = new Date(currentDate.value)
|
const date = new Date(currentDate.value)
|
||||||
date.setDate(date.getDate() + 1)
|
date.setDate(date.getDate() + 1)
|
||||||
const newDate = date.toISOString().split('T')[0]
|
const newDate = date.toISOString().split('T')[0]
|
||||||
|
|
||||||
currentDate.value = newDate
|
await switchToDate(newDate)
|
||||||
router.replace(`/image/${newDate}`)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
navigating.value = false
|
||||||
navigating.value = false
|
|
||||||
}, 500)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换日历状态(watch会自动保存)
|
// 切换日历状态(watch会自动保存)
|
||||||
|
|||||||
Reference in New Issue
Block a user