Skip to content
本页目录

聚焦式切换主题模式-light/dark

知识点罗列

效果示例

<script lang='ts' setup>
import { nextTick, ref, watch } from 'vue'
import { ElSwitch } from 'element-plus'
import { isDark, toggleDark } from './dark'

const darkMode = ref(isDark.value)
watch(
  () => darkMode.value,
  () => {
    console.log('change-------mode')
    toggleDark()
  },
)

let resolveFn: (value: boolean | PromiseLike<boolean>) => void
function switchTheme(event: MouseEvent) {
  console.log('change-------start')
  const isAppearanceTransition
    // @ts-expect-error
    = document.startViewTransition
    && !window.matchMedia('(prefers-reduced-motion: reduce)').matches
  if (!isAppearanceTransition || !event) {
    resolveFn(true)
    return
  }
  const x = event.clientX
  const y = event.clientY
  const endRadius = Math.hypot(
    Math.max(x, innerWidth - x),
    Math.max(y, innerHeight - y),
  )
  // @ts-expect-error: Transition API
  const transition = document.startViewTransition(async () => {
    resolveFn(true)
    await nextTick()
  })
  transition.ready.then(() => {
    console.log('change-------transition')
    const clipPath = [
      `circle(0px at ${x}px ${y}px)`,
      `circle(${endRadius}px at ${x}px ${y}px)`,
    ]
    document.documentElement.animate(
      {
        clipPath: isDark.value ? [...clipPath].reverse() : clipPath,
      },
      {
        duration: 500,
        easing: 'ease-in',
        pseudoElement: isDark.value
          ? '::view-transition-old(root)'
          : '::view-transition-new(root)',
      },
    )
  })
}
function beforeChange(): Promise<boolean> {
  return new Promise((resolve) => {
    resolveFn = resolve
  })
}
</script>

<template>
  <div class="custom-wrapper" @click.stop="switchTheme">
    <ClientOnly>
      <ElSwitch
        v-model="darkMode"
        :before-change="beforeChange"
        active-text="Dark"
        inactive-text="Light"
      />
    </ClientOnly>
  </div>
</template>

<style lang='scss' scoped>

</style>

知识点介绍以及使用

View Transition是谷歌浏览器提供的一个新特性,也叫做视图转换动画,或者转场动画能有平滑有效的实现动画的切换效果。

::view-transition表示视图过渡层叠层的根元素,他的结构大概有四层,它包含所有视图过渡且位于所有其他页面内容的顶部,也就是说他的相对父级是HTML元素

在视图过渡期间,::view-transition 包含在相关的伪元素树中,它是该树的顶级节点,并且有一个或多个 ::view-transition-group 子节点。

视图过度过程结构如下

以上视图切换过程仅用于了解其运作原理,核心在于一行JS代码,---> document.startViewTransition,该函数用于启动视图转换

整体过程如下:

  1. 调用document.startViewTransition浏览器会捕捉当前页面的状态以及画面
  2. 执行dom变化,之后会再次记录变化后的页面状态,也是类似于捕获画面
  3. 触发两个状态之间的过度,例如颜色、宽高、背景、位移等变化,也可以是animation动画

该过程可以通过document.documentElement.animate函数触发、更改class、启用animation等

null

MDN官方示例入口👈👈👈👈

参考与引用

  1. 太丝滑了!了解一下原生的视图转换动画 View Transitions API