記得剛畢業實習的時候,第一個需求就是如何實現小程序的主題切換。大家都知道,剛畢業的時候,啥也不會,一通亂改,還好最後老闆不計劃把原來的老的小程序改為uniapp寫,不然,我怕剛進公司就得被踢出公司。
話説回來,在職場摸爬滾打之後,最近正在做一款工具類的小程序 ToolMaster,剛好就想到了做一下主題色的切換。目前已經出效果了,起碼兼容微信小程序平台,大家也都知道微信小程序的情況,所以我覺得全平台應該也是OK的(我沒有測試其他平台)。大家可以先微信掃碼一睹為快,很好找,我的->主題設置 那裏。目前支持多種主題的切換,並且立即生效。
tips:不會UI,所以整體風格還是黑白灰,主題色大多用在邊框,字體圖標上了,不影響主題切換
接下來就直接給大家介紹一下怎麼實現這麼一個絲滑的主題切換功能
解決思路
我將會從兩個方面給大家介紹,我們要改變主題顏色,無非是在<style>標籤和<script>標籤裏面修改,只要搞定這兩者,那就拿捏住了~~
scss
uniapp項目都有一個uni.scss文件,默認就是全局使用,所以我們要切換主題,主題的樣式都是在這個裏面,直接先上代碼
uni.scss
// 全局變量聲明,解決Dart Sass 2.0.0+的!global警告
$theme: null;
// 1. 定義多套主題變量映射(默認主題保留原有值)
$themes: (
// 默認主題(原樣式值)
default: (
mainColor: #164FA3,
mainDark: #0F3A7A,
mainLight: #4A76B8,
secondColor: #E94F8C,
secondDark: #C83A74,
secondLight: #F387B3,
thirdColor: #FFD1DC,
foorthColor: #F8F9FA,
),
theme1: (
mainColor: #088686,
mainDark: #055A5A,
mainLight: #3ABABA,
secondColor: #DE5B24,
secondDark: #B54215,
secondLight: #F28A5C,
thirdColor: #FFE0D0,
foorthColor: #F8F9FA,
)
)
上面這部分代碼很好理解,你要切換主題,肯定要先定義好你的主題,就比如白天模式/夜間模式
// 2. 主題切換混合器
// uni.scss 中修改 themeify 混入
@mixin themeify {
@each $theme-name, $theme-map in $themes {
// 同時匹配頁面根元素和組件根元素的主題類
.theme-#{$theme-name} &,
&.theme-#{$theme-name} {
$theme: $theme-map !global;
@content;
}
}
}
這部分核心作用是讓樣式能根據不同的主題類動態應用對應的顏色配置
// 3. 主題變量獲取函數
@function themed($key) {
@return map-get($theme, $key);
}
這部分就相當於js裏面的函數一樣,可以傳參,然後返回具體的值
接下來我給大家介紹在頁面中怎麼使用
頁面
<style scoped lang="scss">
// 漸變背景樣式
.gradient-bg-primary {
@include themeify {
background: linear-gradient(135deg, themed('mainColor') 0%, themed('secondColor') 100%);
position: relative;
overflow: hidden;
}
}
</style>
lang="scss"是必不可少,使用的時候,在要使用的類裏面 用@include themeify {}包起來,然後使用上面的類似js函數的 themed('mainColor') 就能獲取到你在uni.scss中定義的mainColor,當然,有一個前提,就是頁面的根目錄要有一個類來標識當前選中的是哪個主題。
經過我不斷的嘗試,我發現直接給根節點動態的加類樣式不太好搞,尤其是對於微信小程序來説,不能獲取dom,所以只能手動給每個頁面(組件)的根元素加上主題的類樣式,那麼我採取的方案是vuex + mixins
store
與page同層級新建store/index.js
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
// 從緩存讀取主題,默認default
theme: uni.getStorageSync('theme') || 'default'
},
mutations: {
setTheme(state, themeName) {
state.theme = themeName
uni.setStorageSync('theme', themeName) // 同步保存到緩存
}
}
})
export default store
代碼很好理解,就是取緩存,沒有就是默認主題;然後可以保存主題,同時存到緩存裏面。
main.js
import Vue from 'vue'
import App from './App'
// 1. 導入創建好的 store
import store from './store/index'
// 2. 全局註冊 store
Vue.prototype.$store = store
App.mpType = 'app'
const app = new Vue({
...App,
store
})
app.$mount()
為了更好的得到當前的主題類,我們會用到計算屬性來獲取store中theme的值,那麼就可以考慮使用mixins,它可以全局混入,不必每個文件都寫一遍computed
mixins
// mixins/themeMixin.js
export default {
computed: {
// 全局可用的主題類名計算屬性
themeClass() {
return `theme-${this.$store.state.theme}`;
},
// 當前主題的完整配置(可選,用於需要在JS中獲取主題色的場景)
currentThemeConfig() {
// 這裏需要和uni.scss中的$themes配置保持一致
const themes = {
default: {
mainColor: "#164FA3",
secondColor: "#E94F8C",
thirdColor: "#FFD1DC",
secondLight: "#F387B3"
},
theme1: {
mainColor: "#088686",
secondColor: "#DE5B24",
thirdColor: "#FFE0D0",
foorthColor: "#F8F9FA",
},
}
return themes[this.$store.state.theme] || themes.default;
}
}
}
return theme-${this.$store.state.theme}; 這裏是與scss中對應,大家可以根據需要調整
currentThemeConfig是方面通過js操作樣式
然後在main.js中全局混入
// 導入主題混入
import themeMixin from './mixins/themeMixins.js'
// 註冊全局混入
Vue.mixin(themeMixin)
在所有頁面(組件)的根元素上,加上動態類 :class="themeClass" 即可,當然,沒有使用變量的頁面就不用啦
<template>
<view class="container" :class="themeClass">
//你的其他代碼
</view>
</template>
js
都有currentThemeConfig了,那麼直接使用就好了。注意,不要在data中直接使用。
export default {
data() {
return {
secondColor: null
}
},
onShow(){
this.secondColor = this.currentThemeConfig.secondColor
}
}
現在data中聲明為null,然後在生命週期函數內初始化,具體哪個生命週期,看個人情況。
總結
雖然沒有在其他平台測試,但是像微信小程序這麼難啃的骨頭都可以,其他平台應該也大差不差吧。另外,如果要做主題切換,最好在小程序開始之初,就有所考慮,避免後期代碼量高了之後在去替換。
最後,沒去看看效果的盆友還是可以看看我的效果之後再開始操作,可以掃下方的小程序碼體驗
做的不好,歡迎大家留言討論交流,共同進步,大神路過輕噴。