zoukankan      html  css  js  c++  java
  • 给vue+element-ui动态设置主题色(包括外链样式、内联样式、行内样式)

    基本思路

    实现思路:实现一个mixins混入的主题js即theme.js,注册到全局下。使用el-color-picker组件切换颜色的时候,把颜色值传递到根root下,在根实例下监听主题色的变化来更改页面的主题,然后所有具体的路由页面的主题色修改通过在APP.vue页面监听路由变化来调用改变主题色方法。这里面用到providey与inject的使用,以及怎样把设置的主题色传递到根节点下,这里使用了vue1.x中的dispatch方法。

    大致总结:

    • 1.把得到的主题色传递给根root实例,在根实例中监听主题色的变化,并调用setThemeColor(newval, oldval)方法;
    • 2.在APP.vue中监听路由变化,并调用setThemeColor(newval, oldval)方法,目的是进入具体路由页面需要修改页面的head中的style样式、DOM元素中的行内style样式;

    具体实现如下。

    整体效果

    先看下整体实现效果:

    效果预览地址:《vue+element-ui动态设置主题效果》

    使用方式

    设置element-ui主题色引入到main.js中

    在src/styles下新建element-variables.scss:

    /* 改变主题色变量 */
    $--color-primary: #42b983;
    
    /* 改变 icon 字体路径变量,必需 */
    $--font-path: '~element-ui/lib/theme-chalk/fonts';
    
    @import "~element-ui/packages/theme-chalk/src/index";
    
    :export {
      colorPrimary: $--color-primary
    }

    main.js中引入该css:

    import variables from '@/styles/element-variables.scss'

    全局混入theme.js、emitter.js

    • theme.js主要方法setThemeColor(newval, oldval),该方法传入新的颜色值与旧的颜色值;
    • emitter.js中使用$$dispatch方法把修改的主题色提交到根实例下;

    在main.js 中引入该两个JS并注册:

    import theme from '@/mixins/theme.js'
    import emitter from '@/mixins/emitter.js'
    
    Vue.mixin(theme)
    Vue.mixin(emitter)

    核心代码调用

    • 主题色提交到根实例代码以及监听具体的路由页面修改样式(APP.vue)
    export default {
      name: 'App',
      inject: {
        themeConfig: {
          default: () => ({
            themeColor: '',
            defaultColor: ''
          })
        }
      },
      data() {
        return {
          themeColor: ''
        }
      },
      watch: {
        $route() {
          // 关键作用-进入到具体路由页面更新页面中DOM样式
          if (typeof this.themeConfig.themeColor != 'undefined' && this.themeConfig.themeColor !== this.themeConfig.defaultColor) {
            this.$nextTick(() => {
              if (this.themeConfig.themeColor && this.themeConfig.defaultColor) {
                this.setThemeColor(this.themeConfig.themeColor, this.themeConfig.defaultColor)
              }
            })
          }
        }
      },
      created() {
        // 如果本地存在主题色从本地获取,并提交给root分发到页面进行渲染
        if(Cookies.get('themeColor')) {
          this.themeColor = Cookies.get('themeColor');
          this.$$dispatch('root','root.config',[this.themeColor,true]); // 传递数组-解决初始加载执行setThemeColor两次问题
        } else {
          this.themeColor = this.themeConfig.themeColor;
        }
      },
      methods: {
        // 改变主题颜色
        changeThemeColor(value) {
          this.$$dispatch('root','root.config',value);
          Cookies.set('themeColor', value, { path: '/' });
        }
      }
    }
    • 根实例监听获取的主题色并监听设置主题色(main.js)
    new Vue({
      el: '#app',
      name: 'root',
      provide(){
        return {
          themeConfig: this
        }
      },
      data() {
        return {
          themeColor: variables.colorPrimary.toLowerCase(),
          defaultColor: variables.colorPrimary.toLowerCase(),
          themeFirstLoaded: true, // 主题是否第一次加载,解决初始主题watch跟$route执行setThemeColor两次问题
        }
      },
      created() {
        this.$on('root.config',(result,themeFirstLoaded) => {
          this.themeColor = result.toLowerCase();
          this.themeFirstLoaded = themeFirstLoaded;
        })
      },
      watch: {
        themeColor(newval, oldval) {
          if(!this.themeFirstLoaded) {
            this.setThemeColor(newval, oldval);
          }
        }
      },
      router,
      components: { App },
      template: '<App/>'
    })

    theme.js设置主题代码

    export default {
      methods: {
        // 样式更新
        updateStyle(stylecon, oldCulster, newCluster) {
          let newStyleCon = stylecon;
          oldCulster.forEach((color, index) => {
            let regexp = '';
            if (color.split(',').length > 1) {
              const rgbArr = color.split(',');
              regexp = new RegExp("\s*" + rgbArr[0] + "\s*,\s*" + rgbArr[1] + "\s*,\s*" + rgbArr[2] + "\s*", 'ig');
            } else {
              regexp = new RegExp(color, 'ig');
            }
            newStyleCon = newStyleCon.replace(regexp, newCluster[index])
          })
          return newStyleCon;
        },
    
        // 得到需要修改的一系类颜色值
        getThemeCluster(theme) {
          const clusters = [theme];
          for (let i = 0; i <= 9; i++) {
            clusters.push(this.getTintColor(theme, Number(i / 10).toFixed(2)));
          }
          clusters.push(this.getShadeColor(theme, 0.1));
          return clusters;
        },
    
        // 得到色调颜色
        getTintColor(color, tint) {
          let red = parseInt(color.slice(0, 2), 16);
          let green = parseInt(color.slice(2, 4), 16);
          let blue = parseInt(color.slice(4, 6), 16);
    
          if (tint == 0) {
            return [red, green, blue].join(',');
          } else {
            red += Math.round((255 - red) * tint);
            green += Math.round((255 - green) * tint);
            blue += Math.round((255 - blue) * tint);
            red = red.toString(16);
            green = green.toString(16);
            blue = blue.toString(16);
            return `#${red}${green}${blue}`
          }
        },
    
        // 获取阴影色调颜色
        getShadeColor(color, shade) {
          let red = parseInt(color.slice(0, 2), 16);
          let green = parseInt(color.slice(2, 4), 16);
          let blue = parseInt(color.slice(4, 6), 16);
    
          red = Math.round((1 - shade) * red);
          green = Math.round((1 - shade) * green);
          blue = Math.round((1 - shade) * blue);
    
          red = red.toString(16);
          green = green.toString(16);
          blue = blue.toString(16);
          return `#${red}${green}${blue}`
        },
    
        // 获取外链css文本内容
        getCSSText(url) {
          return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest()
            xhr.onreadystatechange = () => {
              if (xhr.readyState === 4 && xhr.status === 200) {
                const styleText = xhr.responseText.replace(/@font-face{[^}]+}/, '')
                resolve(styleText);
              }
            }
            xhr.open('GET', url)
            xhr.send()
          })
        },
    
    
        // 获取外链CSS样式的url地址
        getRequestUrl: function(src) {
          if (/^(http|https):///g.test(src)) {
            return src;
          }
          let filePath = this.getFilePath();
          let count = 0;
          const regexp = /..//g;
          while (regexp.exec(src)) {
            count++;
          }
          while (count--) {
            filePath = filePath.substring(0, filePath.lastIndexOf('/'));
          }
          return filePath + "/" + src.replace(/..//g, "");
        },
    
        // 获取当前window的url地址
        getFilePath: function() {
          const curHref = window.location.href;
          if (curHref.indexOf('/#/') != -1) {
            return curHref.substring(0, curHref.indexOf('/#/'));
          } else {
            return curHref.substring(0, curHref.lastIndexOf('/') + 1);
          }
        },
    
        // 修改主题色-head样式以及DOM行内样式
        async setThemeColor(newval, oldval) {
          if (typeof newval !== 'string') return;
          const newThemeCluster = this.getThemeCluster(newval.replace('#', ''));
          const orignalCluster = this.getThemeCluster(oldval.replace('#', ''));
          // 获取原始值中包含rgb格式的值存为数组
          const rgbArr = orignalCluster[1].split(',');
          const orignalRGBRegExp = new RegExp("\(\s*" + rgbArr[0] + "\s*,\s*" + rgbArr[1] + "\s*,\s*" + rgbArr[2] +
            "\s*\)", 'i');
    
          // 获取外链的样式内容并替换样式
          let styleTag = document.getElementById('new-configTheme__styles');
          const tagsDom = document.getElementsByTagName('link');
          if (!styleTag && tagsDom.length) {
            styleTag = document.createElement('style')
            styleTag.setAttribute('id', 'new-configTheme__styles')
            document.head.appendChild(styleTag);
            const tagsDomList = Array.prototype.slice.call(tagsDom);
            let innerTextCon = '';
            for (let i = 0; i < tagsDomList.length; i++) {
              const value = tagsDomList[i];
              const tagAttributeSrc = value.getAttribute('href');
              const requestUrl = this.getRequestUrl(tagAttributeSrc);
              const styleCon = await this.getCSSText(requestUrl);
              if (new RegExp(oldval, 'i').test(styleCon) || orignalRGBRegExp.test(styleCon)) {
                innerTextCon += this.updateStyle(styleCon, orignalCluster, newThemeCluster);
              }
            }
            styleTag.innerText = innerTextCon;
          }
    
          // 获取页面的style标签
          const styles = [].slice.call(document.querySelectorAll('style')).filter((style) => {
            const text = style.innerText;
            return new RegExp(oldval, 'i').test(text) || orignalRGBRegExp.test(text);
          })
    
          // 获取页面的style标签内容,使用updateStyle直接更新即可
          styles.forEach((style) => {
            const {
              innerText
            } = style;
            if (typeof innerText !== 'string') return;
            style.innerText = this.updateStyle(innerText, orignalCluster, newThemeCluster);
          })
    
          // 获取DOM元素上的style
          const domAll = [].slice.call(document.getElementsByTagName('*')).filter((dom, index) => {
            const stylCon = dom.getAttribute('style');
            return stylCon && (new RegExp(oldval, 'i').test(stylCon) || orignalRGBRegExp.test(stylCon))
          })
          domAll.forEach((dom) => {
            const styleCon = dom.getAttribute('style');
            dom.style = this.updateStyle(styleCon, orignalCluster, newThemeCluster);
          })
        }
      }
    }

    主要思路:通过传入新、旧颜色值替换head标签中样式以及DOM元素style的行内元素的样式。

    重要:外链的样式最好是压缩的样式,比如在vue-cli脚手架中,本地开发环境需要把样式提取到一个文件,并且压缩,dev.config.js部分代码如下:

    const ExtractTextPlugin = require('extract-text-webpack-plugin') // 提取CSS
    const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') // 压缩CSS
    
    const devWebpackConfig = merge(baseWebpackConfig, {
      module: {
        rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap,extract: true, usePostCSS: true })
      },
    
      plugins: [
        // ...省略其他代码
        new ExtractTextPlugin({
          filename: 'bundle.css',
          allChunks: true
        }),
        new OptimizeCSSPlugin({
          cssProcessorOptions: config.build.productionSourceMap
            ? { safe: true, map: { inline: false } }
            : { safe: true }
        })
      ]
    })

    github示例源码地址:《vue+element-ui动态主题色设置》

    参考地址

  • 相关阅读:
    MySQL 三节点企业版
    Raft 一致性算法论文译文 JAVA
    MySQL EXPLAIN 命令详解学习
    MySQL 5.7.17 Group Replication 初始
    JAVA 线程池以及其他
    什么是IIS并发连接数
    CUDA
    各种学习手册在线
    弱电系统标准CAD图例识图讲解
    ACM---算法
  • 原文地址:https://www.cnblogs.com/moqiutao/p/13896314.html
Copyright © 2011-2022 走看看