增加黑夜模式

This commit is contained in:
M0914
2025-09-24 10:25:37 +08:00
parent 957a342624
commit 5620df010c
4 changed files with 220 additions and 14 deletions
+126 -8
View File
@@ -272,6 +272,104 @@ body {
flex: 1;
}
/* 暗黑模式样式 */
html.dark {
filter: invert(1) hue-rotate(180deg);
}
/* 只有在没有View Transition时才使用CSS过渡 */
html:not(.view-transition-active) {
transition: filter 300ms ease;
}
html.dark img,
html.dark video,
html.dark .avatar,
html.dark .image,
html.dark .thumb,
html.dark [class*="icon-"],
html.dark .product-icon {
filter: invert(1) hue-rotate(180deg);
}
/* 主题切换按钮样式 */
.theme-toggle {
position: relative;
width: 50px;
height: 26px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 13px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 4px;
}
.theme-toggle::before {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 22px;
height: 22px;
background: white;
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
html.dark .theme-toggle::before {
transform: translateX(24px);
}
.theme-toggle .theme-icon {
font-size: 12px;
color: white;
z-index: 1;
transition: opacity 0.3s ease;
}
.theme-toggle .sun-icon {
opacity: 1;
}
.theme-toggle .moon-icon {
opacity: 0;
}
html.dark .theme-toggle .sun-icon {
opacity: 0;
}
html.dark .theme-toggle .moon-icon {
opacity: 1;
}
/* View Transition API 动画样式 */
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-old(root) {
z-index: 1;
}
::view-transition-new(root) {
z-index: 2147483646;
}
html.dark::view-transition-old(root) {
z-index: 2147483646;
}
html.dark::view-transition-new(root) {
z-index: 1;
}
/* 移动端优化 */
@media (max-width: 768px) {
.license-code-display {
@@ -279,13 +377,13 @@ body {
padding: 12px;
max-height: 200px;
}
.notification {
right: 10px;
left: 10px;
max-width: none;
}
.config-code {
font-size: 11px;
padding: 8px;
@@ -293,36 +391,56 @@ body {
word-break: break-all;
overflow-wrap: break-word;
}
.config-section {
padding: 12px;
margin: 12px 0;
}
/* 配置步骤移动端优化 */
.config-step-mobile {
flex-direction: column;
space-y: 12px;
}
.config-step-mobile .config-step-content {
margin-left: 0;
margin-top: 8px;
}
/* 首页内容移动端优化 */
.home-section {
padding: 16px;
margin-bottom: 16px;
}
.home-title {
font-size: 28px;
line-height: 1.2;
}
.home-subtitle {
font-size: 16px;
line-height: 1.4;
}
/* 主题切换按钮移动端优化 */
.theme-toggle {
width: 45px;
height: 24px;
border-radius: 12px;
}
.theme-toggle::before {
width: 20px;
height: 20px;
}
html.dark .theme-toggle::before {
transform: translateX(21px);
}
.theme-toggle .theme-icon {
font-size: 10px;
}
}
+11 -3
View File
@@ -87,9 +87,17 @@
</a>
</nav>
<button @click="showConfigModal = true" class="p-2 text-gray-600 hover:text-purple-600 transition-colors" title="配置">
<i class="fas fa-cog"></i>
</button>
<div class="flex items-center space-x-3">
<!-- 主题切换按钮 -->
<div @click="toggleTheme($event)" class="theme-toggle" title="切换主题">
<i class="fas fa-sun theme-icon sun-icon"></i>
<i class="fas fa-moon theme-icon moon-icon"></i>
</div>
<button @click="showConfigModal = true" class="p-2 text-gray-600 hover:text-purple-600 transition-colors" title="配置">
<i class="fas fa-cog"></i>
</button>
</div>
</div>
</div>
</div>
+11 -3
View File
@@ -64,16 +64,19 @@ const App = {
this.loadProducts()
this.loadPlugins()
this.setDefaultExpiryDate()
// 加载主题设置
Utils.loadTheme()
// 监听滚动事件
const handleScroll = () => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
this.showBackToTop = scrollTop > 300
}
// 监听滚动事件
window.addEventListener('scroll', handleScroll)
// 保存函数引用以便清理
this._handleScroll = handleScroll
},
@@ -239,6 +242,11 @@ const App = {
top: 0,
behavior: 'smooth'
})
},
// 主题切换
toggleTheme(event) {
Utils.toggleTheme(event)
}
}
}
+72
View File
@@ -108,6 +108,78 @@ const Utils = {
const date = new Date()
date.setFullYear(date.getFullYear() + 1)
return date.toISOString().split('T')[0]
},
// 主题切换功能
toggleTheme(event) {
// 检查浏览器是否支持 View Transition API
if (!document.startViewTransition) {
// 不支持则直接切换主题,不添加动画
document.documentElement.classList.toggle('dark')
this.saveTheme()
return
}
// 标记正在使用View Transition,避免CSS过渡冲突
document.documentElement.classList.add('view-transition-active')
const transition = document.startViewTransition(() => {
document.documentElement.classList.toggle('dark')
this.saveTheme()
})
transition.ready.then(() => {
const { clientX, clientY } = event
const endRadius = Math.hypot(Math.max(clientX, innerWidth - clientX), Math.max(clientY, innerHeight - clientY))
const clipPath = [`circle(0px at ${clientX}px ${clientY}px)`, `circle(${endRadius}px at ${clientX}px ${clientY}px)`]
const isDark = document.documentElement.classList.contains('dark')
document.documentElement.animate(
{
clipPath: isDark ? clipPath.reverse() : clipPath
},
{
duration: 450,
easing: 'ease-in',
pseudoElement: isDark ? '::view-transition-old(root)' : '::view-transition-new(root)'
}
)
})
// 动画完成后移除标记类
transition.finished.finally(() => {
document.documentElement.classList.remove('view-transition-active')
})
},
// 保存主题设置
saveTheme() {
const isDark = document.documentElement.classList.contains('dark')
localStorage.setItem('theme', isDark ? 'dark' : 'light')
},
// 加载主题设置
loadTheme() {
const savedTheme = localStorage.getItem('theme')
if (savedTheme === 'dark') {
document.documentElement.classList.add('dark')
} else if (savedTheme === 'light') {
document.documentElement.classList.remove('dark')
} else {
// 如果没有保存的主题,检查系统偏好
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
if (prefersDark) {
document.documentElement.classList.add('dark')
}
}
},
// 获取当前主题
getCurrentTheme() {
return document.documentElement.classList.contains('dark') ? 'dark' : 'light'
}
}