主题切换功能

MuYan2022-09-16VueVueuni-app

功能说明

用于前端相同布局,不同样式的切换(黑、白主题样式切换),兼容H5、APP

  • 所需依赖
npm install --save css-vars-ponyfill vuex

前置条件:配置好 vuex

第一步

于已配置好的 vuex 内配置 store/index.jsstore/module/theme.js

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import theme from './module/theme'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    theme
  },
})

export default store
// store/module/theme.js
import cssVars from 'css-vars-ponyfill';
// 自定义的缓存方法 建议使用 uni.setStorageSync,具体自行自行去相应网站查阅
import {
  setCookie,
  getCookie
} from '@/xxx.js'

const themeNameArr = [
  'dark',
  'light'
]

const THEME_KEY = 'theme'

const THEME_STYLE_KEY = 'themeStyle'

export default {
  namespaced: true,
  state: {
    theme: getCookie(THEME_KEY) || 'dark', // 枚举值:dark、light
    themeStyle: ''
  },
  mutations: {
    setTheme(state, key) {
      state.theme = key
      setCookie(THEME_KEY, key ? key : themeNameArr[0])
    },
    setThemeStyle(state, key) {
      state.themeStyle = key
      setCookie(THEME_STYLE_KEY, key)
    },
  },
  actions: {
    async setThemeNav({
      state,
      dispatch
    }, theme = '') {
      // true 黑 false 白
      let judge = theme || await dispatch('getTheme')
      uni.setNavigationBarColor({
        frontColor: judge ? '#ffffff' : '#000000',
        backgroundColor: judge ? '#181825' : '#ffffff',
        animation: {
          duration: 0,
          timingFunc: 'easeIn'
        }
      })
      uni.setTabBarStyle({
        // tab 上的文字默认颜色
        color: judge ? '#8A8D9F' : '#000000',
        // tab 上的文字选中时的颜色
        selectedColor: judge ? '#0CC993' : '#0CC993',
        // tab 的背景色
        backgroundColor: judge ? '#272934' : '#FFFFFF',
        // tabBar上边框的颜色, 仅支持 black/white
        borderStyle: judge ? 'black' : 'white'
      })
    },
    async changeTheme({
      commit,
      state,
      dispatch
    }, theme = '') {
      let key = await dispatch('getTheme') ? themeNameArr[1] : themeNameArr[0]
      commit('setTheme', theme || key)
      dispatch('initTheme')
      // 切换刷新
      // #ifdef H5
      window.location.reload()
      // #endif
      // #ifdef APP-PLUS
      // plus.runtime.restart()
      // #endif
    },
    async getStyleJson({
      state,
      dispatch
    }) {
      // 判断主题 true 黑 false 白
      let judge = await dispatch('getTheme')
      return {
        '--font-color': `${judge ? '#FFFFFF' : '#272934'}`,
        '--theme-color': '#0CC993',
      }
    },
    // style 样式,JSON 对象转字符串
    getStyleStr({
      state
    }, style) {
      let s = []
      for (let i in style) {
        s.push(i + ':' + style[i]);
      }
      s = s.join(';')
      return s
    },
    initTheme({
      commit,
      dispatch
    }) {
      dispatch('getStyleJson').then(async res => {
        let style = await dispatch('getStyleStr', res)
        commit('setThemeStyle', style)
        // #ifdef H5
        cssVars({
          watch: true,
          variables: res,
          // 当添加,删除或修改其<link>或<style>元素的禁用或href属性时,ponyfill将自行调用
          onlyLegacy: true, // false 默认将css变量编译为浏览器识别的css样式 true 当浏览器不支持css变量的时候将css变量编译为识别的css
        });
        // #endif
      })
    },
    getTheme({
      state
    }) {
      // true 黑 false 白
      return state.theme === themeNameArr[0]
    }
  }
};

第二步

于新建 common 文件夹下新建 themeMixins.js

// themeMixins.js
import {
  mapState,
  mapActions
} from 'vuex'

const getThemeStyleMixins = () => {
  return {
    onLoad() {
      // APP 环境每次引入
      // #ifdef APP-PLUS
      this.initTheme()
      // #endif
    },
    computed: {
      ...mapState('theme', ['theme', 'themeStyle']),
      getThemeStyle() {
        // #ifdef H5
        return this.themeStyle
        // #endif
        // #ifdef APP-PLUS
        return this.themeStyle
        // #endif
      }
    },
    onShow() {
      // getCurrentPages 判断页面存在, globalData 判断主要是不在 App 混入
      if(getCurrentPages().length && !this.globalData) {
        this.$nextTick(() => {
          this.setThemeNav()
        })
      }
    },
    methods: {
      ...mapActions('theme', ['changeTheme', 'initTheme', 'setThemeNav', 'getTheme'])
    }
  }
}

export default getThemeStyleMixins

第三步

  • main.js 混入 themeMixins.js
// main.js
import Vue from 'vue'
import App from './App'
import store from './store'
import themeMixins from '@/common/themeMixins.js';

Vue.mixin(themeMixins());

Vue.config.productionTip = false

Vue.prototype.$store = store

App.mpType = 'app'

const app = new Vue({
  store,
  ...App
})
  • APP.vue 配置混入的 initTheme 方法
export default {
  onLaunch: function() {
    // #ifdef H5
    this.initTheme()
    // #endif
  }
}

第四步

  • 于每个页面的根 view 标签处配置 themeStyle 样式
<template>
  <view :style="themeStyle"></view>
<template>
  • 通过 css 的 var() 函数使用已配置的样式
.demo-wrap {
  background: var(--theme-color);
  font-size: var(--font-color);
}
上次更新 2026/6/23 11:49:15
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8