关于hexo博客的暗色模式

目录

  1. 1. 选用方案
  2. 2. HTML 配置
  3. 3. CSS 配置
    1. 3.1. 颜色配置
    2. 3.2. 按钮配置
    3. 3.3. 贴上代码
  4. 4. JavaScript 配置

这个博客本身选用的 mashiro 主题比较简洁,虽然确实很好用,但是还是有一些小功能需要自己去修改才会比较舒适。考虑到博客本身的配色有些过亮,于是打算自己添加一个简单的暗色模式。

选用方案

博客本身的框架是 hexo,如果用的是 NeXT 主题的话,github 上已经有人做了对应的适配。但是经尝试之后,我发现这个适配并不适合这个主题。于是决定做个简单的暗色模式。查找资料后,发现利用简单的 JS 修改 dom 就可以实现

HTML 配置

想要切换暗色模式,就要弄出一个按钮。按钮用 <div> 标签配合 onclick 就可以实现。

不过,想要给所有的网页都加上这个按钮,就需要修改主题的配置文件。

个人目前在用的主题需要修改 themes\mashiro\layout\layout.ejs 这个文件。在 <body/> 前面加上按钮对应的标签就可以。

CSS 配置

颜色配置

暗色模式的修改比较简单,将原先网页上面的所有元素都反转一下颜色再反转一下色相就可以做到。图片则需要再反转一次颜色。建议反转的时候使用过渡效果。

按钮配置

具体需要改成什么风格的按钮还是取决于个人喜好,不过我这里是选用的圆角矩形按钮。图标使用了精灵图,可以在使用的时候体现上下滑动的动画效果。精灵图的尺寸是 60px×120px。推荐的尺寸是和按钮的宽度相同或是倍数关系。

贴上代码

//于themes\mashiro\source\css\style.styl文件中修改
html
  scroll-padding-top: 80px
  transition: all 0.5s ease-in-out

.dark
  filter: invert(0.9) hue-rotate(180deg)//every thing go black/white

.dark img
  filter: invert(0.9) hue-rotate(180deg)//but images dont

.float-button
  transition: all 0.5s ease-in-out
  position: fixed;//fix the position
  height: 60px;
  width: 60px;
  bottom: 50px;
  right: 50px;
  z-index:999;//always on top
  background: #292929;//not that black
  background-image: url("/the/image/path");
  background-position: 0px 60px;//moon part
  background-size: cover;//extend the display part
  border-radius: 40%;

.dark .float-button
  background-position: 0px 0px;//sun part

这个 styl 文件在生成的时候会编译成对应的 css 文件,具体的过程不是很懂,似乎是 npm 弄的扩展语法,不过也向下兼容 css 语法。

JavaScript 配置

JavaScript 这边就非常简单。直接上代码(从别的地方$^1$偷的思路)

<!-- themes\mashiro\layout\_partial\dark-mode.ejs -->
<script>
  const isDark = localStorage.getItem("isDarkMode");
  if (isDark === "1") {
    document.documentElement.classList.add("dark");
  } else {
    document.documentElement.classList.remove("dark");
  }
</script>
<script src="/scripts/dark_mode.js"></script>

然后在 themes\mashiro\layout\layout.ejs里面修改

<!DOCTYPE html>
<%- partial('_partial/dark-mode') %>
<html>
  <!-- other ejs code -->
</html>

dark-mode.ejs代码如果放到 <html> 后面,就会发生刷新闪屏的问题,原因是脚本会在 html 的 dom 树渲染的同时或完成之后才开始执行。为了避免在切换页面/刷新的时候出现闪屏的问题,dark-mode.ejs 中的 js 脚本需要放到 html 标签的前面。这样在页面还未渲染的时候,脚本就已经给 <html> 标签附好了 class=dark,可以正确的显示背景颜色了。

另外,关于为何是修改 <html> 的 class 而不是在 js 中直接修改 <html> 标签的 css 类样式,是因为 css 样式只有在 dom 渲染完成之后才能被修改。因此单纯 js 修改 css 样式无论如何都不能避免闪屏。

// source\scripts\dark_mode.js
function setDark() {
  localStorage.setItem("isDarkMode", "1");
  document.documentElement.classList.add("dark");
}
// 变白函数
function removeDark() {
  localStorage.setItem("isDarkMode", "0");
  document.documentElement.classList.remove("dark");
}
// switch按钮
function switch_dark_mode() {
  let isDark = localStorage.getItem("isDarkMode");
  if (isDark == "1") {
    removeDark();
  } else {
    setDark();
  }
}

切换的原理是,点击按钮后,先判断目前处于什么模式,然后把切换到的模式储存到 localStorage 里面,最后更新一下 <html> 标签的 class。标签有 dark 会触发对应的 css 规则,切换到黑夜模式。

至于为什么是用 localStorage 储存暗色模式的判断变量,具体可以看看 localStorage、cookies 和 session 的区别。对于这种只会在本地用到的变量,储存在 localStorage 最合适。