• vue动态换肤(自定义主题)


    前言

    有时候一个项目的主题并不能满足所有人的审美, 所以这个时候就需要换肤功能登场了。
    下面是一个换肤demo, 思路很简单,定义一个全局css变量,然后在页面根元素获取变量并动态修改这个变量值即可完成。

    效果

    效果图

    具体实现

    1.准备项目

    准备一个含有lessvuex的项目

    2.安装插件

    yarn add style-resources-loader vue-cli-plugin-style-resources-loader -D
    
    • 1

    3.新建global.less

    global.less用于定义全局变量设置全局默认样式

    • 路径: src/theme/global.less (先建一个theme目录)
    // 默认主题  因为会放在rgba()中  所以只需要rgb这三个值 使用rgba的好处是一个主题可以根据透明度配置更多相似主题的颜色
    @themeColor: var(--themeColor, 100, 149, 237);
    
    • 1
    • 2

    4.配置vue.config.js

    vue.config.js是项目可选的配置文件

    • 路径: 与 package.json 同级
    const path = require("path");
    
    module.exports = {
      pluginOptions: {
        "style-resources-loader": {
          preProcessor: "less",
          patterns: [
            path.resolve(__dirname, "./src/theme/global.less"),
          ],
        },
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里通过配置style-resources-loaderglobal.less设置的变量供项目中其他文件直接使用,而避免重复在每个样式文件中通过@import导入

    5.新建model.js

    model.js用于预设几套固定主题

    • 路径: src/theme/model.js
    export const themes = {
      /* 默认主题 */
      default: {
        backgroundColor: `${100},${149},${237}`
      },
      /* 暗黑主题 */
      dark: {
        backgroundColor: `${0},${0},${0}`
      },
      /* 鲜红主题 */
      red: {
        backgroundColor: `${247},${72},${72}`
      },
      /* 草绿主题 */
      green: {
        backgroundColor: `${59},${235},${115}`
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里我预设了四套固定主题

    6.新建changeTheme.js

    changeTheme.js用于定义修改主题的方法,修改样式与接收值来确定使用哪套主题的方法

    • 路径: src/theme/changeTheme.js
    import {
        themes
    } from "./model";
    
    // 修改样式
    const changeStyle = (obj) => {
        document.documentElement.style.setProperty(`--themeColor`, obj.backgroundColor);
    };
    
    // 改变主题的方法
    export const setTheme = (themeName) => {
        const haveTheme = themes[themeName];
        // 有预设的主题名称
        if (haveTheme) {
            localStorage.setItem("primaryColor", haveTheme.backgroundColor);
            changeStyle(haveTheme);
        } else {
            let haveTheme = {
                backgroundColor: localStorage.getItem("primaryColor")
            };
            changeStyle(haveTheme);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    7.动态换肤功能实现

    • 路径: src/view/Home.vue
    <template>
      <div class="home">
        <header>温情keyheader>
        <main>
          <div class="box-item">
            <div class="title">css动画-animationdiv>
            <div class="content">
              steps()可以传入两个参数,第一个是一个大于0的整数,他是将间隔动画等分成指定数目的小间隔动画,也就是指定每个阶段分为几步来展示动画
              ,然后根据第二个参数来决定显示效果。第二个参数设置后
              其实和step-start,step-end同义,在分成的小间隔动画中判断显示效果。
            div>
          div>
          <div class="box-item box2">
            <div class="title">css动画-animationdiv>
            <div class="content">
              steps()可以传入两个参数,第一个是一个大于0的整数,他是将间隔动画等分成指定数目的小间隔动画,也就是指定每个阶段分为几步来展示动画
              ,然后根据第二个参数来决定显示效果。第二个参数设置后
              其实和step-start,step-end同义,在分成的小间隔动画中判断显示效果。
            div>
          div>
        main>
        <footer><a href="https://www.wenqingkey.cn" style="text-decoration: none;color: #fff;">www.wenqingkey.cna>footer>
    
        <div class="changeTheme" :style="{ right: openThemeCom === true ? '0' : '-310px' }">
          <div @click="chooseTheme">{{ openThemeCom === true ? "→" : "←" }}div>
          <div @click="setThemeHandle('default')">div>
          <div @click="setThemeHandle('dark')">div>
          <div @click="setThemeHandle('red')">div>
          <div @click="setThemeHandle('green')">div>
          <div>
            <input type="color" :value="defaultColor" @input="chooseColor"/>
          div>
        div>
      div>
    template>
    
    <script>
    import { setTheme } from "../theme/changeTheme";
    
    export default {
      data() {
        return {
          // 选择主题组件开闭
          openThemeCom: false,
        };
      },
      computed: {
        // 取色器 默认颜色
        defaultColor() {
          /* 将 rgb 转换为 hex 值 */
          let color = localStorage.getItem("primaryColor").split(',');
          let hex = "#" + ((1 << 24) + (Number(color[0]) << 16) + (Number(color[1]) << 8) + Number(color[2])).toString(16).slice(1);
          // console.log(hex);
          return hex;
        }
      },
      created() {
        this.initTheme();
      },
      methods: {
        /* 初始化主题 */
        initTheme() {
          setTheme();
        },
        /* 选择主题组件显示/隐藏 */
        chooseTheme() {
          this.openThemeCom = !this.openThemeCom;
        },
        /* 切换主题 */
        setThemeHandle(val) {
          setTheme(val);
        },
        /* 自定义主题 */
        chooseColor(val) {
          // hex 转换为 rgb
          let hex = val.target.value;
          let r = parseInt('0x' + hex.slice(1, 3));
          let g = parseInt('0x' + hex.slice(3, 5));
          let b = parseInt('0x' + hex.slice(5, 7));
          let newPrimaryColor = `${r},${g},${b}`;
          // console.log(newPrimaryColor);
          localStorage.setItem("primaryColor", newPrimaryColor);
          setTheme();
        },
      },
    };
    script>
    <style lang="less" scoped>
    .home {
      widows: 100vw;
      height: 100vh;
      display: grid;
      grid-template-rows: 50px 1fr 50px;
      background: #eee;
      header,
      footer {
        width: 100vw;
        height: 50px;
        background: rgba(@themeColor, 1);
        text-align: center;
        line-height: 50px;
        font-size: 1.6em;
        color: #fff;
      }
    
      main {
        padding: 10px;
        .box-item {
          border: 1px solid #555;
        }
        .box2 {
          margin-top: 20px;
        }
        .title {
          padding: 6px;
          color: rgba(@themeColor, 1);
          font-size: 1.3em;
          border-bottom: 1px solid #555;
        }
        .content {
          padding: 10px;
          background: rgba(@themeColor, 0.2);
        }
      }
    
      .changeTheme {
        position: fixed;
        bottom: 70px;
        right: 0;
        height: 40px;
        width: 360px;
        background: #fff;
        display: grid;
        grid-template-columns: repeat(6, 1fr);
        column-gap: 5px;
        transition: 0.3s ease;
        div {
          position: relative;
        }
        div::after {
          display: inline-block;
          width: 60px;
          height: 20px;
          text-align: center;
          line-height: 20px;
          position: absolute;
          top: -22px;
          left: 0;
        }
        div:nth-child(1) {
          line-height: 40px;
          text-align: center;
        }
        div:nth-child(2) {
          background: rgb(100, 149, 237);
        }
        div:nth-child(2)::after {
          content: "默认";
        }
        div:nth-child(3) {
          background: rgb(0, 0, 0);
        }
        div:nth-child(3)::after {
          content: "暗黑";
        }
        div:nth-child(4) {
          background: rgb(247, 72, 72);
        }
        div:nth-child(4)::after {
          content: "鲜红";
        }
        div:nth-child(5) {
          background: rgb(59, 235, 115);
        }
        div:nth-child(5)::after {
          content: "草绿";
        }
        div:nth-child(6)::after {
          content: "自定义";
        }
      }
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184

    源码地址

    gitee源码地址

  • 相关阅读:
    两年Java开发工作经验面试总结
    Spring 声明式事务控制
    SeeOD应用:He-Ne激光束聚焦物镜设计
    基于STM32的蔬菜大棚温湿度智能控制系统设计
    华为云云耀云服务器L实例评测|云耀云服务器L实例部署Linux管理面板mdserver-web
    【计算机硬件体系架构】计算机电脑基本架构
    pytorch保存onnx模型
    C++ 2022常见知识点3
    数据结构—数组栈的实现
    QT web 开发 - video -- 笔记
  • 原文地址:https://blog.csdn.net/qq_48960335/article/details/127429671