# 概述

深色主题,也就是夜间玩手机必须要的暗黑模式,好像也没啥需要概述的,贴个图好了 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>