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

前言

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

效果

效果图

具体实现

1.准备项目

准备一个含有lessvuex的项目

2.安装插件

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

3.新建global.less

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

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

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"),
      ],
    },
  },
};

这里通过配置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}`
  }
};

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

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);
    }
};

7.动态换肤功能实现

  • 路径: src/view/Home.vue
<template>
  <div class="home">
    <header>温情key</header>
    <main>
      <div class="box-item">
        <div class="title">css动画-animation</div>
        <div class="content">
          steps()可以传入两个参数,第一个是一个大于0的整数,他是将间隔动画等分成指定数目的小间隔动画,也就是指定每个阶段分为几步来展示动画
          ,然后根据第二个参数来决定显示效果。第二个参数设置后
          其实和step-start,step-end同义,在分成的小间隔动画中判断显示效果。
        </div>
      </div>
      <div class="box-item box2">
        <div class="title">css动画-animation</div>
        <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.cn</a></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>

源码地址

gitee源码地址

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vue中,可以使用动态路由来实现路由的自定义动态路由是指根据不同的参数或条件生成不同的路由。 首先,在Vue的路由配置文件中(通常是`router/index.js`),可以使用路由的动态参数来定义动态路由。例如,可以在路由路径中使用冒号(:)来表示参数,如下所示: ```javascript { path: '/user/:id', name: 'User', component: User } ``` 上述代码中,`:id` 表示一个动态参数,它可以匹配任意值。当访问 `/user/123` 这个路径时,会显示 `User` 组件,并且可以通过 `$route.params.id` 来获取参数值。 如果需要在动态路由中传递多个参数,可以通过在路径中使用多个动态参数来实现。例如: ```javascript { path: '/user/:id/post/:postId', name: 'Post', component: Post } ``` 上述代码中,`:id` 和 `:postId` 分别表示两个不同的动态参数。 除了使用动态参数,还可以使用路由的查询参数来实现路由的自定义。查询参数是指在URL中以问号(?)开始的参数,例如 `?id=123`。可以在路由配置中使用 `props` 属性,将查询参数传递给组件。例如: ```javascript { path: '/user', name: 'User', component: User, props: (route) => ({ id: route.query.id }) } ``` 上述代码中,`props` 函数会将查询参数中的 `id` 属性传递给 `User` 组件,并通过 `props` 属性将其赋值给组件的 `id` 属性。 通过以上方式,可以根据不同的参数或条件生成不同的路由,并根据需要将参数传递给组件进行处理。这样就可以实现动态路由的自定义了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

温情key

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值