问题解决|使用Less变量和媒体查询实现深浅色模式适配

grtsinry43
6/10/2024
33 views
预计阅读时长 13 分钟

使用Less变量简化css写法,并不影响css变量和媒体查询在深浅模式切换时的效果

<!--more-->

分析问题

首先要清楚问题的来源是什么,使用媒体查询api可以根据深浅色为css变量赋予不同的值,而Less(或是scss)不是原生被浏览器所支持,运行时需要先转为为原生css,这一步也就是编译,有点类似ts和js的关系。这里采用的是vite创建的Vue3项目
而Less/Scss在编译的过程中,进行的是完全的字段替换,这也就意味着在运行之前css已经被固定,无法在运行的过程中动态修改,当然也不支持媒体查询的所有属性,但一些还是支持的哈,比如设备类型,最大宽度什么的,这里深浅色默认是不支持的。

问题出现

这里首先我是想办法完全使用Less的,也就是媒体查询+正常定义变量

LESS
1@primary: #1890ff; // 全局主色
2@bg: #ffffff; // 全局背景
3@font: #333333; // 全局字体颜色
4@warning: #faad14; // 警告
5@success: #52c41a; // 成功
6@error: #f5222d; // 错误
7@info: #1890ff; // 信息
8@disabled: #bfbfbf; // 失效
9@link: #1890ff; // 链接
10@hover: #001764; // hover
11
12@media (prefers-color-scheme: dark) {
13  :root {
14    @primary: #718dff; // 全局主色
15    @bg: #1d1e21; // 全局背景
16    @font: #ffffff; // 全局字体颜色
17    @warning: #faad14; // 警告
18    @success: #a8ff7d; // 成功
19    @error: #f5222d; // 错误
20    @info: #80c1ff; // 信息
21    @disabled: #4e4e4e; // 失效
22    @link: #a0b5ff; // 链接
23    @hover: #d5e8ff; // hover
24  }
25}

但是实际测试中发现所编译出的css根本没有@media的字样,所以又是inline编译了,导致完全写死,不可能在运行中改变

问题解决

因为测试Less写法一直有问题,而原生css是可以完美解决的,为了既保留Less的简便的变量写法,又解决变量无法修改的问题,因此利用字段替换编译的特性,我们可以将Less变量赋值为css变量,这样当编译之后所生成的css,依然是变量形式储存内容,具体代码可以参考:

colors.less

LESS
1:root {
2  --primary: #1890ff; // 全局主色
3  --bg: #ffffff; // 全局背景
4  --font: #333333; // 全局字体颜色
5  --warning: #faad14; // 警告
6  --success: #52c41a; // 成功
7  --error: #f5222d; // 错误
8  --info: #1890ff; // 信息
9  --disabled: #bfbfbf; // 失效
10  --link: #1890ff; // 链接
11  --hover: #001764; // hover
12}
13
14@primary: var(--primary);
15@bg: var(--bg);
16@font: var(--font);
17@warning: var(--warning);
18@success: var(--success);
19@error: var(--error);
20@info: var(--info);
21@disabled: var(--disabled);
22@link: var(--link);
23@hover: var(--hover);
24
25@media (prefers-color-scheme: dark) {
26  :root {
27    --primary: #718dff; // 全局主色
28    --bg: #1d1e21; // 全局背景
29    --font: #ffffff; // 全局字体颜色
30    --warning: #faad14; // 警告
31    --success: #a8ff7d; // 成功
32    --error: #f5222d; // 错误
33    --info: #80c1ff; // 信息
34    --disabled: #4e4e4e; // 失效
35    --link: #a0b5ff; // 链接
36    --hover: #d5e8ff; // hover
37  }
38}

global.less

LESS
1@import "@/styles/colors";
2
3*,
4*::before,
5*::after {
6  box-sizing: border-box;
7  margin: 0;
8  padding: 0;
9}
10
11a {
12  color: @link;
13  text-decoration: none;
14}
15
16a:hover {
17  text-decoration: underline;
18  color: @hover;
19}
20
21ul {
22  list-style: none;
23}
24
25
26body {
27  min-height: 100vh;
28  color: @font;
29  background: @bg;
30  transition: color 0.5s,
31  background-color 0.5s;
32  line-height: 1.6;
33  font-family: Inter,
34  -apple-system,
35  BlinkMacSystemFont,
36  'Segoe UI',
37  Roboto,
38  Oxygen,
39  Ubuntu,
40  Cantarell,
41  'Fira Sans',
42  'Droid Sans',
43  'Helvetica Neue',
44  sans-serif;
45  font-size: 15px;
46  text-rendering: optimizeLegibility;
47  -webkit-font-smoothing: antialiased;
48  -moz-osx-font-smoothing: grayscale;
49}
50
51#app {
52  margin: 0 auto;
53  font-weight: normal;
54}

随后正常在main.js引入global.less即可:

JS
1import '@/styles/global.less'

问题延伸

问题到这里解决了吗?当然,但是还有个需求场景,某些网站会提供用户手动切换深色浅色模式的功能,而css变量可在运行中动态修改的优势就显现了出来

这里就需要js在运行中更改变量啦,具体的实现逻辑如下:

这里采用触发事件的方式进行,在Vue3中,你可以使用mitt进行事件的监听,

SHELL
1npm install mitt

可以直接注册在全局app上,也可以单独延续Vue2的习惯创建Vue实例挂载到上面:

这里的举例是创建Vue实例挂载到上面,新建一个eventBus.js

JS
1import { createApp } from 'vue';
2import mitt from 'mitt';
3
4const emitter = mitt();
5
6const bus = createApp({});
7
8bus.config.globalProperties.$bus = emitter;
9
10export default bus;

这样就可以全局触发和监听事件

接下来创建一个工具函数,在挂载时候添加监听,新建util/useColorScheme.js

JS
1import { onMounted, onUnmounted } from 'vue';
2import bus from "@/eventBus.js";
3/**
4 * 当触发themeChange事件,切换页面的颜色主题,在onMounted,onUnmounted生命周期钩子函数中设置
5 * 修改原理是css变量
6 */
7export function useColorScheme() {
8    const themeChange = (theme) => {
9        console.log('themeChange', theme)
10        if (theme) {
11            // 深色模式
12            document.documentElement.style.setProperty('--primary', '#718dff');
13            document.documentElement.style.setProperty('--bg', '#1d1e21');
14            document.documentElement.style.setProperty('--font', '#ffffff');
15            document.documentElement.style.setProperty('--warning', '#faad14');
16            document.documentElement.style.setProperty('--success', '#a8ff7d');
17            document.documentElement.style.setProperty('--error', '#f5222d');
18            document.documentElement.style.setProperty('--info', '#80c1ff');
19            document.documentElement.style.setProperty('--disabled', '#4e4e4e');
20            document.documentElement.style.setProperty('--link', '#a0b5ff');
21            document.documentElement.style.setProperty('--hover', '#d5e8ff');
22        } else {
23            // 浅色模式
24            document.documentElement.style.setProperty('--primary', '#1890ff');
25            document.documentElement.style.setProperty('--bg', '#ffffff');
26            document.documentElement.style.setProperty('--font', '#333333');
27            document.documentElement.style.setProperty('--warning', '#faad14');
28            document.documentElement.style.setProperty('--success', '#52c41a');
29            document.documentElement.style.setProperty('--error', '#f5222d');
30            document.documentElement.style.setProperty('--info', '#1890ff');
31            document.documentElement.style.setProperty('--disabled', '#bfbfbf');
32            document.documentElement.style.setProperty('--link', '#1890ff');
33            document.documentElement.style.setProperty('--hover', '#001764');
34        }
35    };
36
37    onMounted(() => {
38        bus.config.globalProperties.$bus.on('themeChange', themeChange);
39    });
40
41    onUnmounted(() => {
42        bus.config.globalProperties.$bus.off('themeChange', themeChange);
43    });
44}

这个就是在当触发themeChange事件,切换页面的颜色主题,在onMounted,onUnmounted生命周期钩子函数中设置,工具都准备好啦,就可以在任意地方使用啦

JS
1import {ref, watchEffect} from 'vue';
2import {useColorScheme} from "@/util/useColorScheme.js";
3import bus from "@/eventBus.js";
4
5// 定义深浅模式的变量
6const isDark = ref(false);
7
8// 切换深浅模式
9const handleThemeButton = () => {
10  isDark.value = !isDark.value;
11};
12
13watchEffect(() => {
14  //当深浅模式变量改变时,发出自定义事件
15  bus.config.globalProperties.$bus.emit('themeChange', isDark.value);
16});
17
18useColorScheme();

总结一下

其实有点复杂不太优雅,不过在大的Vue项目中,一定会用到pinia,mitt等工具来进行状态管理,全局事件总线监听的,折腾一下利于积累经验()

回到最开始的问题,原生css变量可用于动态修改,给了实时切换的可能,而less/scss的便捷写法又能大大简化开发,好啦,就到这里呀

效果演示

相关推荐

折腾记录|使用 Nuxt.js 重写个人主页,使用 SSR 优化 SEO ,实现一些期待已久的效果

在 22 年刚创建个人主页的时候,由于我的技术水平不够,只能用一些 wordpress type...

grtsinry43
9/19/2024
24
0
0

使用 BeautifulSoup 配合请求库实现简单的爬虫程序

事情起源于课内的课程实验作业...因为要求要用爬虫,~~不必说课内讲的一言难尽,更不必说就算讲了我也...

grtsinry43
1/7/2025
67
0
0

使用 pf4j-spring 实现插件注入和 api 接口动态注册 | 插件系统构建(上)

哪个男孩不想拥有一个自己的插件系统?(x)话说回来,这个我已经计划好久了,不过一直在学其他的东西,刚...

grtsinry43
1/26/2025
40
0
0

学习分享|跨域解决、安卓开发探索、油猴脚本探索

最近学习的一些内容,包括跨域问题及其解决方案,安卓开发的简单探索,OpenAI的api做了个小插...

grtsinry43
6/10/2024
35
0
0

用一个月的时间写一个自己的博客系统——Grtblog的技术介绍

终于,历时一个多月的开发 ~~bug~~ 和测试,这个目前问题很多很不成熟很难用的系统终于上线了.....

grtsinry43
12/14/2024
117
4
0
COMMENT 7273217729056870400

发表评论

登录之后评论体验更好哦 ~
支持 Markdown 语法 0 / 3000

在风雨飘摇之中

本站已运行了

一路走来,感谢陪伴与支持

愿我们不负热爱,继续前行

全站通知
更新通知