一键换肤方案总结
fuzuxian 3/8/2022 CSS
# 换肤方案1::root + css变量
:root 选择器匹配文档根元素。
在 HTML 中,根元素始终是 html 元素。
:root {
--bg-color-0: #fff;
--bg-color-1: #fff;
--text-color: #333;
--grey-1: #1c1f23;
}
:root .dark {
--bg-color-0: #16161a;
--bg-color-1: #35363c;
--text-color: #fff;
--grey-1: #f9f9f9;
}
body {
background-color: var(--bg-color-0);
}
.content {
background-color: var(--bg-color-1);
color: var(--text-color);
}
.content button {
background-color: var(--bg-color-1);
border: 1px solid var(--grey-1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
切换:
document.body.classList.remove('dark')
document.body.classList.add('dark')
1
2
2
# 换肤方案2:css变量 + css-vars-ponyfill兼容IE
- utils/theme.js 文件
export const themeVars = {
light: {
'--subMenuBg': '#1f2d3d',
'--pageBgColor': '#EAEFF8',
'--page-container-bgColor': '#FFFFFF',
'--description-title': '#333333',
},
dark: {
'--subMenuBg': '#fff',
'--pageBgColor': '#141837',
'--page-container-bgColor': '#1B2242',
'--description-title': 'rgba(255,255,255,0.80)',
}
};
import cssVars from 'css-vars-ponyfill';
export const initTheme = theme => {
cssVars({
watch: true, // 当添加删除或修改其<link>或<style>元素的禁用或href属性时,ponyfill将自行调用
variables: themeVars[theme], // variables 自定义属性名/值对的集合
onlyLegacy: false // false 默认将css变量编译为浏览器识别的css样式
});
return themeVars[theme]; // 返回变量,为了能够在.vue文件中的script标签中使用
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- 切换换肤按钮
<el-switch
v-model="currentTheme" active-text="白天" inactive-text="黑夜"
active-value="light" inactive-value="dark" @change="changeTheme"
></el-switch>
<script>
methods: {
created () {
this.currentTheme = localStorage.getItem('dataTheme') || 'light';
this.changeTheme()
},
...mapMutations({ setCurrentThemeObj: 'theme/setCurrentThemeObj' }),
changeTheme () {
localStorage.setItem('dataTheme', this.currentTheme);
const currentThemeObj = initTheme(this.currentTheme);
this.setCurrentThemeObj(currentThemeObj);
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- store:存储主题信息,以便在.vue文件中的script标签中使用
const state = { currentThemeObj: {} };
const getters = {
currentThemeObj: state => state.currentThemeObj;
};
const mutations = {
setCurrentThemeObj (state, currentThemeObj) {
state.currentThemeObj = currentThemeObj;
}
};
export default { namespaced: true, state, getters, mutations };
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- echart 换肤 --- .vue文件中的script标签中使用
computed: { ...mapGetters({ currentThemeObj: 'currentThemeObj' }) },
watch: { currentThemeObj () { this.setOption(); } },
methods: {
getOption () {
const fontColor = this.currentThemeObj['--alarmsRecentMonthFontColor'];
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 换肤方案3:scss变量@mixin混入覆盖
- ./src/style/theme/index.scss
$theme-light: ( overview-row-bgcolor: #fff );
$theme-dark: ( overview-row-bgcolor: #1b2242);
$themes: ( light: $theme-light, dark: $theme-dark, );
$theme-map: null;
// 第三步: 定义混合指令, 切换主题,并将主题中的所有规则添加到theme-map中
@mixin themify() {
@each $theme-name, $map in $themes {
// & 表示父级元素 !global 表示覆盖原来的
[data-theme="#{$theme-name}"] & {
$theme-map: () !global;
// 循环合并键值对
@each $key, $value in $map {
$theme-map: map-merge( $theme-map, ( $key: $value, ) ) !global;
}
// 表示包含 下面函数 themed()
@content;
}
}
}
@function themed($key) { @return map-get($theme-map, $key); }
// mixin主题颜色
@mixin bgColor($color) {
@include themify { background-color: themed($color); }
}
@mixin borderColor($color) {
@include themify { border-color: themed($color); }
}
@mixin textColor($color) {
@include themify { color: themed($color); }
}
// @mixin text-color($key) {
// color: map-get($colors-light, $key);
// [data-theme="dark"] & { color: map-get($colors-dark, $key); }
// }
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
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
- vue.config.js
const fs = require("fs");
const webpack = require("webpack");
// 获取主题文件名
const themeFiles = fs.readdirSync("./src/style/theme");
let ThemesArr = [];
themeFiles.forEach(function (item, index) {
let stat = fs.lstatSync("./src/style/theme/" + item);
if (stat.isDirectory() === true) {
ThemesArr.push(item);
}
});
module.exports = {
css: { loaderOptions: {
scss: { // 注意: 在sass-loader v8 中,这个选项是prependData
additionalData: `@import "./src/style/theme/index.scss";`,
}, },
},
configureWebpack: (config) => {
return { plugins: [
new webpack.DefinePlugin({ // 定义全局变量的插件
THEMEARR: JSON.stringify(ThemesArr),
THEMEFILES: JSON.stringify(themeFiles),
}),
],};
},
};
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
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
- APP.vue
<template>
<div id="app"> <router-view data-theme="light-theme" /> </div>
</template>
<script>
export default {
mounted() {
console.log("ThemesArr", THEMEARR, "THEMEFILES", THEMEFILES);
document.getElementsByTagName("body")[0].setAttribute("data-theme", "default");
},
};
</script>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- Home.vue--切换主题
currentTheme: "default",
currentThemeIndex: 0,
themeValue: [],
methods: {
onConfirm(currentTheme) {
this.currentTheme = currentTheme;
this.currentThemeIndex = this.themeValue.findIndex(
(theme) => theme === currentTheme );
document.getElementsByTagName("body")[0]
.setAttribute("data-theme", THEMEARR[this.currentThemeIndex]);
},
},
mounted() {
this.themeValue = THEMEARR;
this.currentThemeIndex = this.themeValue.findIndex(
(theme) => theme === "default" );
this.currentTheme = this.themeValue[this.currentThemeIndex];
},
<style lang="scss">
.home {
.t-home-info {
@include themify() {
color: themed("color");
font-weight: themed("font-weight");
font-size: themed("font-size");
}
}
}
</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
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
这种方式切换主题,会有卡顿,切换速度较慢。 方案二较好!!亲测!!