最近使用uni-app开发App,其中有个功能是可以一键切换App的主题皮肤。其中主题皮肤分为黑夜版和白天版。经过一番研究,终于大功告成,因此记录一下。
之前,在做web端进行更换皮肤的时候,是在页面的根元素上自定义一个data-theme属性,属性的值分别是data-theme=“dark”和data-theme=“light”,然后通过切换dark和light来进行皮肤的动态更换:
但是在App环境当中没有window对象,因此只能另辟蹊径。
经过一番思考,决定仍然采用切换dark和light。首先,明确换肤换的是什么?换肤换的是背景色、字体颜色、边框色。其中,白天版light是默认。然后,在uni.scss当中定义全局的颜色变量,再义一个class类,在切换到dark的时候,显示这个class类,用于覆盖light下的原有颜色,用于显示黑夜版的皮肤。切换到light的时候,这个类消失,展示出白天版的皮肤。
具体做法如下:
1、在页面放置一个切换皮肤的按钮:
切换函数如下:
handleChangeSkin() {
const isDark = this.isDark;
const theme = isDark ? 'dark' : 'light';
console.log(theme);
this.setCurThemeType(theme);
uni.setStorageSync('theme', theme);
this.$store.dispatch('handleActionTheme', theme);
getApp().handleSetTabBar();
if (isDark) {
uni.setTabBarStyle({
color: '#9299A7',
selectedColor: '#3C79FF',
backgroundColor: '#2B3757'
});
} else {
uni.setTabBarStyle({
color: '#9299A7',
selectedColor: '#3C79FF',
backgroundColor: '#fff'
});
}
},
其中 uni.setStorageSync('theme', theme);是将切换的主题缓存起来,下次进入的时候在缓存当中直接取值。
this.$store.dispatch('handleActionTheme', theme);用于动态的改变定义在vuex当中的变量,达到动态换肤的效果。
//上面的代码用于动态的改变APP底部导航的颜色。
if (isDark) {
uni.setTabBarStyle({
color: '#9299A7',
selectedColor: '#3C79FF',
backgroundColor: '#2B3757'
});
} else {
uni.setTabBarStyle({
color: '#9299A7',
selectedColor: '#3C79FF',
backgroundColor: '#fff'
});
}
2、在uni.scss当中定义变量:
// light background
$light-bg-main: #f5f5f5;
$light-bg-gross: #fff;
$light-bg-menu: #f5f5f5;
// dark background
$dark-bg-main: #19223a;
$dark-bg-gross: #1d2742;
$dark-bg-menu: #2b3757;
// light font
$light-font-title: #666;
$light-font-gross: #666;
// dark font
$dark-font-title: rgba(255, 255, 255, 0.7);
$dark-font-gross: rgba(255, 255, 255, 0.7);
// light border
$light-border-divide: #dfdfdf;
// dark border
$dark-border-divide: #364364;
3、在sass文件当中定义两种不同的类,用于切换不同的皮肤下,所展示出来的颜色。
.main {
background-color: $light-bg-main;
&-dark {
background-color: $dark-bg-main;
}
}
.menu {
background-color: $light-bg-menu;
&-dark {
background-color: $dark-bg-menu;
}
}
.gross {
background-color: $light-bg-gross;
color: $light-font-gross;
&-dark {
background-color: $dark-bg-gross;
color: $dark-font-gross;
}
}
.divide {
border-bottom-style: solid;
border-bottom-width: 1px;
font-size: 12px;
border-color: $light-border-divide;
&-dark {
border-color: $dark-border-divide;
}
}
.titleFont {
color: #333;
&-dark {
color: rgba(255, 255, 255, 0.9);
}
}
4、在vuex的index.js当中定义切换的变量、切换的函数:
export default new Vuex.Store({
state: {
curThemeType: 'light',
},
mutations: {
setCurThemeType(state, data) {
state.curThemeType = data;
}
},
actions: {
handleActionAgree({ commit }, boo) {
commit('handleAgree', boo);
},
},
getters: {
themeType(state) {
return state.curThemeType;
}
},
modules: {}
});
5、使用 mixin混合机制,让主题变量themeType,成为全局变量,是的每个组件都能获得拥有 mixin中的方法和属性:
import { mapGetters, mapMutations } from 'vuex';
export default {
install(Vue) {
Vue.mixin({
mixins: [MescrollMixin, MescrollMoreItemMixin], // 使用mixin
data() {
return {
};
},
methods: {
...mapMutations(['setCurThemeType']),
},
computed: {
...mapGetters(['themeType'])
},
});
}
};
6、然后再main.js里引入 minxin.js:
import Vue from 'vue';
import App from './App';
import moment from 'moment';
import VueI18n from 'vue-i18n';
import store from './store';
import myMixin from './common/mixins.js';
import uView from 'uview-ui';
const en = require('@/lang/en-us.json');
const zh = require('@/lang/zh-cn.json');
const tw = require('@/lang/zh-tw.json');
const ja = require('@/lang/ja-jp.json');
const mergeZH = Object.assign({}, zh);
const mergeEN = Object.assign({}, en);
const mergeTW = Object.assign({}, tw);
const mergeJA = Object.assign({}, ja);
// import VConsole from 'vconsole';
//let vConsole = new VConsole();
Vue.use(VueI18n);
Vue.use(myMixin);
Vue.use(uView);
const i18n = new VueI18n({
locale: store.state.lang,
messages: {
'en-US': {
lang: mergeEN
},
'zh-CN': {
lang: mergeZH
},
'zh-TW': {
lang: mergeTW
},
'ja-JP': {
lang: mergeJA
}
}
});
App.mpType = 'app';
Vue.prototype._i18n = i18n;
Vue.prototype.moment = moment;
const app = new Vue({
i18n,
store,
...App
});
app.$mount();
export default app;
7、最后在需要切换主题的界面,例如index.vue里配置如下:
所以直接在 mixin.js里面 引用了 Vuex, 并且把Getters 和 Mutations 存放在 computed 和 methods里面; 最后通过 Vue.mixin(themeMixin) 把这computed 和 methods混入到所有 Vue组件里面; 这样任何组件可以直接使用 computed 里面的 themeType,