前言
有时候一个项目的主题并不能满足所有人的审美, 所以这个时候就需要换肤功能登场了。
下面是一个换肤demo
, 思路很简单,定义一个全局css变量
,然后在页面根元素获取变量并动态修改这个变量值即可完成。
效果
具体实现
1.准备项目
准备一个含有less
、vuex
的项目
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
是项目可选的配置文件
const path = require("path");
module.exports = {
pluginOptions: {
"style-resources-loader": {
preProcessor: "less",
patterns: [
path.resolve(__dirname, "./src/theme/global.less"),
],
},
},
};
这里通过配置style-resources-loader
让global.less
设置的变量供项目中其他文件直接使用,而避免重复在每个样式文件中通过@import
导入
5.新建model.js
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.动态换肤功能实现
<template>
<div class&#61;"home">
<header>温情keyheader>
<main>
<div class&#61;"box-item">
<div class&#61;"title">css动画-animationdiv>
<div class&#61;"content">
steps()可以传入两个参数&#xff0c;第一个是一个大于0的整数&#xff0c;他是将间隔动画等分成指定数目的小间隔动画&#xff0c;也就是指定每个阶段分为几步来展示动画
&#xff0c;然后根据第二个参数来决定显示效果。第二个参数设置后
其实和step-start&#xff0c;step-end同义&#xff0c;在分成的小间隔动画中判断显示效果。
div>
div>
<div class&#61;"box-item box2">
<div class&#61;"title">css动画-animationdiv>
<div class&#61;"content">
steps()可以传入两个参数&#xff0c;第一个是一个大于0的整数&#xff0c;他是将间隔动画等分成指定数目的小间隔动画&#xff0c;也就是指定每个阶段分为几步来展示动画
&#xff0c;然后根据第二个参数来决定显示效果。第二个参数设置后
其实和step-start&#xff0c;step-end同义&#xff0c;在分成的小间隔动画中判断显示效果。
div>
div>
main>
<footer><a href&#61;"https://www.wenqingkey.cn" style&#61;"text-decoration: none;color: #fff;">www.wenqingkey.cna>footer>
<div class&#61;"changeTheme" :style&#61;"{ right: openThemeCom &#61;&#61;&#61; true ? &#39;0&#39; : &#39;-310px&#39; }">
<div &#64;click&#61;"chooseTheme">{{ openThemeCom &#61;&#61;&#61; true ? "→" : "←" }}div>
<div &#64;click&#61;"setThemeHandle(&#39;default&#39;)">div>
<div &#64;click&#61;"setThemeHandle(&#39;dark&#39;)">div>
<div &#64;click&#61;"setThemeHandle(&#39;red&#39;)">div>
<div &#64;click&#61;"setThemeHandle(&#39;green&#39;)">div>
<div>
<input type&#61;"color" :value&#61;"defaultColor" &#64;input&#61;"chooseColor"/>
div>
div>
div>
template>
<script>
import { setTheme } from "../theme/changeTheme";
export default {
data() {
return {
openThemeCom: false,
};
},
computed: {
defaultColor() {
let color &#61; localStorage.getItem("primaryColor").split(&#39;,&#39;);
let hex &#61; "#" &#43; ((1 << 24) &#43; (Number(color[0]) << 16) &#43; (Number(color[1]) << 8) &#43; Number(color[2])).toString(16).slice(1);
return hex;
}
},
created() {
this.initTheme();
},
methods: {
initTheme() {
setTheme();
},
chooseTheme() {
this.openThemeCom &#61; !this.openThemeCom;
},
setThemeHandle(val) {
setTheme(val);
},
chooseColor(val) {
let hex &#61; val.target.value;
let r &#61; parseInt(&#39;0x&#39; &#43; hex.slice(1, 3));
let g &#61; parseInt(&#39;0x&#39; &#43; hex.slice(3, 5));
let b &#61; parseInt(&#39;0x&#39; &#43; hex.slice(5, 7));
let newPrimaryColor &#61; &#96;${r},${g},${b}&#96;;
localStorage.setItem("primaryColor", newPrimaryColor);
setTheme();
},
},
};
script>
<style lang&#61;"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(&#64;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(&#64;themeColor, 1);
font-size: 1.3em;
border-bottom: 1px solid #555;
}
.content {
padding: 10px;
background: rgba(&#64;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源码地址