谢夏戈 @ xiexiage.com

Dark Mode 暗黑模式

Aug 28, 2023

现在很多网页和App都配备了"暗黑模式",为了让人们在夜晚访问时不那么刺眼。 我非常喜欢“暗黑模式”。事实上即使不在晚上我也用的是“暗黑模式”。

🔬研究

最近在大佬的博客源码的首页 index.html 下看到这个👇

<script>
  ;(function () {
    const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
    const setting = localStorage.getItem('vueuse-color-scheme') || 'auto'
    if (setting === 'dark' || (prefersDark && setting !== 'light'))
      document.documentElement.classList.toggle('dark', true)
  })()
</script>

这是一个判断网页浏览器是否为“暗黑模式”的代码

逻辑是

  1. window.matchMedia('(prefers-color-scheme: dark)').matches 判断浏览器是否为暗黑模式,返回Ture/False给到变量 prefersDark
  2. localStorage.getItem('vueuse-color-scheme') || 'auto' 获取浏览器本地存储的主题模式记录,返回给变量 setting
  3. 接下来判断
    1. 如果【setting】是黑的,那么网页也黑。
    2. 如果浏览器黑且【setting】不是白,那么网页也黑。

判断完毕后,只是在 <html> 这个标签多加上一个 “dark” 的类名

<html class="dark">
  //....
</html>

后续再说如何根据这个类名来变黑。

知识点一:立即执行函数

什么是立即执行函数?JS立即执行函数模式是一种语法,可以让你的函数在定义后立即被执行,这种模式本质上就是函数表达式(命名的或者匿名的),在创建后立即执行。

(funcion(){
    //...
})()

知识点二:matchMedia()

matchMedia() 方法的值可以是任何一个 CSS @media 规则 的特性, 如 min-height, min-width, orientation 等。

用以下代码可以查询用户的浏览器是否“暗黑模式” 如是,prefersDark 为 True。

const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches

可以在浏览器的控制台输入以下这句👇

window.matchMedia('(prefers-color-scheme: dark)')

知识点三:localStorage

localStorage.setItem(键,值) 就是把(键值对)长期保存在浏览器存储里。 localStorage.getItem(键) 就是把值取出来。

// 存👇
localStorage.setItem('保存的钥匙', '保存的内容')

// 取👇
const key = localStorage.getItem('保存的钥匙')
console.log(key)// 保存的内容

在浏览器按下F12,可以在控制台上的 “应用程序” -> “本地存储” 上看到。

知识点四:document

document.documentElement

简单理解用它来获取 根元素 - <html> 标签

classList

classList 属性返回元素的类名。用于添加,移除及切换 CSS 类。

  • add(class1,class2) 添加类名
  • contains(class) 判断是否存在
  • item(index) 返回索引值对应类名
  • remove(class1,class2) 移除类名
  • toggle(class,true/false) 切换类名
    • 参数1:类名,如果存在则移除且返回false,不存在则添加返回true。
    • 参数2:可选,用于设置元素是否强制添加或移除类,不管该类名是否存在。

所以我们使用 👇 就可以在<html>上动态添加 dark 类名了。

document.documentElement.classList.toggle('dark', true)

🌗 配置 CSS

前面已经介绍了,如何判断浏览器是否处于“暗黑模式”,并且判断完后在根元素 <html> 挂上 dark 这个类名

接下来就说说如何根据类目来切换颜色。

body {
  background-color: white;
}
html.dark body {
  background-color: black;
}
.text {
  color: black;
}
html.dark .text {
  color: white;
}
  1. 定义【字】默认颜色为黑色,【背景】默认白色。
  2. 当在类名“dark”下的则为【字】白色,【背景】黑色。

👇 写一个测试用的 html 文件

<!doctype html>
<html>
  <head>
    <title>测试暗黑模式</title>
    <script>
      ;(function () {
        const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
        const setting = localStorage.getItem('vueuse-color-scheme') || 'auto'
        if (setting === 'dark' || (prefersDark && setting !== 'light'))
          document.documentElement.classList.toggle('dark')
      })()

      function toggleDark() {
        document.documentElement.classList.toggle('dark')
      }
    </script>
    <style>
      body {
        background-color: white;
      }
      html.dark body {
        background-color: black;
      }
      .text {
        color: black;
      }
      html.dark .text {
        color: white;
      }
    </style>
  </head>
  <body>
    <div class="text">暗黑模式</div>
    <button onclick="toggleDark()">切换</button>
  </body>
</html>

到这一步,其实已经可以切换暗黑模式了。

🎉优化

css变量

像上面,如果每一个class 都要设置一个“光亮”的值,一个“暗黑”的值,那就太麻烦了。

我们可以先利用CSS变量,预先设置一些变量名,这些变量名要符合所用的属性,比如叫【text】的就是字,【bg】就是背景,然后再给这个变量在光亮模式下选一个颜色,在暗黑模式下选一个颜色。👇

/* 这里负责定义颜色
在其他地方在这里调颜色用 */
html {
  --text-color: #333; /* 黑字 */
  --bg-color: #fff; /* 白底 */
}

html.dark {
  --text-color: #fff; /* 白字 */
  --bg-color: #333; /* 黑底 */
}

选完以后,我们再把这些变量运用到实际上的配色中。👇

/* 在其他地方在这里调颜色用👇 */
body {
  background-color: var(--bg-color);
}

.text {
  color: var(--text-color);
}

当你需要设置的类目非常多时,这个方法就很方便了。

dark & auto & light

我们可以设置三个按钮让用于来选择当前是黑、白、或是自动。

【设置三个按钮控制也不错,以下这步不是必须的】

像本博客一样一个按钮实现三种情况👇

当用户点击按钮时,根据不同情况改变存储在本地的主题信息。

  1. 浏览器黑,页白,点 -> 存 auto
  2. 浏览器白,页黑,点 -> 存 auto
  3. 浏览器黑,页黑,点 -> 存 light
  4. 浏览器白,页白,点 -> 存 dark
function toggleDark() {
  // 判断浏览器是否为黑
  const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches

  // 判断页面是否为黑
  const pageDark = document.documentElement.classList.contains('dark')

  // 改变页面的颜色,并且保存到浏览器中。
  document.documentElement.classList.toggle('dark')
  if (prefersDark !== pageDark) {
    localStorage.setItem('prefers-color-scheme', 'auto')
  }
  else {
    const color = pageDark ? 'light' : 'dark'
    localStorage.setItem('prefers-color-scheme', color)
  }
}

监听浏览器颜色的变换

通常浏览器在变色的时候,网页是不会跟着变化的,除非你按刷新按钮,或者去监听👇

window.addEventListener('perfers-color-scheme-change', () => {
  // do something
})

更好的办法👇

在 MediaQueryList 上添加一个带有回调的事件监听器由 Window.matchMedia() 返回:

const darkModePreference = window.matchMedia('(prefers-color-scheme: dark)')

darkModePreference.addEventListener('change', () => localStorage.getItem('prefers-color-scheme') === 'auto' && activateDarkMode())

function activateDarkMode() {
  document.documentElement.classList.toggle('dark')
}

监听浏览器的黑白变换,当存储的值是auto才会随着变化!

VueUse 插件

【以下这步也不是必须的,只是用插件更加方便】

useDark & useToggle

import { useDark, useToggle } from '@vueuse/core'

const isDark = useDark()
const toggleDark = useToggle(isDark) // 这个会
  • useDark() 判断当前网页(html标签上有没有dark类)
  • useToggle() 切换html.dark

usePreferredDark

import { usePreferredDark } from '@vueuse/core'

const isDark = usePreferredDark()
//
  • usePreferredDark() 判断当前浏览器是否黑色

在Vue项目中,安装VueUse这个插件然后配置好以上。就可以更加方便的调用我们的暗黑模式啦!

2023-PRESENT © 谢夏戈