Vue实时切换主题样式,第二弹

277 阅读2分钟

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

前言

我之前写过一篇文章,关于如何实时切换主题样式,但该方式的弊端在于,每个主题,你都需要为它定义各个不同的变量,比如黑白主题,黑夜下,文字基础变量@fontColor: white;,白天时,你需要再次定义该变量@fontColor: black;,甚至任性的你,可能随意再定义一个变量名,比如@dayFontColor: black;。随着项目的扩大,这样的变量名越来越多,里面有很多重复的(不同的开发人员,命名习惯不一样),这增加了项目维护的困难性,所以我决心寻找另一种替代方案

更好的主题切换方案

利用给HTML根元素:root添加属性的方式,实现实时切换主题功能

如何做到呢?

第一步

在项目内创建modeOptions.js文件,代码如下所示:

const modeOptions = {
  light: {
    '--default-color-1': 'rgba(255, 255, 255, 1)',
  },
  dark: {
    '--default-color-1': 'rgba(0, 0, 0, 1)',
  }
}

export default modeOptions

在上面,我们统一了变量名称--default-color-1,在不同的主题里,它的值是不一样的

第二步

在项目内创建applyMode.js文件,代码如下所示:

import modeOptions from './modeOptions'

function render (mode) {
  const rootElement = document.querySelector(':root')
  const options = modeOptions[mode]
  const opposite = mode === 'dark' ? 'light' : 'dark'
  for (const k in options) {
    rootElement.style.setProperty(k, options[k])
  }

  rootElement.classList.remove(opposite)
  rootElement.classList.add(mode)
}

export default function applyMode (mode) {
  if (mode !== 'auto') {
    render(mode)
    return
  }

  const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches
  const isLightMode = window.matchMedia('(prefers-color-scheme: light)').matches

  if (isDarkMode) render('dark')
  if (isLightMode) render('light')

  if (!isDarkMode && !isLightMode) {
    console.log('You specified no preference for a color scheme or your browser does not support it. I schedule dark mode during night time.')
    const hour = new Date().getHours()
    if (hour < 6 || hour >= 18) render('dark')
    else render('light')
  }
}

第三步

ThemeOptions.vue里,引入applyMode代码,代码如下所示:

<template>
    <div class="theme-options">
        <ul class="color-theme-options">
            <li
                v-for="(mode, index) in modeOptions"
                :key="index"
                :class="getClass(mode.mode)"
                @click.prevent="selectMode(mode.mode)"
            >{{ mode.title }}</li>
        </ul>
    </div>
</template>

<script>
import applyMode from './applyMode'
export default {
    data () {
        return {
            modeOptions: [
                { mode: 'dark', title: '暗黑模式' },
                { mode: 'light', title: '亮色模式' }
            ],
            currentMode: 'light'
        }
    },
    methods: {
        selectMode (mode) {
            if (mode !== this.currentMode) {
                this.currentMode = mode
                applyMode(mode)
                localStorage.setItem('mode', mode)
            }
        },
        getClass (mode) {
            return mode !== this.currentMode ? mode : `${mode} active`
        },
}
</script>

第四步

在样式文件内,添加var(--default-color-1),无论.styl,还是.css都支持该语法

.font {
    color: var(--default-color-1)
}

最后

成功变换了主题,且易于后期维护,完结撒花,贴一张小李子盛世美颜的图片

1.gif