uniapp小程序实现主题切换功能
业务场景:小程序中切换主题,如:白天,夜间,暗夜等。实现常规版与老年版界面的切换。
思路
- 原理:通过css-var变量的方式实现主题切换
- 定义主题样式至vuex中,通过vuex的mutations封装函数改变主题样式
- 使用mixins混入全局组件中,注册主题样式
- 在页面使用变量
:style={theme}
引入 - 使用scss简化var变量写法
实现代码
1、store/index.js
TIP
css-var变量不支持rpx单位,需要通过uni.upx2px()方法转换为px
js
// 页面路径:store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);//vue的插件机制
function rpxTopx(rpx) {
return `${uni.upx2px(rpx)}px`;
}
//Vuex.Store 构造器选项
const store = new Vuex.Store({
state: {
themeName: uni.getStorageSync("theme"),
themeStyle: {
"nature": `--title-size:${rpxTopx(32)};--info-size:${rpxTopx(28)};
`,
"older": `--title-size:${rpxTopx(44)};--info-size:${rpxTopx(36)};
`
}
},
getters: {
theme(state) {
return state.themeStyle[state.themeName]
}
},
mutations: {
// 切换主题方法
setTheme(state, themeName) {
state.themeName = themeName
}
}
})
export default store
// 页面路径:store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);//vue的插件机制
function rpxTopx(rpx) {
return `${uni.upx2px(rpx)}px`;
}
//Vuex.Store 构造器选项
const store = new Vuex.Store({
state: {
themeName: uni.getStorageSync("theme"),
themeStyle: {
"nature": `--title-size:${rpxTopx(32)};--info-size:${rpxTopx(28)};
`,
"older": `--title-size:${rpxTopx(44)};--info-size:${rpxTopx(36)};
`
}
},
getters: {
theme(state) {
return state.themeStyle[state.themeName]
}
},
mutations: {
// 切换主题方法
setTheme(state, themeName) {
state.themeName = themeName
}
}
})
export default store
2、mixins/theme.js
每个页面都需要获取相应主题的var变量字符串,使用mixin进行简化写法
js
import {
mapState,
mapGetters
} from 'vuex'
export default {
install(Vue) {
Vue.mixin({
computed: {
...mapState({
themeName: 'themeName'
}),
...mapGetters({
theme: "theme"
})
}
})
}
}
import {
mapState,
mapGetters
} from 'vuex'
export default {
install(Vue) {
Vue.mixin({
computed: {
...mapState({
themeName: 'themeName'
}),
...mapGetters({
theme: "theme"
})
}
})
}
}
3、main.js中导入store与mixin
js
import Vue from 'vue'
import App from './App'
import store from './store'
import mixin from '@/mixins/theme.js'
Vue.use(mixin)
App.mpType = 'app'
const app = new Vue({
store,
...App,
// 启用动态插槽名支持
scopedSlotsCompiler: 'augmented'
})
import Vue from 'vue'
import App from './App'
import store from './store'
import mixin from '@/mixins/theme.js'
Vue.use(mixin)
App.mpType = 'app'
const app = new Vue({
store,
...App,
// 启用动态插槽名支持
scopedSlotsCompiler: 'augmented'
})
4、uni.scss中使用var变量
scss
$title-size :var(--title-size); //标题字体大小
$info-size:var(--info-size);
$title-size :var(--title-size); //标题字体大小
$info-size:var(--info-size);
5、在页面中使用
html
<template>
<view :style="theme" class="page">
<button @click="setTheme('nature')" class="btn">常规版</button>
<button @click="setTheme('older')" class="btn">老年版</button>
</view>
</template>
<script>
export default {
data() {
return {}
},
methods: {
setTheme(theme) {
this.$store.commit('setTheme', theme)
uni.showToast({
title: '切换成功',
duration: 2000
})
}
}
}
</script>
<style lang="scss" scoped>
.tool_text {
margin-top: 25rpx;
font-size: $title-size;
color: #ffffff;
line-height: 48rpx;
}
<style>
<template>
<view :style="theme" class="page">
<button @click="setTheme('nature')" class="btn">常规版</button>
<button @click="setTheme('older')" class="btn">老年版</button>
</view>
</template>
<script>
export default {
data() {
return {}
},
methods: {
setTheme(theme) {
this.$store.commit('setTheme', theme)
uni.showToast({
title: '切换成功',
duration: 2000
})
}
}
}
</script>
<style lang="scss" scoped>
.tool_text {
margin-top: 25rpx;
font-size: $title-size;
color: #ffffff;
line-height: 48rpx;
}
<style>
总结
实现有没难度,目前需要每个页面都引入style:{theme}
,小程序App.vue中不能书写视图,目前没有找到解决方法。