vue 官方换肤实现

演示地址:

https://elementui.github.io/theme-chalk-preview/#/zh-CN

 

原理:

element ui 2.0版本之后是基于scss实现的,所有到颜色都是通过变量进行定义,所以我们可以通过修改变量来达到动态换肤的目的.

 

代码:

1.换肤组件:

ThemePicker

  1 <template>
  2   <el-color-picker
  3     v-model="theme"
  4     :predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
  5     class="theme-picker"
  6     popper-class="theme-picker-dropdown"
  7   />
  8 </template>
  9 
 10 <script>
 11 const version = require('element-ui/package.json').version // element-ui version from node_modules
 12 const ORIGINAL_THEME = '#409EFF' // default color
 13 
 14 export default {
 15   data() {
 16     return {
 17       chalk: '', // content of theme-chalk css
 18       theme: ''
 19     }
 20   },
 21   computed: {
 22     defaultTheme() {
 23       return this.$store.state.settings.theme
 24     }
 25   },
 26   watch: {
 27     defaultTheme: {
 28       handler: function(val, oldVal) {
 29         this.theme = val
 30       },
 31       immediate: true
 32     },
 33     async theme(val) {
 34       const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
 35       if (typeof val !== 'string') return
 36       const themeCluster = this.getThemeCluster(val.replace('#', ''))
 37       const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
 38       console.log(themeCluster, originalCluster)
 39 
 40       const $message = this.$message({
 41         message: '  Compiling the theme',
 42         customClass: 'theme-message',
 43         type: 'success',
 44         duration: 0,
 45         iconClass: 'el-icon-loading'
 46       })
 47 
 48       const getHandler = (variable, id) => {
 49         return () => {
 50           const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
 51           const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
 52 
 53           let styleTag = document.getElementById(id)
 54           if (!styleTag) {
 55             styleTag = document.createElement('style')
 56             styleTag.setAttribute('id', id)
 57             document.head.appendChild(styleTag)
 58           }
 59           styleTag.innerText = newStyle
 60         }
 61       }
 62 
 63       if (!this.chalk) {
 64         const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
 65         await this.getCSSString(url, 'chalk')
 66       }
 67 
 68       const chalkHandler = getHandler('chalk', 'chalk-style')
 69 
 70       chalkHandler()
 71 
 72       const styles = [].slice.call(document.querySelectorAll('style'))
 73         .filter(style => {
 74           const text = style.innerText
 75           return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
 76         })
 77       styles.forEach(style => {
 78         const { innerText } = style
 79         if (typeof innerText !== 'string') return
 80         style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
 81       })
 82 
 83       this.$emit('change', val)
 84 
 85       $message.close()
 86     }
 87   },
 88 
 89   methods: {
 90     updateStyle(style, oldCluster, newCluster) {
 91       let newStyle = style
 92       oldCluster.forEach((color, index) => {
 93         newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
 94       })
 95       return newStyle
 96     },
 97 
 98     getCSSString(url, variable) {
 99       return new Promise(resolve => {
100         const xhr = new XMLHttpRequest()
101         xhr.onreadystatechange = () => {
102           if (xhr.readyState === 4 && xhr.status === 200) {
103             this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
104             resolve()
105           }
106         }
107         xhr.open('GET', url)
108         xhr.send()
109       })
110     },
111 
112     getThemeCluster(theme) {
113       const tintColor = (color, tint) => {
114         let red = parseInt(color.slice(0, 2), 16)
115         let green = parseInt(color.slice(2, 4), 16)
116         let blue = parseInt(color.slice(4, 6), 16)
117 
118         if (tint === 0) { // when primary color is in its rgb space
119           return [red, green, blue].join(',')
120         } else {
121           red += Math.round(tint * (255 - red))
122           green += Math.round(tint * (255 - green))
123           blue += Math.round(tint * (255 - blue))
124 
125           red = red.toString(16)
126           green = green.toString(16)
127           blue = blue.toString(16)
128 
129           return `#${red}${green}${blue}`
130         }
131       }
132 
133       const shadeColor = (color, shade) => {
134         let red = parseInt(color.slice(0, 2), 16)
135         let green = parseInt(color.slice(2, 4), 16)
136         let blue = parseInt(color.slice(4, 6), 16)
137 
138         red = Math.round((1 - shade) * red)
139         green = Math.round((1 - shade) * green)
140         blue = Math.round((1 - shade) * blue)
141 
142         red = red.toString(16)
143         green = green.toString(16)
144         blue = blue.toString(16)
145 
146         return `#${red}${green}${blue}`
147       }
148 
149       const clusters = [theme]
150       for (let i = 0; i <= 9; i++) {
151         clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
152       }
153       clusters.push(shadeColor(theme, 0.1))
154       return clusters
155     }
156   }
157 }
158 </script>
159 
160 <style>
161 .theme-message,
162 .theme-picker-dropdown {
163   z-index: 99999 !important;
164 }
165 
166 .theme-picker .el-color-picker__trigger {
167   height: 26px !important;
168   width: 26px !important;
169   padding: 2px;
170 }
171 
172 .theme-picker-dropdown .el-color-dropdown__link-btn {
173   display: none;
174 }
175 </style>

该组件将选择到颜色保存到vuex的state中, 代码: "this.$store.state.settings.theme"

 

2.添加store/modules/setting.js文件

 1 import variables from '@/styles/element-variables.scss'
 2 
 3 const state = {
 4   theme: variables.theme
 5 }
 6 
 7 const mutations = {
 8   CHANGE_SETTING: (state, { key, value }) => {
 9     if (state.hasOwnProperty(key)) {
10       state[key] = value
11     }
12   }
13 }
14 
15 const actions = {
16   changeSetting({ commit }, data) {
17     commit('CHANGE_SETTING', data)
18   }
19 }
20 
21 export default {
22   namespaced: true,
23   state,
24   mutations,
25   actions
26 }

setting.js中默认主题是引入的'@/styles/element-variables.scss'

3. 添加element-variables.scss

 1 /**
 2 * I think element-ui's default theme color is too light for long-term use.
 3 * So I modified the default color and you can modify it to your liking.
 4 **/
 5 
 6 /* theme color */
 7 $--color-primary: #1890ff;
 8 $--color-success: #13ce66;
 9 $--color-warning: #FFBA00;
10 $--color-danger: #ff4949;
11 // $--color-info: #1E1E1E;
12 
13 $--button-font-weight: 400;
14 
15 // $--color-text-regular: #1f2d3d;
16 
17 $--border-color-light: #dfe4ed;
18 $--border-color-lighter: #e6ebf5;
19 
20 $--table-border:1px solid#dfe6ec;
21 
22 /* icon font path, required */
23 $--font-path: '~element-ui/lib/theme-chalk/fonts';
24 
25 @import "~element-ui/packages/theme-chalk/src/index";
26 
27 // the :export directive is the magic sauce for webpack
28 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
29 :export {
30   theme: $--color-primary;
31 }

element-variables.scss 文件通过:export将theme导出,这样就能像使用js变量一样使用scss变量了,
准备工作就绪,开始测试

4.添加测试代码

 1 <template>
 2   <!-- 添加全局样式 -->
 3   <div>
 4     <div>
 5       <div class="test-theme">
 6         <el-button type="primary" />
 7       </div>
 8       <br>
 9       <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
10     </div>
11   </div>
12 </template>
13 <script>
14 import ThemePicker from '@/components/ThemePicker'
15 export default {
16   name: 'App',
17   components: {
18     ThemePicker
19   },
20   data() {
21     return {
22       msg: 'Dynamic Themes',
23       theme: 'default',
24     }
25   },
26   methods: {
27     themeChange(val) {
28       this.$store.dispatch('settings/changeSetting', {
29         key: 'theme',
30         value: val
31       })
32     }
33   }
34 }

大功告成!

优点: 可以动态改变主色调
缺点: 没有足够的个性化,仅仅能修改一些基本的配色

posted on 2020-03-15 11:26  atomgame的记事本  阅读(1574)  评论(0编辑  收藏  举报

导航