谢夏戈 @ 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('保存的钥匙','保存的内容')

//取👇
let 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 {
        let 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 © 谢夏戈