我们知道亮色暗色主题功能, 主要是给html元素加了一个自定义属性, 不同的自定义属性对应不同的css变量, 根据我们的选择不同, 去渲染不同的样式。我们在暗色亮色切换的基础上, 加一个跟随系统切换
首先, 在做这种主题切换功能的时候, 方案其实是有很多的, 比如css变量的方案, SASS方案, 传统方案等等, 今天, 我们基于css变量的方案, 实现一下主题切换
需要我们另起一个theme.css文件, 定义两个css变量
/* 定义暗色主题 */
html[data-theme="dark"] {
--text-primary: #fff;
--background-primary: #1b1b1b;
}
/* 默认亮色主题 */
:root {
--text-primary: #1b1b1b;
--background-primary: #fff;
}
在main.js中, 将上方定义的css变量进行导入
import './style/theme.css'
现在, 我们准备一个组件, 用于我们的主题切换, 将我们的css变量用于我们的样式中
background: var(--background-primary);
color: var(--text-primary);
接下来, 我们来写一个下拉框, 用于主题的选择
<div>
<select v-model="theme">
<!-- <option value="os">跟随系统</option> -->
<option value="light">亮色</option>
<option value="dark">暗色</option>
</select>
</div>
定义一个响应式数据, 优先去本地取数据, 本地没有就显示亮色
const theme = ref(localStorage.getItem("theme") || "light");
如果, 我们选择了其他选项, 应该触发监听, 去更改存储和自定义属性
watchEffect(() => {
// 存储本地
localStorage.setItem("theme", theme.value);
document.documentElement.dataset.theme = theme.value;
});
这样, 我们亮色和暗色的切换就完成了
接下来, 需要我们熟悉一个API, 用于我们跟随主题进行切换
Window.matchMedia() - Web API 接口参考 | MDN
该方法, 会返回一个新的 MediaQueryList 对象,表示指定的媒体查询 (en-US)字符串解析后的结果。返回的 MediaQueryList 可被用于判定 Document 是否匹配媒体查询,或者监控一个 document 来判定它匹配了或者停止匹配了此媒体查询。
我们来写一下, 匹配当前主题颜色是不是暗色, 通过该方法返回的结果, 我们可以拿到布尔类型true或者false
const match = matchMedia('(prefers-color-scheme: dark)');
console.log(match);
如果matches, 返回false, 说明当前主题是亮色
我们如果选择了深色, 就会返回true
这样, 就可以帮助我们跟随系统进行切换了
if(theme.value === 'os') {
document.documentElement.dataset.theme = match.matches? 'dark':'light'
}
但是, 这样写依然存在一个问题, 就是我们去更改主题颜色的时候, 页面更改不了, 所以, 我们需要给上面方法返回值注册一个change事件
const match = matchMedia('(prefers-color-scheme: dark)');
console.log(match);
match.addEventListener('change', () => {
document.documentElement.dataset.theme = match.matches? 'dark': 'light'
})
这样, 再次更改电脑主题颜色, 页面也会发生变化
最后, 我们再优化一下代码, 将相同的代码, 提取出来封装成一个函数
<script setup>
import { ref, watchEffect } from "vue";
const theme = ref(localStorage.getItem("theme") || "light");
const match = matchMedia('(prefers-color-scheme: dark)');
const changeFun = () => {
document.documentElement.dataset.theme = match.matches? 'dark': 'light'
}
// 用于更改主题颜色
watchEffect(() => {
if(theme.value == 'os') {
changeFun();
match.addEventListener('change', changeFun);
}
// 存储本地
localStorage.setItem("theme", theme.value);
document.documentElement.dataset.theme = theme.value;
});
</script>