Element-Plus主题切换

Element-Plus主题切换

基于 Vue3 + TypeScript + Scss + Element-plus 实现 Element-plus 主题切换。

Element-plus 主题切换涉及了 实验性:Web APICSS伪元素,因此在一些个别的浏览器和老版本的浏览器上无法实现该动画效果。

Web API:View Transitions API

View Transitions API 提供了一种机制,可以在更新 DOM 内容的同时,轻松地创建不同 DOM 状态之间的动画过渡。同时还可以在单个步骤中更新 DOM 内容。该API的startViewTransition()方法开始一个新的视图过渡

CSS伪元素:::view-transition-image-pair、::view-transition-new、::view-transition-old

::view-transition-image-pair: 表示一个视图过渡的旧视图状态和新视图状态的容器——即过渡前和过渡后的状态。
::view-transition-new: 表示视图过渡的新视图状态——即过渡后新视图的实时表示。
::view-transition-old: 表示视图过渡的旧视图状态——即过渡前旧视图的静态屏幕截图。

html

<script setup lang="ts">
import {Moon, Sunny} from "@element-plus/icons-vue";
// 引入切换主题的hooks
import {theme, toggleTheme} from "@/utils/useTheme";
import type {Theme} from "@/utils/useTheme";

defineOptions({
  name: 'ThemeSwitch',
})

const themeValue = ref<Theme>()
const switchRef = ref<HTMLElement>()

watchEffect((): void => {
  themeValue.value = theme.value
});

function changeSwitch(val: Theme) {
  if (!switchRef.value) return
  // 获取按钮的坐标位置
  const {offsetLeft: x, offsetHeight: y} = switchRef.value as any;
  toggleTheme(val, x, y)
}
</script>

<template>
  <div ref="switchRef">
    <el-switch
        v-model="themeValue"
        active-value="dark"
        inactive-value="light"
        :active-action-icon="Moon"
        :inactive-action-icon="Sunny"
        style="--el-switch-on-color: #2c2c2c; --el-switch-off-color: #f2f2f2"
        @change="changeSwitch"
    />
  </div>

</template>

<style scoped lang="scss">
</style>

scss

:root {
  ...
  // 关闭默认的 CSS 动画并防止新旧视图状态以任何方式混合(新状态从旧状态上方“擦除”,而不是过渡)
  &::view-transition-image-pair(root) {
    isolation: auto;
  }
  &::view-transition-old(root),
  &::view-transition-new(root) {
    animation: none;
    mix-blend-mode: normal;
    display: block;
  }
}

// 亮色主题
:root {
  ...
}

// 暗色主题
html[data-theme='dark'] {
  ...
  
  // 设置旧页面视图的屏幕截图层级
  &::view-transition-old(*) {
    z-index: 999;
  }
}

ts

export type Theme = "light" | "dark";

const LOCAL_KEY: string = "__theme__";

// 检查用户是否偏好深色主题
const darkModeMediaQuery: MediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');

// 初始化主题
export const theme = ref<Theme>((localStorage.getItem(LOCAL_KEY) as Theme) || (darkModeMediaQuery.matches ? 'dark' : 'light'));

// 监听主题变化并存储
watchEffect((): void => {
  localStorage.setItem(LOCAL_KEY, theme.value);
  document.documentElement.dataset.theme = theme.value;
});

// 监听系统主题变化
darkModeMediaQuery.addEventListener('change', (): void => {
  setTheme(darkModeMediaQuery.matches ? 'dark' : 'light')
});

// 设置主题
function setTheme(value: Theme): void {
  theme.value = value;
}

// 切换主题
export function toggleTheme(value: Theme, x: number, y: number): void {
  // 浏览器不支持 View Transitions 时的回退方案
  if (!(document as any).startViewTransition) {
    setTheme(value);
    return
  }

  // 获取屏幕对角线
  const getDiagonal: number = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y));

  // 绘画路径
  const clipPath: string[] = [`circle(0% at ${x}px ${y}px)`, `circle(${getDiagonal}px at ${x}px ${y}px)`];

  // 开始一次视图过渡:
  (document as any).startViewTransition((): void => {
    setTheme(value);
  }).ready.then((): void => {
  	// 根据不同的主题切换时,动画的方向将发生改变
    document.documentElement.animate({clipPath: value === 'dark' ? clipPath.reverse() : clipPath}, {
      // 持续时间
      duration: 400,
      // 指定附加动画的伪元素
      pseudoElement: `::view-transition-${value === 'dark' ? 'old' : 'new'}(root)`,
    })
  })
}
### 修改 Element Plus 主题色的具体实现步骤 在使用 Element Plus 时,如果需要修改主题色,可以通过以下几种方式实现: 1. **通过 SCSS 变量覆盖默认样式** 创建一个 SCSS 文件,例如 `src/styles/element-variables.scss`,并在其中定义 `$--color-primary` 来更改主色调。也可以根据需求调整其他颜色变量,如 success、warning 等: ```scss // src/styles/element-variables.scss $--color-primary: #27ba9b; $--color-success: #1dc779; $--color-warning: #ffb302; $--color-danger: #e26237; $--color-error: #cf4444; ``` 之后,在项目的入口文件中引入这个自定义的 SCSS 文件以覆盖默认主题样式 [^3]。 2. **通过 CSS 变量切换主题** 在页面中使用 CSS 变量来定义主题色,并通过 JavaScript 动态更改这些变量值。例如: ```html <style> :root { --theme-color: #000; --bg-color: #fff; } :root.dark { --bg-color: #303030; --theme-color: #fff; } </style> ``` 随后,通过 JavaScript 切换类名(如 `dark`)即可实现主题色的动态切换 [^5]。 3. **使用 Element Plus主题定制工具** 如果希望更加灵活地定制主题,可以利用 Element Plus 提供的主题定制功能。具体操作包括创建一个用于覆盖默认样式的 SCSS 文件,并在其中声明所需的配色方案。例如: ```scss @forward 'element-plus/theme-chalk/src/common/var.scss' with ( $colors: ( 'primary': ('base': #27ba9b), 'success': ('base': #1dc779), 'warning': ('base': #ffb302), 'danger': ('base': #e26237), 'error': ('base': #cf4444) ) ); ``` 这种方法适用于需要对多个颜色进行统一管理的场景 [^4]。 4. **手动修改样式文件** 对于某些特定需求,可以直接修改 Element Plus 的源码样式文件。然而,这种方式不推荐用于常规的主题切换,因为每次更新 Element Plus 版本时都需要重新应用修改 [^1]。 5. **主题切换注意事项** -切换主题时,需要注意 light 和 dark 模式下的所有颜色都必须完整声明,否则可能导致部分样式显示异常。 - 如果仅在一个主题中声明了某个颜色变量,则另一个主题中的对应样式可能不会发生变化 [^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值