feat: 深色模式圆形揭示动画 + B 站私信规则
主题切换: - 抽出 useTheme 全局单例,主题在挂载前初始化,登录页不再闪白底 - 用 View Transitions API 以点击点为圆心做圆形揭示;不支持的浏览器 加 .no-vt 类退化为整页淡变,两者互斥避免揭示区露出过渡中的颜色 - 尊重 prefers-reduced-motion;过渡被浏览器跳过时静默降级为直接切换 - 主题按钮图标旋转切换 新规则: - bilibili-private-message:向指定 UID 发送 B 站私信, 发送者 UID 自动从账号 Cookie 读取 视觉打磨: - 统计数字改为缓出计数动画,等宽数字避免跳动时抖动 - 补 :focus-visible 焦点环 说明:当前 SDK 未提供视频投稿接口,无法支持定时投稿视频, 详见 docs/bilibili-config.md。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
Fold,
|
||||
} from '@element-plus/icons-vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import LogoMark from '@/components/LogoMark.vue'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -43,18 +44,8 @@ const menus = [
|
||||
|
||||
const activePath = computed(() => `/${route.path.split('/')[1] ?? ''}`)
|
||||
|
||||
const isDark = ref(document.documentElement.classList.contains('dark'))
|
||||
|
||||
function toggleDark() {
|
||||
isDark.value = !isDark.value
|
||||
document.documentElement.classList.toggle('dark', isDark.value)
|
||||
localStorage.setItem('gb-theme', isDark.value ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
// 恢复上次的主题选择
|
||||
if (localStorage.getItem('gb-theme') === 'dark' && !isDark.value) {
|
||||
toggleDark()
|
||||
}
|
||||
// 主题状态是全局单例,初始化在 main.ts 里完成
|
||||
const { isDark, toggleTheme } = useTheme()
|
||||
|
||||
async function handleLogout() {
|
||||
await userStore.logout()
|
||||
@@ -108,8 +99,15 @@ async function handleLogout() {
|
||||
<div class="header-title">{{ route.meta.title ?? '' }}</div>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="icon-btn" :title="isDark ? '切换到亮色' : '切换到暗色'" @click="toggleDark">
|
||||
<el-icon :size="17"><Moon v-if="!isDark" /><Sunny v-else /></el-icon>
|
||||
<button
|
||||
class="icon-btn theme-btn"
|
||||
:title="isDark ? '切换到亮色' : '切换到暗色'"
|
||||
@click="toggleTheme"
|
||||
>
|
||||
<Transition name="theme-icon" mode="out-in">
|
||||
<el-icon v-if="isDark" :size="17" key="sun"><Sunny /></el-icon>
|
||||
<el-icon v-else :size="17" key="moon"><Moon /></el-icon>
|
||||
</Transition>
|
||||
</button>
|
||||
<el-dropdown>
|
||||
<span class="user-chip">
|
||||
@@ -329,6 +327,37 @@ async function handleLogout() {
|
||||
background: var(--el-fill-color);
|
||||
}
|
||||
|
||||
/* 主题按钮:hover 时图标轻微旋转,点击有回弹 */
|
||||
.theme-btn .el-icon {
|
||||
transition: transform 0.4s var(--gb-ease);
|
||||
}
|
||||
|
||||
.theme-btn:hover .el-icon {
|
||||
transform: rotate(25deg) scale(1.1);
|
||||
}
|
||||
|
||||
.theme-btn:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
/* 图标切换:旧图标转出,新图标转入 */
|
||||
.theme-icon-enter-active,
|
||||
.theme-icon-leave-active {
|
||||
transition:
|
||||
opacity 0.2s var(--gb-ease),
|
||||
transform 0.28s var(--gb-ease);
|
||||
}
|
||||
|
||||
.theme-icon-enter-from {
|
||||
opacity: 0;
|
||||
transform: rotate(-90deg) scale(0.5);
|
||||
}
|
||||
|
||||
.theme-icon-leave-to {
|
||||
opacity: 0;
|
||||
transform: rotate(90deg) scale(0.5);
|
||||
}
|
||||
|
||||
.user-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user