目录
🎉应用背景
🎉分析实现思路
🎉CSS实现主题切换
🎉SCSS实现主题切换
🎉结语
🎉应用背景
现在的主流网站中,无论是一些技术文档获取官网,都存在着使用一个switch按钮实现主题背景切换的一个效果,那么这种效果是怎么实现的呢?
🎉分析实现思路
其实我们去查看一些携带了这种功能的官网,可以看到他们是在html标签这种通过控制其类名来实现的主题切换,我们可以参考这种思路来定义我们自己的主题。
🎉CSS实现主题切换
css来实现主题切换,需要用到的是css变量,我们可以根据项目主题定义二套变量,然后通过判断html元素上面的class变量来实现切换主题。
我在style.css文件中定义了二套主题,root下的是默认的主题,dark下的是暗色主题
:root {
--main-bgc: pink;
--main-text-color: black;
}
.dark {
--main-bgc: #000;
--main-text-color: #fff;
}
<template>
<div class="item">
<h1>当前主题为亮色</h1>
<button class="btn1" @click="changeTheme">点击切换主题</button>
</div>
</template>
<style>
.item {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--main-bgc);
color: var(--main-text-color);
}
</style>
当前的效果是这样的,记得一定要引入style.css,可以看到样式已经生效了
通过前面的分析我们还定制了一套主题为dark,我们只需要在html标签上面加上类名即可生效
可以看到当前的效果已经实现了,为什么能实现这样的效果,我们通过观察css变量,可以发现他们定义的变量名都是一样的,那么我们得分析为啥添加类名之后就得听dark类名的呢?
当二个样式重复的时候,浏览器听谁的,得看谁的权重高
:root
.dark
可以发现他们俩的权重是一样高的,但是因为.dark在下面,下面的会覆盖上面的样式,所以现在效果是可以实现的,如果我们将他们俩调换一下位置,效果就无法实现了。
所以这里需要特别的注意位置前后
那么有没有一种方法可以避免这种情况的发生,答案是肯定有的,我们可以实现一个特殊的选择器提高权重
html[data-theme="dark"] {
--main-bgc: #000;
--main-text-color: #fff;
}
这个选择器不是类名选择器 是属性选择器,在html上查找此自定义属性,其使用方法与class类名是一样的,添加自定义属性即可。
像这样也是可以实现效果的
要实现手动切换的方式有很多种,可以是一个按钮,可以是一个switch滑块,通过他们的点击或者什么事件来实现切换即可,但是注意刷新后会回到最初的效果,为了刷新后效果不丢失,我们可以将其当前的属性值存储在本地localStorage等中。 下面的scss实现我会写一个小demo,可以参考。
🎉SCSS实现主题切换
首先在项目中我们得安装sass
pnpm i sass
注意scss是不能在main.js文件中直接引入的,需要在组件的样式中引入或者在配置文件中全局注册,像这样在配置文件中配置一下就可以全局使用scss变量了。
style.scss
// 定义的两套主题
$themes: (
"light": (
bgColor: #fff,
textColor: #000,
btnColor: pink
),
"dark": (
bgColor: #000,
textColor: #fff,
btnColor: blue
)
);
// 定义默认主题
$curTheme: "light";
// 定义混合 useTheme()
@mixin useTheme() {
// 循环生成样式
@each $key, $value in $themes {
$curTheme: $key !global;
.#{$key} & {
@content; // 将 @content 代表的样式插入到生成的选择器中
}
}
}
// 定义函数 getValue($key) 获取当前主题的值
@function getValue($key) {
$themeMap: map-get($themes, $curTheme);
@return map-get($themeMap, $key);
}
$themes: 这个变量保存了两套主题(light 和 dark)。每个主题都被定义为一个映射,包含诸如 bgColor、textColor 和 btnColor 等属性。
@mixin useTheme(): 这个混合用于循环遍历 $themes 中的每个主题,这里循环遍历后,我们后期维护起来就会非常的方便,如果要增加主题,就只要在$themes中添加一个即可。对于每个主题,将 $curTheme 设置为全局变量,并使用它生成一个 CSS 选择器(.light 或 .dark)。@content 中的样式将插入到每个生成的选择器中。
@function getValue($key): 这个函数接受一个属性键作为参数,并返回当前主题中该属性的对应值。
那么我们在组件中可以这样使用,@include来应用即可
<template>
<div class="item">
<h1>当前主题为暗色</h1>
<button class="btn1" @click="changeTheme">点击切换主题</button>
<button class="btn2">测试按钮主题是否切换</button>
</div>
</template>
<style lang="scss" scoped>
.item {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
@include useTheme {
background-color: getValue('bgColor');
color: getValue('textColor');
}
}
.btn1 {
padding: 10px;
border: none;
border-radius: 10px;
@include useTheme {
background-color: getValue('btnColor');
}
}
.btn2 {
padding: 10px;
border: none;
border-radius: 10px;
@include useTheme {
background-color: getValue('btnColor');
}
}
</style>
现在我们只要通过添加class类名即可实现主题切换的效果
其实通过观察上面的代码,按钮部分是有很多重复的代码的,难道每次新加一个元素就得调用一次@include嘛? 这时候为了减少重复代码,我们可以使用@mixin混入实现这样的效果。
@mixin buttonStyles {
padding: 10px;
border: none;
border-radius: 10px;
margin: 10px;
@include useTheme {
background-color: getValue("btnColor");
}
}
在组件中给需要实现主题切换的按钮调用该混入即可
.btn1 {
@include buttonStyles;
}
.btn2 {
@include buttonStyles;
}
实现按钮点击主题切换,其实完成这个效果,上面也已经提供了思路,直接写大家肯定是能写出来的,但是为了效率呢,咱们可以使用一下封装好的库帮我们完成这个效果,(实则是为了偷懒,😄)。
我们可以使用@vueuse/core来帮我们实现切换类名的效果
安装@vueuse/core
npm i @vueuse/core
加上这样一段配置,给按钮绑定好触发事件,启动服务
<div class="item">
<h1>当前主题为{{ isDark ? '暗色' : '亮色' }}</h1>
<button @click="toggle()" class="btn1">点击切换主题</button>
</div>
import { useDark, useToggle } from '@vueuse/core'
const isDark = useDark({
// 存储到localStorage/sessionStorage中的Key 根据自己的需求更改
storageKey: 'useDarkKEY',
// 暗黑class名字
valueDark: 'dark',
// 高亮class名字
valueLight: 'light',
})
const toggle = useToggle(isDark);
启动服务后可以在localStorge中看到这样一个数据
这就是我们之前配置的颜色,@vueuse/core的实现思路也是将当前的主题存储到本地,实现刷新不丢失的效果。
这样之后,我们的点击切换主题效果就完成了。
🎉结语
二种实现主题切换的功能都完成了,可以继续扩展功能,可以做一个switch滑块来切换,中间放入小图标,让其看起来效果更佳。