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) 编辑 收藏 举报