# 概述
深色主题,也就是夜间玩手机必须要的暗黑模式,好像也没啥需要概述的,贴个图好了 qwq
<img src="https://cdn.jsdelivr.net/gh/ERUIHNIYHBKBNF/picapica@main/frontend/2020020601.webp" width="500px">
现成的很多 ui 库比如 naiveUI,vuetify,antdesign 这种都支持暗黑模式来着。
暗黑模式要玩真的,就要动用很多 css 变量,还要慢慢调整配色很复杂的样子,所以这边提供一个简单粗暴的办法,虽然不能做到细节调整,但日常的大部分网站效果都还不错的。
观察深色模式下的网站,不难发现基本都是黑白反色,其它颜色相对变暗,具体实现之前先看两个颜色概念。
# 前置知识
# RGB 色环
RGB 色环长这个样子,三原色是红绿蓝,其它颜色都由这三种颜色调配出来的,大概不用细说 qwq
<img src="https://cdn.jsdelivr.net/gh/ERUIHNIYHBKBNF/picapica@main/frontend/2022020602.webp" width="300px">
# CSS 中的滤镜
css 的滤镜 filter 就是用来操作某个元素的色彩的。
这里会用到两个滤镜分别是 invert()
和 hue-rotate()
invert()
:取值范围 0~1,取值为 1 时,色环上任意一点的颜色会变成相对于圆心对称的点的颜色。hue-rotete()
:以角度为单位,进行色相旋转。
直接上图更直观一些:
实际操作:https://codepen.io/ERUIHNIYHBKBNF/pen/Rwjoxwo
<img src="https://cdn.jsdelivr.net/gh/ERUIHNIYHBKBNF/picapica@main/frontend/2020020603.webp">
图一原图,图二使用 invert(1)
进行处理,图三使用 hue-rotete(180deg)
进行处理,图四使用两种滤镜同时处理。
对比图一和图四,深色模式的效果也就显而易见了,对应颜色都看上去比较舒服了。
# 具体实现
简单直接:
// 对根元素添加 theme-dark 类名,对需要保留原色彩的元素添加 darkmode-ignore 类名即可 | |
.theme-dark { | |
filter: invert(1) hue-rotate(180deg); | |
img, video, .darkmode-ignore{ | |
filter: invert(1) hue-rotate(180deg); | |
} | |
} |
我们先对网站整体,也就是根元素使用一次这样的深色主题滤镜,然后你会发现有图片的网站变成了阴间模式。
这样会有一个问题是,图片视频这样的元素在深色模式下也不应该被使用滤镜。不难发现其实 invert(1) hue-rotate(180deg)
是非常对称的一个操作,那么我们对图片视频再应用一次这样的滤镜就可以了。
当然,额外设定一个名为 darkmode-ignore
的类,用于其它一些不想被深色模式影响的元素。
再丢一个在 vue 项目里是实际应用好了:
当然我们可以把这个功能抽象出一个组件来,theme-container.vue:
<template>
<!--这里有个特判,就是在路由为About的页面始终不应用深色模式-->
<div id="app" :class="$route.name === 'About' || theme">
<slot></slot>
</div>
</template>
<script>
export default {
computed: {
theme() {
return this.$store.state.theme;
},
},
}
</script>
<style lang="scss">
.theme-dark {
filter: invert(1) hue-rotate(180deg);
img, video, .darkmode-ignore{
filter: invert(1) hue-rotate(180deg);
}
}
</style>
在 vuex 中写法也并不麻烦,记得存一下 localStorage 确保刷新网页之后主题不变:
{ | |
state: { | |
theme: localStorage.getItem('wangwang-theme') || 'theme-dark', | |
}, | |
mutations: { | |
changeTheme() { | |
this.state.theme = this.state.theme === 'theme-dark' ? 'theme-light' : 'theme-dark'; | |
localStorage.setItem('wangwang-theme', this.state.theme); | |
} | |
}, | |
} |
然后在 App.vue 中直接套在最根部那个 <router-view/>
外面即可:
<template>
<div id="app">
<theme-container>
<router-view/>
</theme-container>
</div>
</template>