一、js切换主题方案
1、link动态切换(不推荐)
- 通过改变link 标签的 href 属性实现动态修改样式
- 优点:实现了按需加载,提高了性能。
- 缺点:动态加载样式文件,可能会因为网络问题导致样式加载过慢;可维护性较差,后续新增或修改主题较为麻烦。
示例仅为核心切换部分,实际可根据业务拆分至全局文件,自行调配
xml
复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>主题切换示例</title>
<!--
默认主题
id="theme-style" - 为link元素设置ID
href指向默认主题CSS文件
-->
<link id="theme-style" rel="stylesheet" href="./assets/css/default.css">
</head>
<body>
<div class="container">
<h1>主题切换演示</h1>
<!--显示当前主题名称-->
<p>当前主题: <span id="current-theme">默认</span></p>
<!-- 主题切换按钮组 -->
<div class="theme-buttons">
<!--
每个按钮通过onclick事件调用changeTheme函数
并传入对应的主题名称参数
-->
<button onclick="changeTheme('default')">默认</button>
<button onclick="changeTheme('black')">暗黑</button>
<button onclick="changeTheme('greenyellow')">淡绿</button>
<button onclick="changeTheme('aqua')">浅蓝</button>
</div>
</div>
<script>
// 主题切换
function changeTheme(themeName) {
// 获取当前的theme-style链接元素
const themeStyle = document.getElementById('theme-style');
// 根据选择主题更新CSS文件路径
themeStyle.href = `./assets/css/${ themeName }.css`;
// 更新显示的当前主题名称
document.getElementById('current-theme').textContent = getThemeDisplayName(themeName);
// 将用户选择保存到localStorage,实现主题持久化
localStorage.setItem('theme', themeName);
}
// 获取主题的显示名称
function getThemeDisplayName(themeName) {
const themeNames = {
'aqua': '浅蓝',
'black': '暗黑',
'default': '默认',
'greenyellow': '淡绿'
};
return themeNames[themeName] || themeName; // 如果没有匹配则返回原名称
}
// 初始化主题
function initTheme() {
// 获取历史主题或使用默认主题
const savedTheme = localStorage.getItem('theme') || 'default';
// 调用主题切换函数更新主题
changeTheme(savedTheme);
}
// 页面加载时初始化主题
initTheme();
</script>
2、可参考vue切换方案6
(推荐)
3、css变量+自定义属性切换(推荐)
- 优点:不会因为网络问题导致样式切换延迟;使用方便;新增或修改主题方便灵活
- 缺点:首屏加载时会略微耗时
示例仅为核心切换部分,实际可根据业务拆分至全局文件,自行调配
xml
复制代码
<!DOCTYPE html>
<!--html的data-theme属性控制全局主题-->
<html lang="en" data-theme="default">
<head>
<meta charset="UTF-8">
<title>主题切换</title>
<style>
/* 默认主题变量 */
:root {
--bg-color: #ffffff;
--text-color: #333333;
transition: background-color 0.3s ease, color 0.3s ease;
}
/* 暗黑主题变量 */
[data-theme="black"] {
--bg-color: #333333;
--text-color: #ffffff;
}
/* 浅蓝主题变量 */
[data-theme="aqua"] {
--bg-color: aqua;
--text-color: greenyellow;
}
/* 淡绿主题变量 */
[data-theme="greenyellow"] {
--bg-color: greenyellow;
--text-color: aqua;
}
/* 页面内使用 */
body {
background-color: var(--bg-color);
color: var(--text-color);
}
#current-theme {
color: var(--text-color);
}
h3 {
color: var(--text-color);
}
/* 仅按钮样式 可忽略 */
.default {
color: #333333;
background-color: #ffffff;
border-color: #ffffff;
}
.black {
color: #ffffff;
background-color: #333333;
border-color: #333333;
}
.aqua {
color: greenyellow;
background-color: aqua;
border-color: aqua;
}
.greenyellow {
color: aqua;
background-color: greenyellow;
border-color: greenyellow;
}
</style>
</head>
<body>
<div class="app">
<h3>主题切换演示</h3>
<div class="theme-Text">当前主题: <span id="current-theme">默认</span></div>
<div class="btns">
<!--data-theme-name为自定义属性,用于存储主题名称-->
<button class="btn default" data-theme-name="default">默认</button>
<button class="btn black" data-theme-name="black">暗黑</button>
<button class="btn greenyellow" data-theme-name="greenyellow">淡绿</button>
<button class="btn aqua" data-theme-name="aqua">浅蓝</button>
</div>
</div>
<script>
// 获取包含所有主题按钮的容器元素
const btns = document.querySelector('.btns');
// 主题切换
function themeToggle(theme) {
let html = document.querySelector('html');
// 设置data-theme属性值
html.setAttribute('data-theme', theme);
// 保存当前主题
localStorage.setItem('theme', theme);
// 更新页面显示的当前主题名称
document.getElementById('current-theme').textContent = getThemeDisplayName(theme);
}
// 获取主题的显示名称
function getThemeDisplayName(theme) {
// 主题名称映射表
const themeNames = {
'aqua': '浅蓝',
'black': '暗黑',
'default': '默认',
'greenyellow': '淡绿'
};
// 返回对应的中文名称,如果没有匹配则返回原值
return themeNames[theme] || theme;
}
// 按钮事件
btns.addEventListener('click', (e) => {
// 检查点击的是否是带有'btn'类的元素
if(e.target.classList.contains('btn')) {
// 从被点击按钮的dataset中获取data-theme-name属性值
const themeName = e.target.dataset.themeName;
// 调用主题切换函数
themeToggle(themeName);
}
});
// 初始化主题
function initTheme() {
// 尝试从本地存储获取之前保存的主题,如果没有则使用'default'默认主题
const savedTheme = localStorage.getItem('theme') || 'default';
// 应用获取到的主题
themeToggle(savedTheme);
}
// 页面加载时初始化主题
initTheme();
</script>
</body>
</html>
二、vue切换主题方案
1、css变量+动态类名/自定义属性
- 使用方法参考
js切换方法3
,定义各个主体全局变量,
- 在index.html 中,定义data-theme 属性和默认主题值 default
- App.vue 中,初始化主题
- 项目任务需要切换的位置使用css变量
- 在任意位置定义变更自定义属性的逻辑,实现全局切换
2、css的filter(不推荐)
- css的filter过滤实现全局切换
- 无法修改局部变量,不太友好
3、v-bind切换(不推荐)
4、link文件或顶级css类名切换(不推荐)
5、组件库的主题切换
- 可通过修改当前组件库的全局变量,达到换肤效果
- 如element-puls,可以修改组件库全局变量,使默认色切换
- 下面例子演示将
element主题色#409eff
变为粉色#ffc0cb
ini
复制代码
<script>
// 处理主题样式
function handleThemeStyle(theme) {
document.documentElement.style.setProperty('--el-color-primary', theme);
for(let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${ i }`, `${ getLightColor(theme, i / 10) }`);
}
for(let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-dark-${ i }`, `${ getDarkColor(theme, i / 10) }`);
}
}
// hex颜色转rgb颜色
function hexToRgb(str) {
const newStr = str.replace('#', '');
let hexs = newStr.match(/../g);
let newHexs = [];
if(hexs) {
for(let i = 0; i < 3; i++) {
newHexs.push(parseInt(hexs[i], 16));
}
}
return newHexs;
}
// rgb颜色转Hex颜色
function rgbToHex(r, g, b) {
let hexs = [r.toString(16), g.toString(16), b.toString(16)];
for(let i = 0; i < 3; i++) {
if(hexs[i].length == 1) {
hexs[i] = `0${ hexs[i] }`;
}
}
return `#${ hexs.join('') }`;
}
// 变浅颜色值
function getLightColor(color, level) {
let rgb = hexToRgb(color);
for(let i = 0; i < 3; i++) {
rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
}
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
// 变深颜色值
function getDarkColor(color, level) {
let rgb = hexToRgb(color);
for(let i = 0; i < 3; i++) {
rgb[i] = Math.floor(rgb[i] * (1 - level));
}
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
handleThemeStyle('#ffc0cb')
</script>
6、js修改css变量换肤方案
- 为了方便演示,代码写在一个组件里,一般情况在入口文件中定义初始的全局变量;
- js部分可拆解为通用方法
- 可以组合动画效果,切换更流畅
- 切换部分可封装为系统主题切换组件,可以通过vuex或者pinia控制存储与切换
完整代码
xml
复制代码
<template>
<div class="box">
<h3>系统主题选择</h3>
<div>当前主题:<span style="color:var(--text-color)">{{ currentTheme }}</span></div>
<div v-for="(item,index) in themeList" :key="index" :style="{'background-color':item.textColor}" class="theme-item"
@click="changeTheme(item)">{{ item.title }}
</div>
<button @click="resetTheme">重置主题</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const currentTheme = ref(null);
const themeList = [
{ title: '科技蓝', textColor: '#165dff', bgColor: '#ffffff', borderColor: '#cccccc' },
{ title: '静谧绿', textColor: '#18a058', bgColor: '#ffffff', borderColor: '#cccccc' },
{ title: '星辰紫', textColor: '#8a2be2', bgColor: '#ffffff', borderColor: '#cccccc' },
{ title: '曜影红', textColor: '#a41f24', bgColor: '#ffffff', borderColor: '#cccccc' }
];
// 设置主题
function setTheme(themeObj) {
document.documentElement.style.setProperty('--text-color', themeObj.textColor);
document.documentElement.style.setProperty('--bg-color', themeObj.bgColor);
document.documentElement.style.setProperty('--border-color', themeObj.borderColor);
}
// 系统主题切换
function changeTheme(themeObj) {
currentTheme.value = themeObj.title;
setTheme(themeObj);
localStorage.setItem('theme', JSON.stringify(themeObj));
}
// 重置主题
function resetTheme() {
setTheme(themeList[0]);
localStorage.removeItem('theme');
}
// 初始化主题
function initTheme() {
// 本地读取保存的主题
const savedTheme = JSON.parse(localStorage.getItem('theme'));
// 使用历史主题或默认主题
if(savedTheme?.title) {
changeTheme(savedTheme);
currentTheme.value = savedTheme.title;
}
else {
changeTheme(themeList[0]);
currentTheme.value = themeList[0].title;
}
};
initTheme();
</script>
<style lang="scss">
:root {
--text-color: #165dff;
--bg-color: #f5f7fa;
--border-color: #e4e7ed;
}
.box {
display: flex;
flex-direction: column;
align-items: center;
background-color: var(--bg-color);
.theme-item {
width: 200px;
color: #ffffff;
padding: 10px 40px;
margin: 10px;
text-align: center;
cursor: pointer;
transition: all 1s;
&:hover {
opacity: 0.8;
}
}
}
</style>