vue

vue3 实现主题切换

2024-09-05  本文已影响0人  暴躁程序员

一、方式一:自定义主题(常规版)

  1. 在 src/assets/theme.scss 中定义不同主题变量
html[data-theme='default'] {
  --color-page-bg: #fff;
  --color-page-fs: #000;
  --size-page-fs: 20px;
}

html[data-theme='dark'] {
  --color-page-bg: #000;
  --color-page-fs: #fff;
  --size-page-fs: 20px;
}

html[data-theme='red'] {
  --color-page-bg: red;
  --color-page-fs: #fff;
  --size-page-fs: 20px;
}
  1. 在入口 main.js 中注册
import '@/assets/theme.scss'
  1. 在 index.html 中,定义 data-theme 属性和默认主题值 default
<!doctype html>
<html lang="en" data-theme="default">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>

  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
  1. 在 src/App.vue 中,初始化主题
<script setup>
import { RouterView } from 'vue-router'
import { ref } from 'vue'
// 初始化主题
if (localStorage.getItem('current-theme')) {
  const currentTheme = ref(localStorage.getItem('current-theme'))
  document.documentElement.setAttribute('data-theme', currentTheme.value)
}
</script>

<template>
  <RouterView />
</template>
  1. 在 src/views/HomeView.vue 中,实现切换主题
<script setup>
// 切换主题
const fnToggle = (theme) => {
  localStorage.setItem('current-theme', theme)
  document.documentElement.setAttribute('data-theme', theme)
}

const themeOptions = [
  {
    label: '默认主题',
    value: 'default'
  },
  {
    label: '暗黑主题',
    value: 'dark'
  },
  {
    label: '红色主题',
    value: 'red'
  }
]
</script>

<template>
  <div class="wrapper">
    <h1>vue 主题切换</h1>
    <div>hello world</div>
    <button v-for="item in themeOptions" :key="item.label" @click="fnToggle(item.value)">
      {{ item.label }}
    </button>
  </div>
</template>
<style>
.wrapper {
  height: 100vh;
  width: 100vw;
  overflow-y: auto;
  box-sizing: border-box;
  background: var(--color-page-bg);
  color: var(--color-page-fs);
  font-size: var(--size-page-fs);
}
</style>

二、方式二:通过 style.filter 属性实现主题切换

  1. 在 src/App.vue 中实现
<script setup>
import { ref } from 'vue'

// 1、初始化模式、主题
const colorAreaValue = ref(Number(localStorage.getItem('style-filter-color')) || 0)
const isDark = localStorage.getItem('style-filter-dark') === 'true' ? true : false
const darkValue = ref(isDark || false)
if (!darkValue.value) {
  document.documentElement.style.filter = `hue-rotate(${colorAreaValue.value}deg)`
} else {
  document.documentElement.style.filter = `invert(${darkValue.value ? '85' : '0'}%)`
}
// 2、切换主题
const changeTheme = (v) => {
  document.documentElement.style.filter = `hue-rotate(${v}deg)`
  localStorage.setItem('style-filter-color', v)
  localStorage.setItem('style-filter-dark', false)
  darkValue.value = false // 主题变更要切换到普通模式
}
// 3、切换模式
const changeMode = (v) => {
  document.documentElement.style.filter = `invert(${v ? '85' : '0'}%)`
  localStorage.setItem('style-filter-dark', v)
  if (!v) {
    document.documentElement.style.filter = `hue-rotate(${colorAreaValue.value}deg)` // 切换到普通模式、主题要生效
  }
}
// 置灰模式
const changeGrayMode = () => {
  document.documentElement.style.filter = `grayscale(100%)`
}
</script>
<template>
  <div class="slider-demo-block">
    <div style="width: 40px">主题</div>
    <el-slider v-model="colorAreaValue" @input="changeTheme" show-input :max="360" />
  </div>
  <div class="slider-demo-block">
    <div style="width: 40px">模式</div>
    <el-switch
      v-model="darkValue"
      @change="changeMode"
      class="ml-2"
      inline-prompt
      style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
      active-text="普通模式"
      inactive-text="暗黑模式"
    />
  </div>
  <div style="margin: 20px 0">
    <el-button type="primary" @click="changeGrayMode">置灰模式</el-button>
  </div>
  <div class="mb-4">
    <el-button>Default</el-button>
    <el-button type="primary">Primary</el-button>
    <el-button type="success">Success</el-button>
    <el-button type="info">Info</el-button>
    <el-button type="warning">Warning</el-button>
    <el-button type="danger">Danger</el-button>
  </div>
</template>

<style lang="scss">
body {
  background: #f9f9f9;
}
.slider-demo-block {
  max-width: 600px;
  display: flex;
  align-items: center;
}
.slider-demo-block .el-slider {
  margin-top: 0;
  margin-left: 12px;
}
</style>
  1. 由于此方式是借助 css 的 filter 过滤属性实现的主题、模式切换,因此:
1. 此方式不需要定义主题文件
2. 此方式是通过 document.documentElement.style.filter 将过滤属性作用于全局
3. 此方式无法操作局部颜色
上一篇 下一篇

猜你喜欢

热点阅读