mirror of
https://github.com/handsomezhuzhu/handsomezhuzhu.github.io.git
synced 2026-04-18 22:32:54 +00:00
chore: update @sugarat/theme to version 0.5.17 and add pnpm as a dependency
This commit is contained in:
@@ -1,152 +1,152 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useData, useRoute } from 'vitepress'
|
||||
|
||||
const { isDark, frontmatter } = useData()
|
||||
const route = useRoute()
|
||||
|
||||
// 图片配置
|
||||
const darkImages = [
|
||||
// '/bg.webp',
|
||||
'/bg2.webp',
|
||||
'/bg3.jpeg'
|
||||
]
|
||||
|
||||
const lightImages = [
|
||||
'/bgw.webp',
|
||||
'/bgw2.jpeg'
|
||||
]
|
||||
|
||||
const currentImages = computed(() => isDark.value ? darkImages : lightImages)
|
||||
const currentIndex = ref(0)
|
||||
const nextIndex = ref(1)
|
||||
|
||||
// 控制显示的图片
|
||||
const activeImage = computed(() => currentImages.value[currentIndex.value])
|
||||
// const nextImage = computed(() => currentImages.value[nextIndex.value])
|
||||
|
||||
// 简单的淡入淡出逻辑:
|
||||
// 我们使用 <transition> 包裹一个 div 作为背景
|
||||
// 也可以使用两个 div 叠加,一个 fadeOut 一个 fadeIn
|
||||
// 这里使用 Vue Transition Group 或 Key 切换
|
||||
|
||||
// --- 配置区域 ---
|
||||
const INTERVAL_TIME = 15000 // 轮换间隔:5000ms = 5秒
|
||||
// ----------------
|
||||
|
||||
let timer: any = null
|
||||
|
||||
const startRotation = () => {
|
||||
stopRotation()
|
||||
timer = setInterval(() => {
|
||||
currentIndex.value = (currentIndex.value + 1) % currentImages.value.length
|
||||
}, INTERVAL_TIME)
|
||||
}
|
||||
|
||||
const stopRotation = () => {
|
||||
if (timer) clearInterval(timer)
|
||||
}
|
||||
|
||||
// 监听模式变化,重置索引,防止索引越界
|
||||
watch(isDark, () => {
|
||||
// 切换模式时,重置 index,确保展示对应模式的第一张图
|
||||
currentIndex.value = 0
|
||||
})
|
||||
|
||||
|
||||
const preloadImages = (images: string[]) => {
|
||||
images.forEach(src => {
|
||||
const img = new Image()
|
||||
img.src = src
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 预加载所有图片
|
||||
preloadImages([...darkImages, ...lightImages])
|
||||
startRotation()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopRotation()
|
||||
})
|
||||
|
||||
// 只在首页显示
|
||||
const show = computed(() => frontmatter.value.layout === 'home')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div v-show="show" class="bg-slider-container">
|
||||
<transition name="bg-fade">
|
||||
<div
|
||||
:key="activeImage"
|
||||
class="bg-slide-item"
|
||||
:style="{ backgroundImage: `url(${activeImage})` }"
|
||||
></div>
|
||||
</transition>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.bg-slider-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: -10; /* 放在最底层 */
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-slide-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
/* 保持与原主题一致的暗度调整,如果需要的话。
|
||||
原主题是在 .VPHome 上盖了一层渐变,所以这里只需要纯图片。
|
||||
*/
|
||||
}
|
||||
|
||||
/* Vue Transition 动画 */
|
||||
.bg-fade-enter-active {
|
||||
transition: opacity 1.5s ease;
|
||||
z-index: 2; /* 新图片在最上面 */
|
||||
}
|
||||
|
||||
.bg-fade-leave-active {
|
||||
/* 旧图片保持显示,直到被新图片覆盖 */
|
||||
transition: opacity 1.5s ease;
|
||||
z-index: 1; /* 旧图片在中间 */
|
||||
}
|
||||
|
||||
.bg-fade-enter-from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.bg-fade-enter-to {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bg-fade-leave-from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bg-fade-leave-to {
|
||||
/* 旧图片保持不透明(或者可以设为 0 但在 enter 之后)
|
||||
这里设为 0,因为如果 enter 是 opacity 1,它覆盖在上面。
|
||||
如果设为 1,可能会在 transition 结束后突然消失,如果新图片有透明度的话。
|
||||
但我们的图片是背景图,理论上是不透明的。
|
||||
为了保险,我们让旧图片淡出,但新图片覆盖在上面。
|
||||
如果新图片淡入 (0->1) 同时旧图片淡出 (1->0),中间时刻 (0.5+0.5) 可能会透。
|
||||
所以策略是:旧图片 保持 1 (或极慢淡出),新图片 淡入。
|
||||
*/
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useData, useRoute } from 'vitepress'
|
||||
|
||||
const { isDark, frontmatter } = useData()
|
||||
const route = useRoute()
|
||||
|
||||
// 图片配置
|
||||
const darkImages = [
|
||||
// '/bg.webp',
|
||||
'/bg2.webp',
|
||||
'/bg3.jpeg'
|
||||
]
|
||||
|
||||
const lightImages = [
|
||||
'/bgw.webp',
|
||||
'/bgw2.jpeg'
|
||||
]
|
||||
|
||||
const currentImages = computed(() => isDark.value ? darkImages : lightImages)
|
||||
const currentIndex = ref(0)
|
||||
const nextIndex = ref(1)
|
||||
|
||||
// 控制显示的图片
|
||||
const activeImage = computed(() => currentImages.value[currentIndex.value])
|
||||
// const nextImage = computed(() => currentImages.value[nextIndex.value])
|
||||
|
||||
// 简单的淡入淡出逻辑:
|
||||
// 我们使用 <transition> 包裹一个 div 作为背景
|
||||
// 也可以使用两个 div 叠加,一个 fadeOut 一个 fadeIn
|
||||
// 这里使用 Vue Transition Group 或 Key 切换
|
||||
|
||||
// --- 配置区域 ---
|
||||
const INTERVAL_TIME = 15000 // 轮换间隔:5000ms = 5秒
|
||||
// ----------------
|
||||
|
||||
let timer: any = null
|
||||
|
||||
const startRotation = () => {
|
||||
stopRotation()
|
||||
timer = setInterval(() => {
|
||||
currentIndex.value = (currentIndex.value + 1) % currentImages.value.length
|
||||
}, INTERVAL_TIME)
|
||||
}
|
||||
|
||||
const stopRotation = () => {
|
||||
if (timer) clearInterval(timer)
|
||||
}
|
||||
|
||||
// 监听模式变化,重置索引,防止索引越界
|
||||
watch(isDark, () => {
|
||||
// 切换模式时,重置 index,确保展示对应模式的第一张图
|
||||
currentIndex.value = 0
|
||||
})
|
||||
|
||||
|
||||
const preloadImages = (images: string[]) => {
|
||||
images.forEach(src => {
|
||||
const img = new Image()
|
||||
img.src = src
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 预加载所有图片
|
||||
preloadImages([...darkImages, ...lightImages])
|
||||
startRotation()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopRotation()
|
||||
})
|
||||
|
||||
// 只在首页显示
|
||||
const show = computed(() => frontmatter.value.layout === 'home')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div v-show="show" class="bg-slider-container">
|
||||
<transition name="bg-fade">
|
||||
<div
|
||||
:key="activeImage"
|
||||
class="bg-slide-item"
|
||||
:style="{ backgroundImage: `url(${activeImage})` }"
|
||||
></div>
|
||||
</transition>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.bg-slider-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: -10; /* 放在最底层 */
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-slide-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
/* 保持与原主题一致的暗度调整,如果需要的话。
|
||||
原主题是在 .VPHome 上盖了一层渐变,所以这里只需要纯图片。
|
||||
*/
|
||||
}
|
||||
|
||||
/* Vue Transition 动画 */
|
||||
.bg-fade-enter-active {
|
||||
transition: opacity 1.5s ease;
|
||||
z-index: 2; /* 新图片在最上面 */
|
||||
}
|
||||
|
||||
.bg-fade-leave-active {
|
||||
/* 旧图片保持显示,直到被新图片覆盖 */
|
||||
transition: opacity 1.5s ease;
|
||||
z-index: 1; /* 旧图片在中间 */
|
||||
}
|
||||
|
||||
.bg-fade-enter-from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.bg-fade-enter-to {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bg-fade-leave-from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bg-fade-leave-to {
|
||||
/* 旧图片保持不透明(或者可以设为 0 但在 enter 之后)
|
||||
这里设为 0,因为如果 enter 是 opacity 1,它覆盖在上面。
|
||||
如果设为 1,可能会在 transition 结束后突然消失,如果新图片有透明度的话。
|
||||
但我们的图片是背景图,理论上是不透明的。
|
||||
为了保险,我们让旧图片淡出,但新图片覆盖在上面。
|
||||
如果新图片淡入 (0->1) 同时旧图片淡出 (1->0),中间时刻 (0.5+0.5) 可能会透。
|
||||
所以策略是:旧图片 保持 1 (或极慢淡出),新图片 淡入。
|
||||
*/
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,156 +1,156 @@
|
||||
<template>
|
||||
<div class="music-player-wrapper" v-if="show">
|
||||
<div class="music-control">
|
||||
<button class="toggle-btn" @click="togglePlayer" :title="isOpen ? '收起播放器' : '展开播放器'">
|
||||
<span v-if="isOpen">🎵</span>
|
||||
<span v-else>🎵</span>
|
||||
</button>
|
||||
</div>
|
||||
<transition name="slide">
|
||||
<div class="music-player-container" v-show="isOpen">
|
||||
<vue3-aplayer
|
||||
:audio="audioList"
|
||||
:fixed="false"
|
||||
:autoplay="false"
|
||||
:loop="'all'"
|
||||
:order="'random'"
|
||||
:preload="'auto'"
|
||||
:volume="0.7"
|
||||
:mutex="true"
|
||||
:lrcType="0"
|
||||
:listFolded="false"
|
||||
:listMaxHeight="'250px'"
|
||||
theme="#b7daff"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import Vue3Aplayer from 'vue3-aplayer'
|
||||
|
||||
const show = ref(false)
|
||||
const isOpen = ref(false)
|
||||
|
||||
// 音乐列表配置 - 您可以根据需要修改这里的音乐
|
||||
const audioList = ref([
|
||||
{
|
||||
name: '夜的钢琴曲',
|
||||
artist: '石进',
|
||||
url: 'https://music.163.com/song/media/outer/url?id=27867140.mp3',
|
||||
cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
|
||||
lrc: ''
|
||||
},
|
||||
{
|
||||
name: 'River Flows In You',
|
||||
artist: 'Yiruma',
|
||||
url: 'https://music.163.com/song/media/outer/url?id=2133562.mp3',
|
||||
cover: 'https://p2.music.126.net/w3av4SHuMAgbBF2KKByaVw==/19217832579785884.jpg',
|
||||
lrc: ''
|
||||
},
|
||||
{
|
||||
name: 'Kiss The Rain',
|
||||
artist: 'Yiruma',
|
||||
url: 'https://music.163.com/song/media/outer/url?id=2618520.mp3',
|
||||
cover: 'https://p1.music.126.net/VjN74c1hoYgPCEZ9DngeQw==/109951163240682406.jpg',
|
||||
lrc: ''
|
||||
}
|
||||
])
|
||||
|
||||
const togglePlayer = () => {
|
||||
isOpen.value = !isOpen.value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 页面加载完成后显示播放器
|
||||
setTimeout(() => {
|
||||
show.value = true
|
||||
}, 500)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.music-player-wrapper {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.music-control {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.toggle-btn:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
|
||||
}
|
||||
|
||||
.music-player-container {
|
||||
position: absolute;
|
||||
bottom: 60px;
|
||||
right: 0;
|
||||
width: 350px;
|
||||
max-width: 90vw;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 深色模式适配 */
|
||||
.dark .music-player-container {
|
||||
background: rgba(30, 30, 30, 0.95);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 过渡动画 */
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.music-player-wrapper {
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.music-player-container {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="music-player-wrapper" v-if="show">
|
||||
<div class="music-control">
|
||||
<button class="toggle-btn" @click="togglePlayer" :title="isOpen ? '收起播放器' : '展开播放器'">
|
||||
<span v-if="isOpen">🎵</span>
|
||||
<span v-else>🎵</span>
|
||||
</button>
|
||||
</div>
|
||||
<transition name="slide">
|
||||
<div class="music-player-container" v-show="isOpen">
|
||||
<vue3-aplayer
|
||||
:audio="audioList"
|
||||
:fixed="false"
|
||||
:autoplay="false"
|
||||
:loop="'all'"
|
||||
:order="'random'"
|
||||
:preload="'auto'"
|
||||
:volume="0.7"
|
||||
:mutex="true"
|
||||
:lrcType="0"
|
||||
:listFolded="false"
|
||||
:listMaxHeight="'250px'"
|
||||
theme="#b7daff"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import Vue3Aplayer from 'vue3-aplayer'
|
||||
|
||||
const show = ref(false)
|
||||
const isOpen = ref(false)
|
||||
|
||||
// 音乐列表配置 - 您可以根据需要修改这里的音乐
|
||||
const audioList = ref([
|
||||
{
|
||||
name: '夜的钢琴曲',
|
||||
artist: '石进',
|
||||
url: 'https://music.163.com/song/media/outer/url?id=27867140.mp3',
|
||||
cover: 'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
|
||||
lrc: ''
|
||||
},
|
||||
{
|
||||
name: 'River Flows In You',
|
||||
artist: 'Yiruma',
|
||||
url: 'https://music.163.com/song/media/outer/url?id=2133562.mp3',
|
||||
cover: 'https://p2.music.126.net/w3av4SHuMAgbBF2KKByaVw==/19217832579785884.jpg',
|
||||
lrc: ''
|
||||
},
|
||||
{
|
||||
name: 'Kiss The Rain',
|
||||
artist: 'Yiruma',
|
||||
url: 'https://music.163.com/song/media/outer/url?id=2618520.mp3',
|
||||
cover: 'https://p1.music.126.net/VjN74c1hoYgPCEZ9DngeQw==/109951163240682406.jpg',
|
||||
lrc: ''
|
||||
}
|
||||
])
|
||||
|
||||
const togglePlayer = () => {
|
||||
isOpen.value = !isOpen.value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 页面加载完成后显示播放器
|
||||
setTimeout(() => {
|
||||
show.value = true
|
||||
}, 500)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.music-player-wrapper {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.music-control {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.toggle-btn:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
|
||||
}
|
||||
|
||||
.music-player-container {
|
||||
position: absolute;
|
||||
bottom: 60px;
|
||||
right: 0;
|
||||
width: 350px;
|
||||
max-width: 90vw;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 深色模式适配 */
|
||||
.dark .music-player-container {
|
||||
background: rgba(30, 30, 30, 0.95);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 过渡动画 */
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.music-player-wrapper {
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.music-player-container {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user