天天看點

uniapp主題切換功能的方式終結篇(全平台相容)

作者:8G網際網路

前面我已經給大家介紹了兩種主題切換的方式,每種方式各有自己的優勢與缺點,例如“scss變量+vuex”方式相容好但不好維護與擴充,“scss變量+require”方式好維護但相容不好,還不清楚的可點下面連結直達了解一下

uniapp主題切換功能的第一種實作方式(scss變量+vuex)

uniapp主題切換功能的第二種實作方式(scss變量+require)

了解了這些才能更好的了解我接下來給大家總結的。

最後做的這個能相容所有平台的主題切換效果,大家可以微信掃碼一睹為快,切換功能在”個人中心“那裡(模仿的b站),目前分白天與夜間模式

uniapp主題切換功能的方式終結篇(全平台相容)

接下來就給大家介紹一下如何做一個相容好,又好維護的主題切換功能

解決思路

uniapp應用在做開發的時候,拆分頁面其實就分兩大部分,主體部分+導航欄與tabbar

uniapp主題切換功能的方式終結篇(全平台相容)

為什麼要這麼分,因為主體部分的樣式通常是普通css代碼控制的,而導航欄+tabBar(例如原生的情況)須要通過api去修改。而css與js目前還不能完全互通。

是以要做全平台相容同樣須要維護主體部分的樣式(純css)與導航欄+tabBar部分的樣式(js),明白了原理,接下來就上代碼

第一部分:全局“主體部分”主題樣式

這樣其實就是之前講過的,上代碼

定義common/css/_theme.scss

$themes: (
    // 白天模式
    light:(
        page: (
            background-color: #fff,
            color: (
                color: #333,
            ),
            block: (
                background-color: #333,
                color: (
                    color: #fff,
                ),
            ),
        ),
        user-page: (
            background-color: #f2f2f2,
            color: (
                color: #666,
            ),
            block: (
                background-color: #999,
                color: (
                    color: #000,
                ),
            ),
        ),
    ),
    // 夜間模式
    dark:(
        page: (
            background-color: #333,
            color: (
                color: #fff,
            ),
            block: (
                background-color: #fff,
                color: (
                    color: #000,
                ),
            ),
        ),
        user-page: (
            background-color: #1a1a1a,
            color: (
                color: #fff,
            ),
            block: (
                background-color: #FFFFFF,
                color: (
                    color: #000,
                ),
            ),
        ),
    )
);           

生成主題樣式

@mixin map-to-class($map, $divider: "-", $select: ".theme", $isRoot: false, $root-select: ".theme") {
    $select: if($select== "" and &, &, $select);
    @each $k, $v in $map {
        $currSelect: if($isRoot, #{$root-select}#{$divider}#{$k}, #{$select}#{$divider}#{$k});
        #{$currSelect} {
            @if type-of($v) ==map {
                @include map-to-class($v, $divider, "", true) {
                    @content;
                }
            } @else {
                @at-root #{$select} {
                    #{$k}: $v !important;
                }
            }
        }
    }
}

@each $key, $mode in $themes {
    @if $key== "light" {
        @include map-to-class($mode);
    }
}
// 或
@each $key, $mode in $themes {
    @if $key== "dark" {
        @include map-to-class($mode);
    }
}           

其實可以循環一次性輸出,這個交給你們了。。。

頁面使用

<template>
    <view class="tpf-page theme-page">
        <text class="theme-color">訂單</text>
        <view class="theme-block block flex flex-align-center flex-pack-center">
            <text class="theme-color">闆塊裡面的文本</text>    
        </view>
        <view class="flex flex-align-center flex-pack-justify change-theme">
            <text class="button" @tap="changeTheme('light')">白天模式</text>
            <text class="button dark" @tap="changeTheme('dark')">夜間模式</text>
        </view> 
    </view>
</template>           

這裡主要通過加theme字首(你自己可以改成想要的)的類theme-page、theme-color、theme-block等等等的方式給内容塊加背景,給字型加顔色等

這樣頁面是不是就很好維護,不同顔色的頁面,你隻須要在_theme.scss主題裡面進行添加或修改後,在頁面添加回應的theme-xxx類即可。

這樣就處理了主體部分的樣式主題切換問題。

第二部分:全部“導航欄+tabBar”主題樣式

因為這部分涉及到原生操作,須要用到api,是以必須是js來維護主題樣式

定義theme.js

// 定義導航欄 與 tabbar 主題色
const themes = {
    light:{
        navBar:{
            backgroundColor:'#FFF',
            frontColor:"#000000"
        },
        tabBar:{
            backgroundColor:'#FFF',
            color:'#333',
            selectedColor:'#0BB640',
            borderStyle:'white'
        }
    },
    dark:{
        navBar:{
            backgroundColor:'#333',
            frontColor:"#ffffff"
        },
        tabBar:{
            backgroundColor:'#333',
            color:'#fff',
            selectedColor:'#0BB640',
            borderStyle:'black'
        }
    }
}
export default themes;            

第一種使用方式vue.prototype的全局挂載(不推薦)

不推薦的原因:::有相容問題!!!

mian.js

//引入主題
import themes from '@/common/theme/theme.js';
....
//全局挂載
Vue.prototype.$themes = themes;           

第二種使用方式:Vuex / globalData

為什麼使用這兩種,因為他們是目前官方相容所有平台的,這裡我隻介紹Vuex的方式

建立store.js

import Vue from 'vue'
import Vuex from 'vuex'
// 引入主題
import themes from '@/common/theme/theme.js';
Vue.use(Vuex);
 
const store = new Vuex.Store({
    state: {
        theme:themes[uni.getStorageSync('theme') || 'light']
    },
    getters: {
 
    },
    mutations: {
        updateTheme(state,mode = 'light'){
            state.theme = themes[mode];
        }
    }
})
 
export default store           

頁面使用

建立好store之後,就可以在頁面裡面動态設定導航欄與tabBar了,具體大家自己去根據喜好封裝。

onReady(){
    //Vuex的方式 
    // 設定導覽列
    uni.setNavigationBarColor(this.$store.state.theme.navBar);
    // 設定tabbar
    uni.setTabBarStyle(this.$store.state.theme.tabBar); 
},           

如何實作切換

更新就是更改store的狀态,因為他是全局的,所有頁面都能應用到

this.$store.commit("updateTheme",mode);
// 設定導覽列
uni.setNavigationBarColor(this.$store.state.theme.navBar);
// 設定tabbar
uni.setTabBarStyle(this.$store.state.theme.tabBar);           

最後總結

要想實作全端相容,肯定所有的代碼都要考慮到相容所有平台,因為做的時候要就考慮到。

主題切換對于應用來說是一個大工程,原理給大家說了,實作部署還須要大家好好的思考,其中擴充性,可維護性等都必須事先考慮的,不然項目肯定做不大。

想看我做的最終成品怎麼樣,可以掃碼看看

uniapp主題切換功能的方式終結篇(全平台相容)

有什麼做得不好的,或沒有考慮到位的,歡迎大家留言讨論交流,共同學習進步。