头图

记得刚毕业实习的时候,第一个需求就是如何实现小程序的主题切换。大家都知道,刚毕业的时候,啥也不会,一通乱改,还好最后老板不计划把原来的老的小程序改为uniapp写,不然,我怕刚进公司就得被踢出公司。

话说回来,在职场摸爬滚打之后,最近正在做一款工具类的小程序 ToolMaster,刚好就想到了做一下主题色的切换。目前已经出效果了,起码兼容微信小程序平台,大家也都知道微信小程序的情况,所以我觉得全平台应该也是OK的(我没有测试其他平台)。大家可以先微信扫码一睹为快,很好找,我的->主题设置 那里。目前支持多种主题的切换,并且立即生效。

tips:不会UI,所以整体风格还是黑白灰,主题色大多用在边框,字体图标上了,不影响主题切换

下载.png

接下来就直接给大家介绍一下怎么实现这么一个丝滑的主题切换功能

解决思路

我将会从两个方面给大家介绍,我们要改变主题颜色,无非是在<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,然后在生命周期函数内初始化,具体哪个生命周期,看个人情况。

总结

虽然没有在其他平台测试,但是像微信小程序这么难啃的骨头都可以,其他平台应该也大差不差吧。另外,如果要做主题切换,最好在小程序开始之初,就有所考虑,避免后期代码量高了之后在去替换。

最后,没去看看效果的盆友还是可以看看我的效果之后再开始操作,可以扫下方的小程序码体验

下载.png

做的不好,欢迎大家留言讨论交流,共同进步,大神路过轻喷。


前端小李子
1 声望0 粉丝