Hugo 实现深浅主题的切换
实现深浅主题切换的方法不只一种,这里记录我所使用的一种。
生成深浅主题对应的 css 文件
配置入口文件
我的博客通过 sass 构建样式,可以将写好的 scss 文件组合生成一个 css 文件。
而组合成为 css 文件需要一个入口文件,在入口文件里指定主题。
比如我在 assets/scss/colors
中指定了两种色调 dark.scss
和 light.scss
, 那么可以在对应的入口文件中 import:
1// assets/scss/styles_dark.scss
2@import "colors/dark";
3
4@import "variables";
5@import "util";
6@import "mixins";
7// ...
1// assets/scss/styles_light.scss
2@import "colors/light";
3
4@import "variables";
5@import "util";
6@import "mixins";
7// ...
这样,就有了 styles_dark.scss
和 styles_light.scss
这两个入口文件。
通过 hugo 生成目标文件
接着,在 <head></head>
中指定入口文件进行操作:
1<!-- head.html -->
2{{ $opts := (dict "targetPath" "css/styles_dark.css" "outputStyle" "compressed") }}
3{{ $styles_dark := resources.Get "scss/styles_dark.scss" | resources.ExecuteAsTemplate "scss/styles_dark.scss" . | resources.ToCSS $opts }}
4
5{{ $opts := (dict "targetPath" "css/styles_light.css" "outputStyle" "compressed") }}
6{{ $styles_light := resources.Get "scss/styles_light.scss" | resources.ExecuteAsTemplate "scss/styles_light.scss" . | resources.ToCSS $opts }}
这里通过 hugo 操作之后,即可生成深浅主题对应的两个 css 文件,位置在 css
目录下。
接着,将地址赋值到 $dark
和 $light
中:
1<!-- head.html -->
2{{ $dark := $styles_dark.Permalink }}
3{{ $light := $styles_light.Permalink }}
保存地址变量到隐藏域
接着,通过隐藏域保存这两个地址以便后续使用:
1<!-- head.html -->
2<input value="{{ $dark }}" id="styles-dark" style="display: none;" />
3<input value="{{ $light }}" id="styles-light" style="display: none;" />
设定初始主题
一开始的主题应由网站发布者指定,读取 config 文件中的变量即可:
1<!-- head.html -->
2{{ if eq .Site.Params.colortheme "dark" }}
3<input value="dark" id="theme-current" style="display: none;" />
4<link id="theme-css" rel="stylesheet" href="{{ $dark }}" />
5{{ else if eq .Site.Params.colortheme "light" }}
6<input value="light" id="theme-current" style="display: none;" />
7<link id="theme-css" rel="stylesheet" href="{{ $light }}" />
8{{ end }}
这里又配置了一个隐藏域,用于后续确认和修改主题。
通过 js 动态设置主题
加载主题
加载步骤如下:
- 读取用户 localStorage 中保存的主题变量;
- 读取当前主题隐藏域值;
- 读取 css 文件地址隐藏域值;
- 修改主题 css 标签中的地址以加载主题。
其中,用户 localStorage 中保存的主题变量是在用户首次切换主题之后生成的,在下一步中进行介绍。
1// theme.js
2function setTheme(theme) {
3 let stylesDark = document.getElementById("styles-dark").value;
4 let stylesLight = document.getElementById("styles-light").value;
5 let themeCss = document.getElementById("theme-css");
6 let setThemeStyle = theme === "dark" ? stylesDark : stylesLight;
7 themeCss.setAttribute("href", setThemeStyle);
8}
9
10function getCurrentTheme() {
11 // user theme
12 let userTheme = localStorage.getItem("userTheme");
13 let userThemeExpire = localStorage.getItem("userThemeExpire");
14 if (
15 userTheme !== null &&
16 userTheme != "" &&
17 userThemeExpire !== null &&
18 userThemeExpire > new Date().getTime()
19 ) {
20 document.getElementById("theme-current").setAttribute("value", userTheme);
21 }
22 return document.getElementById("theme-current").value;
23}
24
25setTheme(getCurrentTheme());
修改主题
每次修改主题都需要修改当前主题隐藏域中的值,并且在用户 localStorage 中存储或修改用户选择的主题,而设置超时时间则是一个可选项:
1// theme.js
2function changeTheme(theme) {
3 setTheme(theme);
4 document.getElementById("theme-current").setAttribute("value", theme);
5 localStorage.setItem("userTheme", theme);
6 // 设置超时时间: 24 小时
7 let expire = new Date().getTime() + 1000 * 3600 * 24;
8 localStorage.setItem("userThemeExpire", expire);
9}
将脚本放在合适的位置
theme.js
脚本需要在脚本所用到的元素加载完毕后,才能正确执行,否则将导致主题修改失败。
由于浏览器加载页面是从上到下按顺序执行的,所以只要不将脚本放在任一依赖项的上方即可让脚本顺利执行。
绑定事件到按钮
脚本编写完毕后,则将其绑定到某个按钮上即可,示例如下:
定义一个按钮:
1<a class="icon" id="theme-change"><i class="fas fa-adjust"></i></a>
绑定事件:
1// main.js
2// button: change theme
3$("#theme-change").click(function () {
4 let newTheme;
5 if (getCurrentTheme() === "light") {
6 newTheme = "dark";
7 } else {
8 newTheme = "light";
9 }
10 changeTheme(newTheme);
11});
抚摸主页上的猫猫,美妙的事情会发生哦~