赞
踩
对一个老项目增加切换主题的需求,从0到1的实现过程以及总结
以上是我画的一个简单示意图,下面是总结
1.通过dom操作切换link标签进行切换引入element ui 的主题文件(主题文件放在public静态资源中)
2.每个主题色值存储为js的key、value的形式,并通过js挂在到全局为var变量
3.通过cssVars插件对使用的var变量进行转换(解决ie不支持var的兼容性问题)
4.由于element ui可配置主题的内容太少,通过css进行组件样式的覆盖从而进行可定义
5.为每个16进制色值增加rgba的变量
6.向window下暴漏公共接口(色值的获取、修改等)为chrome的色值修改插件做铺垫
7.色值存储在全局的sotre中,用于js代码对全局色值的引用
以上是换肤需要做的事,下面我会详细介绍
代码如下(示例):
//index.js import { color as defaultThemeColor } from './default-config.js'//默认主题 import { color as darkThemeColor } from './dark-config.js'//暗黑主题 import store from '@/store/store' import cssVars from 'css-vars-ponyfill' import { fromHex } from '@/utils/index'//css颜色16进制转rgba方法 export const color = { default: colorToRgba(defaultThemeColor), dark: colorToRgba(darkThemeColor) } // 把16进制颜色对象增加对应的rgb颜色值 export function colorToRgba (color) { const param = { ...color } Object.keys(param).forEach(key => { if (param[key].includes('#')) { // 16进制颜色 const { r, g, b } = fromHex(param[key]) param[`${key}-rgb`] = `${r},${g},${b}` } }) return param } export function changeTheme (themeValue) { const body = document.body body.className = 'theme-' + themeValue // 当前使用的色表,以js形式存储 const colorTheme = Object.keys(store.state.home.themeColor).length > 0 && themeValue === localStorage.getItem('themeValue') ? store.state.home.themeColor : color[themeValue] // 把使用的主题色表存在vuex中,以便js使用色值的使用使用 store.dispatch('setThemeColor', colorTheme) // 保存当前主题类型 localStorage.setItem('themeValue', 'default') store.dispatch('setActiveTheme', themeValue) // 获取element ui 组件的主题路径(存在静态文件里) const staticPath = '/static' const itemPath = staticPath + '/theme/' + themeValue + '/index.css' loadCss(itemPath) // 存储当前主题当本地 localStorage.setItem('themeValue', themeValue) // 获取整个dom const root = document.querySelector(':root') || document.documentElement // 遍历主题样式配色 if (root && colorTheme) { for (const key in colorTheme) { // 寻找对应的主题色 if (Object.hasOwnProperty.call(colorTheme, key)) { // 把主题的样式设置根的style上(相当于设置全局的变量) root.style.setProperty(key, colorTheme[key]) } } } // 由于ie不支持var获取全局变量,colorTheme主题键值对使用下面的插件进行处理后就会转为转生的颜色 cssVars({ watch: true, // variables 自定义属性名/值对的集合 variables: colorTheme, // 当添加,删除或修改其<link>或<style>元素的禁用或href属性时,ponyfill将自行调用 // false 默认将css变量编译为浏览器识别的css样式 true 当浏览器不支持css变量的时候将css变量编译为识别的css onlyLegacy: false }) // 为整个html设置引入element ui的主题样式 function loadCss (path) { const head = document.getElementsByTagName('head')[0] const themeLink = document.getElementById('theme') if (themeLink) { // 若存在旧的link,则创建新的link把旧的link替换掉(link 引入element ui主题) // 创建新的link dom const link = document.createElement('link') link.href = path link.rel = 'stylesheet' link.type = 'text/css' // 把新dom插入到当前元素的下边 insertAfter(link, themeLink) // 当通过link引入的css加载完成以后,删除旧的css并把新的link赋上id onloadCss(link, () => { themeLink.parentNode.removeChild(themeLink) link.id = 'theme' }) } else { const link = document.createElement('link') link.href = path link.rel = 'stylesheet' link.type = 'text/css' link.id = 'theme' // 把link插入到head第一个子元素之前(head.childNodes[0]) head.insertBefore(link, head.childNodes[0]) } } } /** * @name 监听css文件的加载进度 * @param node {isDom} 监听的link dom * @param fn {function} 加载结束回调函数 * */ // 监听新的dom(node)是否加载完成 function onloadCss (node, fn) { if (node.attachEvent) { // IE node.attachEvent('onload', function () { fn(null, node) }) } else { // other browser setTimeout(function () { poll(node, fn) }, 0) } function poll (node, callback) { let isLoaded = false if (/webkit/i.test(navigator.userAgent)) { // webkit if (node.sheet) { isLoaded = true } } else if (node.sheet) { // for Firefox try { if (node.sheet.cssRules) { isLoaded = true } } catch (ex) { // NS_ERROR_DOM_SECURITY_ERR if (ex.code === 1000) { isLoaded = true } } } if (isLoaded) { setTimeout(function () { callback(null, node) }, 1) } else { // 若没有加载完,则在10毫秒以后继续判断 setTimeout(function () { poll(node, callback) }, 10) } } } function insertAfter (newNode, curNode) { // newNode插入到curNode.nextElementSibling节点之前 // 也就是把新节点newNode插入到当前节点curNode的下一个节点之前(当前节点之后) curNode.parentNode.insertBefore(newNode, curNode.nextElementSibling) }
以上代码重点就是对外暴漏的 changeTheme () 方法进行使用
在main.js以及主题切换按钮进行调用,传入default或dark等不同的主题标识
//main.js
// 加载用户主题
const theme = localStorage.getItem('themeValue')
if (theme) {
changeTheme(theme)
} else {
changeTheme('default')
}
//index.vue(按钮切换主题方法)
handleSetTheme (theme) {
if(theme===this.activeTheme){return}
this.$store.dispatch('setActiveTheme', theme)
changeTheme(theme)
}
如echarts等配置设置色值写的是js代码,此时需要从store进行引入:
label: {
show: true,
color: this.$store.state.home.themeColor['--text-color-primary'],
},
var引用或者scss变量形式引用:
//var形式
.el-button.el-button--default:not(.is-disabled){
color: var(--button-default-font);
}
//scss变量形式
.el-header {
background-color: $card-primary;
box-shadow: -1px 1px 26px -13px rgba($color-shadow-rgb,.5);
}
基于一个色值变量设置不同透明度:
(错误)
body{
--color:#ffffff
}
$color:var(#ffffff)
rgba(var(--color),0.5)
rgba($color,0.5)
注意:ragb里面不可有var(),因此上面两个rbga都有问题,应改为下面方式:
body{
--color-rgb:255,255,255
}
rgba($color-rgb,0.5)
需要在scss文件中定义这种数值型的值,然后进行引用才行,因此在index.js文件中会有colorToRgba方法为所有16进制color变量生成对应的rgb格式变量color-rgb
1.主题配置文件
//dark-config.js(黑夜主题文件) export const color = { // 主题色 '--color-primary': '#AD3393', '--color-primary-hover': '#BD5CA9', /* 主题按钮 */ '--button-primary-font': '#ffffff', // 按钮文字颜色 '--button-primary-background': '#AD3393', // 按钮背景色 '--button-primary-border': '#AD3393', // 按钮边框 '--button-primary-hover-font': '#ffffff', // 移入按钮文字颜色 '--button-primary-hover-background': '#BD5CA9', // 移入按钮背景色 '--button-primary-hover-border': '#BD5CA9', // 移入按钮边框 // 表格 // 表头背景底色 '--table-header-background': '#4A4A59', '--table-row-background': '#222235', // 表格行背景底色 '--table-top-background': '#3F3F4F', // 置顶时的底色 '--table-head-font-color': '#BCBCC2', // 表头文字 '--table-row-font-color': '#B5B5BB', // 表格行文字颜色 '--table-td-border-bottom-color': '#383849' // 表格单元格下边框 }
由于在切换element ui主题的时候是通过切换html页面根位置的link标签进行的,所以要放在静态文件的位置,通过绝对路径进行引入
const staticPath = '/static'
const itemPath = staticPath + '/theme/' + themeValue + '/index.css'
//改变link引入切换主题
loadCss(itemPath)
chomre插件学习(两个链接内容一样)
github
国内博客
其他同事写了一个chomre插件,可以通过chomre插件修改色值进行预览,之后可以进行配置文件的下载,更加方便其他开发人员以及ui进行设计。
插件思路:
通过项目中暴露给window的获取、修改色值等方法进行色值的展示以及修改,点击预览后调用changTheme方法进行主题更改,这样实现了利用插件实时更改主题,并可以把配置好的主题文件下载下来让开发进行替换。
/* 以下为暴漏给外部的接口可供修改颜色 */ // 当前存储的所有色值 window.allColor = function () { return store.state.home.themeColor } // 存储当前色值对应的各种配置(包括显示中文等) window.chromeConfig = function () { return chromeConfig } // 更改主题色值的方法 window.setThemeColor = function (colorTheme) { const theme = localStorage.getItem('themeValue') const newColor = Object.keys(colorTheme).length ? colorTheme : theme ? initColor[theme] : initColor.default store.dispatch('setThemeColor', colorToRgba(newColor)) } window.changeTheme = changeTheme
/* 此文件为暴漏给插件的文件 describe:每个色值的中文描述 default:默认主题的色值 dark:夜晚主题的色值 type: public:公共色值 input...:表格色值 (除public为公共色值外,其他为组件类型时(如input)用以判断色值的类别) */ import { color as defaultThemeColor } from './default-config.js' import { color as darkThemeColor } from './dark-config.js' const publicColor = {//公共样式配置 '--color-danger': { describe: '高危', type: 'public' }, } const componentColor = {//组件样式单独配置 '--input-border-color': { describe: '输入框-边框', type: 'input' }, '--input-content-color': { describe: '输入框-输入内容', type: 'input' }, '--input-placeholder-color': { describe: '输入框-提示内容', type: 'input' }, } const colorConfig = {//总配置 ...publicColor, ...componentColor, '--text-color-secondary': { describe: '描述等文字' }, } Object.keys(defaultThemeColor).forEach(key => { if (!colorConfig[key]) { console.error('色值配置文件项应与描述配置文件变量相统一') return {} } colorConfig[key].default = defaultThemeColor[key] colorConfig[key].dark = darkThemeColor[key] })
提示:以上是我对老项目增加切换主题功能的心得总结,希望可以多提建议。
有不懂的可以及时留言或者联系微信a13716670638
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。