当前位置:   article > 正文

elementui自定义主题(任意颜色)换肤功能_$--color-primary

$--color-primary

我的项目中使用了scss,elementui官网提供了解决方案如下:

  1. 在项目中改变 SCSS 变量
  2. Element 的 theme-chalk 使用 SCSS 编写,如果你的项目也使用了 SCSS,那么可以直接在项目中改变 Element 的样式变量。
  3. 1. 新建一个样式文件,例如 element-variables.scss,写入以下内容:
  4. /* 改变主题色变量 */
  5. $--color-primary: teal;
  6. /* 改变 icon 字体路径变量,必需 */
  7. $--font-path: '~element-ui/lib/theme-chalk/fonts';
  8. @import "~element-ui/packages/theme-chalk/src/index";
  9. 2. 之后,在项目的入口文件中,直接引入以上样式文件即可(无需引入 Element 编译好的 CSS 文件):
  10. import Vue from 'vue'
  11. import Element from 'element-ui'
  12. import './element-variables.scss'
  13. Vue.use(Element)
  14. 需要注意的是,覆盖字体路径变量是必需的,将其赋值为 Element 中 icon 图标所在的相对路径即可。

以上方法,我们修改主题色变量 $--color-primary 的值,然后在项目入口文件 main.js 中引入该样式文件,覆盖elementui的css文件,即可实现换肤。 

那么怎么做到自定义任意颜色的主题呢?大致思路如下:

  • 1.使用 el-color-picker 组件,供用户选择颜色
  • 2.监听颜色的变化,创建 style 标签,生成样式内容
  • 3.将创建好的样式内容插入 head 中

实际步骤如下:

  • 1.在项目中新建一个scss文件,比如就叫 elementui-variables.scss 

            

           内容如下:

  1. /* theme color */
  2. $--color-primary: #1890ff;
  3. $--color-success: #13ce66;
  4. $--color-warning: #FFBA00;
  5. $--color-danger: #ff4949;
  6. // $--color-info: #1E1E1E;
  7. $--button-font-weight: 400;
  8. // $--color-text-regular: #1f2d3d;
  9. $--border-color-light: #dfe4ed;
  10. $--border-color-lighter: #e6ebf5;
  11. $--table-border:1px solid #dfe6ec;
  12. /* icon font path, required */
  13. $--font-path: '~element-ui/lib/theme-chalk/fonts';
  14. // @import "~element-ui/packages/theme-chalk/src/index";
  15. :export {
  16. theme: $--color-primary;
  17. }
  • 2.考虑到项目全局都有可能使用到颜色这个变量,所以决定用vuex,如图:新建文件 settings.js

           

settings.js 内容如下,theme 的初始值为 elementui-variables.scss 中定义的theme。

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

 

  • 3.新建一个组件(一般在components目录下,我将它命名为ThemePicker),用于监听颜色变量 theme 的改变,然后 getHandler 函数生成样式文件,插入head中。在这里我遇到了一个问题,如果使用 document.head.appendChild(styleTag) ,页面就会变成一片空白,然后我就在想有可能是样式的顺序导致一些有用的样式被覆盖了,所以我就讲它改为 document.getElementsByTagName('style')[0].insertBefore(styleTag, null) ; 完美解决问题。

ThemePicker文件内容如下,可以看到默认的颜色是 this.$store.state.settings.theme ,它是在 elementui-variables.scss 中定义的。

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

页面上有些组件的样式可能是自定义的,不会随 theme 一起变化,可这么实现:

  1. <router-link v-for="tag in Array.from(visitedViews)" ref="tag" :style="isActive(tag)?{
  2. 'background-color': theme,
  3. 'color': '#ffffff',
  4. 'border-color': theme
  5. }:{}"
  6. :to="tag" :key="tag.path" class="tags-view-item">
  7. {{ tag.name }}
  8. <span class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)"/>
  9. </router-link>
  1. computed: {
  2. //...,
  3. theme() {
  4. return this.$store.state.settings.theme
  5. }
  6. },
  • 4.最后在页面上使用 ThemePicker 组件即可:

实际效果录屏:

 

 

 

 

 

 

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号