Skip to content

Vitepress 切换暗黑模式丝滑动画

发表于
更新于
字数
阅读量

移植之前 Hexo 博客中使用的暗黑切换动画,主要用到的原理就是 设置动态css变量原生视图过渡 - startViewTransition 以及 css的clip-path属性

1. 设置全局css

新建 dark.css,并注入到主题配置 docs/.vitepress/theme/index.ts

css
/**
* 黑暗模式切换动画
* -------------------------------------------------------------------------- */
::view-transition-old(*) {
  animation: none;
}
::view-transition-new(*) {
  animation: globalDark .8s ease-in;
}

@keyframes globalDark {
  from {
    clip-path: circle(0% at var(--darkX) var(--darkY));
  }
  to {
    clip-path: circle(100% at var(--darkX) var(--darkY));
  }
}

/**
* 黑暗模式下图片低亮度化
* -------------------------------------------------------------------------- */
.dark img {
    filter: brightness(0.8);
  }

切换动画速度,通过 animation 中的时间控制

2. 设置动态css变量

新建 Dark.ts,并在 Layout 插槽中使用

ts
import { nextTick, provide } from 'vue'
// 判断是否能使用 startViewTransition
const enableTransitions = () => {
  return 'startViewTransition' in document && window.matchMedia('(prefers-reduced-motion: no-preference)').matches
}
// 切换动画
export const toggleDark = (isDark) => {
  provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
    //如果不支持动效直接切换
    if (!enableTransitions()) {
      isDark.value = !isDark.value
      return
    }
    document.documentElement.style.setProperty('--darkX', x + 'px')
    document.documentElement.style.setProperty('--darkY', y + 'px')
    // 原生的视图转换动画 https://developer.mozilla.org/zh-CN/docs/Web/API/Document/startViewTransition
    // pnpm add -D @types/dom-view-transitions 解决 document.startViewTransition 类型错误的问题
    await document.startViewTransition(async () => {
      isDark.value = !isDark.value
      await nextTick()
    }).ready
  })
}
vue
<script setup lang="ts">
import { useData } from 'vitepress'
import { toggleDark } from './Dark'

const { isDark } = useData()
// 实现切换主题过渡动画
toggleDark(isDark)
</script>

最后将Layout 插槽组件,注入到 主题配置 docs/.vitepress/theme/index.ts

ts
import DefaultTheme from 'vitepress/theme'
import Layout from './Layout.vue'
// 暗黑样式
import './style/dark.css'

export default {
  extends: DefaultTheme,
  // 使用注入插槽的包装组件覆盖 Layout
  Layout: Layout
}