UNIAPP多主题
换肤方案简介
| 实现方案 | 问题 | 备注信息 |
|---|---|---|
| 点击切换主题按钮,重启项目, 重新引入对应的主题 | H5操作无异常,运行到APP后出现异常: App V3版本不支持在js中引入css,scss文件 | 考虑弃用 |
| 项目入口文件中App.vue中写一个视图元素 配置一个动态style的样式 | uniapp不支持在App.vue里面添加template标签 | 无法实现 |
| 在每一个页面中根节点添加一个动态style | 页面中不能使用page{}修改页面样式 | 可行 |
App.vue 是 uni-app 的主组件,所有页面都是在 App.vue 下进行切换的,是页面入口文件。但 App.vue 本身不是页面,这里不能编写视图元素,也就是没有 <template>。
换肤实现原理
- 配置主题
css变量文件 - 使用
vuex缓存同步样式设置, 使用stroage缓存样式 - 配置全局
SCSS变量和全局class - 添加
mixin样式混入 - 初始化主题和切换主题
- 单个页面使用
配置主题 CSS 文件
import light from "./theme-light";
import dark from "./theme-dark";
const themes = {
light,
dark
}
export default themes
const light = {
value: [
// 黑白色
{ name: '--white', value: '#ffffff'},
{ name: '--black', value: '#000000'},
// 背景色
{ name: '--bg-color', value: '#f7f9fc'}, // 页面背景色
// 主题色
{ name: '--primary-color', value: '#286df7'},
{ name: '--primary-color-disabled', value: '#afcafa'},
{ name: '--primary-color-light', value: '#e9f2ff'},
],
tabBar:{
color: '#808080',
selectedColor: '#1D61F6',
backgroundColor:'#ffffff'
},
// Android 上的 backgroundColor 参数有限制,黑色大于 rgb(30,30,30), 白色小于 rgb(235,235,235)
navbar: {
frontColor: '#000000', // 前景颜色值,包括按钮、标题、状态栏的颜色,仅支持 #ffffff 和 #000000
backgroundColor: '#dddddd',
}
}
export default light
const dark = {
value: [
// 黑白色
{ name: '--white', value: '#ffffff'},
{ name: '--black', value: '#000000'},
// 背景色
{ name: '--bg-color', value: '#f7f9fc'}, // 页面背景色
// 主题色
{ name: '--primary-color', value: '#286df7'},
{ name: '--primary-color-disabled', value: '#afcafa'},
{ name: '--primary-color-light', value: '#e9f2ff'},
],
tabBar:{
color: "#ffffff",
selectedColor: "#1D61F6",
backgroundColor: "#000000",
},
// Android 上的 backgroundColor 参数有限制,黑色大于 rgb(30,30,30), 白色小于 rgb(235,235,235)
navbar: {
frontColor: '#ffffff', // 前景颜色值,包括按钮、标题、状态栏的颜色,仅支持 #ffffff 和 #000000
backgroundColor: '#333333',
}
}
export default dark
配置全局 SCSS 变量
.status_bar {
height: var(--status-bar-height);
width: 100%;
}
/* 项目颜色配置 */
$black: var(--black);
$white: var(--white);
/* 主色 */
$primary-color: var(--primary-color);
$primary-color-disabled: var(--primary-color-disabled);
$primary-color-light: var(--primary-color-light);
/* 主色 */
/* 文字颜色 */
/* 背景颜色 */
$bg-color: var(--bg-color);
/* 背景颜色 */
同步并缓存样式
// 引入主题
import themes from '@/theme/index'
import storage from '@/utils/storage'
const store = {
state: {
// 写上默认皮肤的数据
skin: '',
skinMap: {},
theme: themes[storage.getItem('theme') || 'light']
},
getters: {
},
mutations: {
// 皮肤更换
skinPeeler(state,skin = []){
// 将皮肤配置JSON转为以 ; 分割的字符串(style 值)
let styleMap = {}
let style = skin.map((item,index)=>{
styleMap[item.name] = item.value
return `${item.name}:${item.value}`
}).join(";");
state.skin = style;
state.skinMap = styleMap;
console.log(skin,styleMap, style, 'mutations')
},
updateTheme(state,mode='light'){
state.theme = themes[mode];
}
}
}
export default store
const getters = {
skin: state => state.theme.skin,
theme: state => state.theme.theme,
skinMap: state => state.theme.skinMap,
}
export default getters
import Vue from 'vue'
import Vuex from 'vuex'
import user from '@/store/modules/user'
import theme from '@/store/modules/theme'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
user,
theme
},
getters
})
export default store
Minix 样式混入
import { mapGetters } from 'vuex'
import storage from '@/utils/storage'
import themes from '@/theme/index'
export default {
install(Vue) {
Vue.mixin({
onShow() {
setTimeout(() => {
this.initNavbar()
}, 100)
},
computed: {
...mapGetters(['skin', 'theme', 'skinMap'])
},
methods: {
// 初始化tabbar和navbar样式
initNavbar() {
let theme = storage.getItem('theme') || 'light'
let themeObj = themes[theme]
uni.setTabBarStyle(themeObj.tabBar)
uni.setNavigationBarColor(themeObj.navbar)
console.log(themeObj, "initNavbar")
},
// 获取某个css变量的颜色值
getStyleProperty(key) {
return this.skinMap[key]
}
}
})
}
}
import themeMixin from './mixin/themeMixin';
Vue.use(themeMixin);
切换与使用主题
// 初始化主题
import themes from '@/theme/index'
import store from '@/store'
import storage from '@/utils/storage'
...
methods: {
// 初始化主题
initTheme() {
let theme = storage.getItem('theme') || 'light'
let themeObj = themes[theme]
store.commit('skinPeeler', themeObj.value)
console.log(themeObj, "initTheme")
storage.setItem('theme', theme)
},
}
...
import storage from "@/utils/storage.js"
import themes from '@/theme/index'
import { mapMutations } from 'vuex'
...
methods: {
...mapMutations({
setTheme : 'skinPeeler',
updateTheme: 'updateTheme'
}),
// 切换主题
changeTheme(type) {
let theme = themes[type]
this.setTheme(theme.value)
this.updateTheme(type)
storage.setItem('theme', type)
uni.setTabBarStyle(theme.tabBar)
uni.setNavigationBarColor(theme.navbar)
console.log(theme, "changeTheme")
},
}
...
<template>
<view :style="skin">
<u-calendar
v-model="showCalendar"
:active-bg-color="activeBgColor"
mode="date">
</u-calendar>
...
</view>
</template>
<script>
...
computed: {
activeColor() {
return this.getStyleProperty('--primary-color')
},
}
...
</script>
.home-page {
color: $primary-color;
background: var(--bg-color);
}