目 录CONTENT

文章目录

halo 博客深度定制与美化教程

华灯
2024-04-15 / 0 评论 / 0 点赞 / 24 阅读 / 38866 字

halo 博客是一个动态的博客框架,其实现了应用与用户配置相分离。在服务器上部署时,即使是小白也能够通过一条命令快速部署成功,而主题等前端页面,则是可以直接脱离后端进行自行定制,而前端也可以调用后端的 API 实现一些复杂的功能。

写在前面

halo 博客有相对活跃的社区,并且在社区用户提出问题的解决速度上较快,如果是主题样式的某些问题,也可以直接去对应主题 Github 的 Issue 区向作者提问,这显得使用 halo 博客的用户群体变得相对『封闭』,因为在搜索引擎中几乎找不到相应的教程,同时会感觉远远不如之前使用 Hexo 博客时在能够在搜索引擎中搜索到一堆美化教程来的方便。本教程主要面向具有一定前端基础的读者提供的一些通用解决方案或思路,因为 halo 的主题不同,其目录文件结构的差异也会比较大,虽然本质上没有多大区别,但是对于前端小白用户来说就比较棘手。

本来并不打算花时间写这一个教程,因为这个博客本身就是综合了多个主题的样式融合而成,当前样式还比较混乱且没有进行整理;而且因为 halo 主题的特点,一些前端小白对 CSS 和 JS 不是很熟练,更不用说去理解清楚主题目录下各个文件代表的含义,这对教程的通用性来说是一个很大的阻碍。但是为了增加一波博客的浏览量,还是决定出卖一下自己的『节操』写一个尽量通俗易懂并且能够便于修改的样式教程💀。本篇博客预计花费的心血和时间较多,如果您觉得对您有帮助,不妨在文章末尾处请我喝杯 Coffee,或者在评论区留个言给我一点继续写下去的动力吧😩!

预备基础知识

在打算对自己的主题进行美化前,你所需要掌握的最基础的知识就是需要知道你的主题全局模板文件的位置,以及如何进行修改。比如,最重要的 CSS 样式文件的位置,HanShan v1.4.4 版本的主题 CSS 文件位置在下面小节有提及到,其它主题需要自己在主题目录中寻找。

学会使用浏览器 F12 定位目标样式

能看到这篇博客的,基本都是使用的主流浏览器(Chrome、Firefox、Safari 等)之一,本站为什么只支持这些主流浏览器?最主要的原因是这些浏览器的内核兼容性都非常强大,其它的一些小众浏览器甚至都不能完全支持一些 CSS 规范和浏览器标准。

关于如何使用 Chrome 浏览器定位并在本地调试 CSS 样式见 Chrome 的官方文档,可以帮助你较为快速的入门。

学会分析页面加载速度慢的原因

这个其实是非必要项,这个小节放到这里的目的是为了提醒读者不要一味地追求博客各种炫酷的动画效果,过多的插件加载会导致页面渲染速度降低,从而造成网页打开速度超级慢,对于浏览你博客的读者来说则是一个非常不愉快的等待过程,甚至在超过 3 秒的转圈圈后仍未加载出来会直接点击右上角。

使用主流浏览器都可以查看具体是什么资源拖了页面整体加载的速度,以 Chrome 浏览器为例,在博客首页用 F12 打开控制台,进入 NetWork 项,勾选 Disable cache,表示禁止浏览器缓存资源到本地,这样可以看到首次加载所有资源的耗时情况。

image.png

然后按 F5 刷新页面,点击 Time 进行排序,就可以看到每个资源的加载耗时情况,一般来说如字体这类比较大的资源加载慢是很正常的,而一些 js 如果加载时间靠后则说明该资源可能需要 CDN 加速或者更换加载源。

img

由于网络差异,上面所述页面资源加载时间并非在所有地区都相同,但是也可以提供一个非常有价值的参考。以下使用本美化教程增加 js 插件时,不妨多注意一下页面资源的加载速度。

主题美化准备工作

虽然 halo 在后台提供了直接编辑主题文件的功能,但是后台自带的页面编辑器远远不能满足主题美化的需求。所以我们需要使用一些功能强大的 IDE 完成,这里我推荐的是宇宙第一 IDE : Vscode (我 IDEA 第一个不服😂),我自己用的也一直是这个编辑器,其它的如 HBuilderX 之类的都远没有这个好用。当然,如果是个正经的程序员的话,Vscode 应该不可能没用过,但是为了照顾没使用过的小白,我尽量将 Vscode 的使用方法说明清楚。

Vscode 的安装及使用

首先需要明白一个需求,在这里使用 Vscode 的目的是方便使用 ssh 远程连接服务器并同步修改主题上的文件使之生效,这样可以避免在本地修改后还要打成 zip 包上传(反正我自己是直接修改服务器端的文件)到后台,同时 Vscode 无敌丰富的插件系统为我们编辑主题文件提供了绝佳的效率和便利。如果你是仅在本地修改主题,然后再将修改后主题的 zip 压缩包上传到服务器端 halo 的后台中,那么也可以不使用 Vscode,其它的本地编辑器一样比较方便。

Vscode 的安装

Vscode 下载地址如下:

Vscode 下载地址

Vscode 界面如下,在下载完成并打开后,可以点击左侧最下方的扩展按钮安装一些主题编辑的插件。

VsCode界面

需要安装的插件如下(插件非常丰富,以下只涉及美化主题所必需的):

  1. FreeMarker:因为 halo 主题的模板文件都是 ftl 结尾的,也就是用的 FreeMarker 模板引擎,所以需要安装对应的插件支持。
  2. JS & CSS Minifier:JS 和 CSS 压缩插件,其它如编译 Sass 或者 Less 的插件需要自己自行安装。
  3. Chinese (Simplied) Language:界面中文支持,这个随自己喜好了。
  4. Remote - Containers:远程服务支持。
  5. Remote -SSH:用于 ssh 连接远程服务器。
  6. Remote - SSH: Editing Configuration Files:远程 ssh 连接服务器的配置文件。
  7. Remote Development:同样,都是支持远程连接的一些插件。
  8. ...

Vscode 的使用

如果你是准备在本地编辑主题文件,那么直接在 Vscode 中打开本地主题文件夹即可,halo 博客在本地安装主题的目录一般在 c:/用户/user/.halo/templates/themes 下面,远程服务器上也是类似的位置,以下是连接远程服务器上的配置文件设置。

在确定上述 Remote 相关的插件安装好后,点击 F1 打开万能搜索,输入 ssh,选择第一个 Remote - SSH: Editing Configuration Files 并单击。

img

然后同样选择下面第一个,默认的 .ssh\config 文件。

image.png

在这个配置文件中编辑信息,其中 Host 后面填写服务器别名,HostName 填写 ip 地址,User 就是登录服务器的用户名。其中后面填写的字段与前面的属性名之间需要间隔一个空格。

# Read more about SSH config files: https://linux.die.net/man/5/ssh_config Host 腾讯云服务器 HostName 213.78.99.216 User ubuntu 

这个配置文件中也可以填写多个主机,只要这样并列写就可以。

Host 腾讯云服务器 HostName 213.78.99.216 User ubuntu  Host 阿里云服务器 HostName 35.26.44.157 User root 

然后我们点击左侧到数第二个按钮,就可以看到我们配置的远程连接信息了,鼠标放置后点击文件夹图标就可以打开连接窗口,输入服务器的登录密码就可以进入编辑了。

img

以上就是 Vscode 连接远程服务器上并编辑文件的基本使用方法,接下来需要了解的是 halo 博客主题文件的目录代表的涵义。

了解 halo 博客主题的目录结构

我们只有在了解主题目录代表的含义之后,才可以进行自己的二次开发或者修改。首先看 halo 官方给出的示例:

├── module // 公共模板目录 │ ├── comment.ftl // 比如:评论模板 │ ├── layout.ftl // 比如:布局模板 ├── source // 静态资源目录 │ ├── css // 样式目录 │ ├── images // 图片目录 │ ├── js // JS 脚本目录 │ └── plugins // 前端库目录 ├── index.ftl // 首页 ├── post.ftl // 文章页 ├── post_xxx.ftl // 自定义文章模板,如:post_diary.ftl。可在后台发布文章时选择。 ├── sheet.ftl // 自定义页面 ├── sheet_xxx.ftl // 自定义模板,如:sheet_search.ftl、sheet_author.ftl。可在后台发布页面时选择。 ├── archives.ftl // 归档页 ├── categories.ftl // 分类目录页 ├── category.ftl // 单个分类的所属文章页 ├── tags.ftl // 标签页面 ├── tag.ftl // 单个标签的所属文章页 ├── search.ftl // 搜索结果页 ├── links.ftl // 内置页面:友情链接 ├── photos.ftl // 内置页面:图库 ├── journals.ftl // 内置页面:日志 ├── 404.ftl // 404 页 ├── 500.ftl // 500 页 ├── README.md // README,一般用于主题介绍或说明 ├── screenshot.png // 主题预览图 ├── settings.yaml // 主题选项配置文件 └── theme.yaml // 主题描述文件 

这个示例给的已经非常完整的,虽然主题开发者并非一定要按照这个目录结构进行主题开发,且结构与这个示例不一定相同,但是本质上是大同小异。在了解了目录结构后,我们就可以开始二次开发了。

主题基本美化教程

这个小节主要给出一些常用的基本美化方法,且围绕的是 CSS 进行修改,尽量减少复杂的 js 和模板文件的修改。以下美化方法均以 HanShan 主题为例,这是由于 HanShan 主题当前使用用户最多,其它主题有不懂如何修改的也可以在评论区告知,我将尽量提供解决办法。以 HanShan 1.4.4 版本为例,主题的 CSS 样式文件在 styles 目录下面,其结构如下:

├── styles // 样式目录 │ ├── _blocks // 子模块样式 │ ├── archives.less // 归档页  │ ├── header.less // 页面头部 │ ├── journals.less // 日志页 │ ├── links.less // 友链页 │ ├── night-mode.less // 深色模式样式  │ ├── photos.less // 相册页 │ ├── post.less // 文章页 │ ├── post-card.less // 首页文章卡片展示样式 │ ├── search.less // 搜索页 │ ├── tags.less // 标签页 │ ├── tocbot.less // 文章目录样式 │ ├── _core // 核心 │ ├── style.less // 文章通用样式 │ ├── _plugin // 插件样式目录 │ ├── hljs.less // 代码块高亮样式 │ └── font.less // 字体引入样式 │ └── main.css // 上述所有 less 子模块编译后生成的未压缩的 css 文件 │ └── main.less // 编译 less 子模块样式 │ └── main.css.map // less 映射 │ └── main.min.css // 最终页面引入的 css 样式,也即是最终的样式文件!!! 

上述结构应该已经比较清晰,接下来简单的介绍以下 Less 和 CSS 之间的关系。我们知道,CSS 是构成前端页面的基本元素之一,也是浏览器能够理解并渲染的基本元素,但是如果 CSS 样式很复杂的时候,纯靠手写 CSS 是一件很麻烦的事,因此有了 Less,它是一门扩展性的 CSS 预处理语言,在 CSS 基础上增加了函数、变量等一些便于写代码并且易于读懂的功能,而 CSS 可以由 Less 编辑而成。也就是上面的 main.css 是由 main.less 通过编译所有子模块的 less 文件而成,main.min.css则是由 main.css 压缩而成。以上 Less 编译和 CSS 压缩都可以交由 Vscode 插件完成,还是很方便的😘 。

Less 快速入门

害,说了这么久还没进入正题,主要是需要的预备知识较多,我也尽量不厌其烦的概述了一些基本概念,接下来我们就直奔主题。

以 HanShan 主题为例,以下所指的全局 CSS 文件位置均简单的代指 main.css,你需要将该 CSS 压缩为 main.min.css 并覆盖之前的 main.min.css 即可在页面生效;同时,全局的 js 模板文件简单的代指 macro.ftl 以及 script.ftl,这两个文件作用类似,可以在 <script></script> 标签内增加 js 代码并自动应用到所有页面。

1.美化有序标签

示例及效果如下:

  1. 这里是第一个标签
  2. 这里是第二个标签
  3. 这里是第三个标签

这个样式在我之前的 Hexo 博客中就已经使用了,我们在 md 中书写的有序标签,会在渲染成 html 时自动转换为 <ol><li></li></ol> 标签,所以只需要在上述 _core/style.less 中增加如下样式,或者直接在 main.css 中增加如下代码:

ol{ list-style: none; /* 隐藏原来的 1. 2. 等序号样式 */ counter-reset: ol-li; }  ol li:before { display: block; float: left; width: 19px; /* 宽度和高度需要根据自己的字体大小进行修正 */ height: 19px; line-height: 19px; margin: 4px 12px 0 0; color: #fff; font-size: 16px; font-weight: 300; font-style: normal; background-color: #49b1f5; border-radius: 50%; text-align: center; content: counter(ol-li); counter-increment: ol-li; transition: all .5s; } 

请注意,上面的修改作用于博客所有页面的有序标签,而一些其它有序标签的使用会受到该样式的影响,因此正常情况下,需要对该样式限定范围,比如限制在文章页面中生效。以 HanShan 主题为例,文章的 HTML 内容渲染都是在一个 class 或者 id 为 post-content 的 DOM 元素中的,如以下所示:

<div class="post-content" id="post-content"> <!-- 文章的内容 --> ${post.content} </div> 

因此你需要根据你的主题找到这个父 DOM 元素,然后增加该 class 作用域限制,以下修改内容均同理,不再赘述。

.post-content ol{ ... }  .post-content ol li:before { display: block; ... } 

然后通过编译 less 并压缩 main.css 得到 main.min.css 后在页面中查看样式效果。更进一步的,还可以设置鼠标放置在有序标签文本上旋转,继续增加如下样式:

ol li:hover::before{ transform: rotate(360deg); -webkit-transform: rotate(360deg); -moz-transform: rotate(360deg); -o-transform: rotate(360deg); -ms-transform: rotate(360deg); } 

2.美化分隔符 hr 样式

效果可以在本站点任意分割线处看到,鼠标放置后会滑动。

img

_core/style.less 或者 main.css 中增加样式如下,注意需要引入 FontAwesome v4.7.0 版本的 CSS 文件。HanShan 主题已经默认引入了,可以直接使用。引入链接如下:

<link type='text/css' rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" media='all'/> 
/* 美化 hr 样式 */ hr { position: relative; margin: 2rem auto; width: calc(100% - 4px); border: 2px dashed #a4d8fa; background: #fff; }  hr { box-sizing: content-box; height: 0; overflow: visible; }  hr:before { position: absolute; top: -10px; left: 5%; z-index: 1; color: #49b1f5; content: '\f0c4'; font: normal normal normal 14px/1 FontAwesome; font-size: 20px; -webkit-transition: all 1s ease-in-out; -moz-transition: all 1s ease-in-out; -o-transition: all 1s ease-in-out; -ms-transition: all 1s ease-in-out; transition: all 1s ease-in-out; }  hr:hover::before{ left: 95%; } 

3.常用的 note 标签

这个是我从 Hexo 博客中带过来的,效果如下:

这里是 info 标签样式

这里是不带符号的 info 标签样式

这里是 primary 标签样式

这里是不带符号的 primary 标签样式

这里是 warning 标签样式

这里是不带符号的 warning 标签样式

这里是 danger 标签样式

这里是不带符号的 danger 标签样式

仍然在 _core/style.less 或者 main.css 中添加样式如下,注意需要引入 FontAwesome v4.7.0 版本的 CSS 文件才会使图标生效。HanShan 主题已经默认引入了,可以直接使用。引入链接如下:

<link type='text/css' rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" media='all'/> 
/* note 公共样式 */ .note { position: relative; padding: 15px; margin-top: 10px; margin-bottom: 10px; border: initial; border-left: 3px solid #eee; background-color: #f9f9f9; border-radius: 3px; font-size: var(--content-font-size); }  .note:not(.no-icon):before { position: absolute; font-family: FontAwesome; font-size: larger; top: 11px; left: 15px; }  .note:not(.no-icon) { padding-left: 45px; }  .note.info { background-color: #eef7fa; border-left-color: #428bca; }  .note.info:not(.no-icon):before { content: "\f05a"; color: #428bca; }  .note.warning { background-color: #fdf8ea; border-left-color: #f0ad4e; }  .note.warning:not(.no-icon):before { content: "\f06a"; color: #f0ad4e; }  .note.primary { background-color: #f5f0fa; border-left-color: #6f42c1; }  .note.primary:not(.no-icon):before { content: "\f055"; color: #6f42c1; }  .note.danger { background-color: #fcf1f2; border-left-color: #d9534f; }  .note.danger:not(.no-icon):before { content: "\f056"; color: #d9534f; } 

在写 md 时使用方式如下,以 html 标签方式引入:

<div class="note info">这里是 info 标签样式</div>  <div class="note info no-icon">这里是不带符号的 info 标签样式</div>  <div class="note primary">这里是 primary 标签样式</div>  <div class="note primary no-icon">这里是不带符号的 primary 标签样式</div>  <div class="note warning">这里是 warning 标签样式</div>  <div class="note warning no-icon">这里是不带符号的 warning 标签样式</div>  <div class="note danger">这里是 danger 标签样式</div>  <div class="note danger no-icon">这里是不带符号的 danger 标签样式</div> 

4.给文章增加内容过期提示信息

以下根据上述的 note 标签样式增加而成,效果如下:

image.png

其实这个功能可以交由 js 完成,但是由于开启了 Pjax 后,页面的局部 js 极容易失效,所以我自行使用 FreeMarker 的语法完成,以下对任意主题都通用。

以 HanShan 主题为例(其它主题都可,找对位置就行),在 post.ftl 页面中,定位到此处渲染文章内容处:

<div class="article-body"> <div class="post-content article-content" id="post-content"> ${post.formatContent!} 

增加代码如下:

<div class="post-content article-content" id="post-content"> + <#if (.now?long-86400000*60)?number_to_datetime gte post.editTime?datetime> + <div class='note warning'>请注意,本文编写于&nbsp; ${(((.now?long) - (post.createTime?long)) / 86400000)?int} &nbsp;天前,最后编辑于&nbsp; ${(((.now?long) - (post.editTime?long)) / 86400000)?int} &nbsp;天前,内容可能已经不具有时效性,请谨慎参考。</div><hr/> + </#if> ${post.formatContent!} 

其中默认的效果是当前时间距离该文章的最后修改时间大于 60 天时,才会出现这个提示信息,如果你需要修改 60 天这个数值,可以在代码中修改以下的 60 数值,即代表多少天。

<#if (.now?long-86400000*60)?number_to_datetime gte post.editTime?datetime> 

5.模仿知乎卡片样式的超链接

效果和原文如下:

博客模仿知乎卡片样式链接

为了适配 halo 博客的主题,以下再进一步解释,如果你使用的是 HanShan 主题,可以直接在 macro.ftl 底部增加引入如下 js 文件,其它主题类似,只需要将该 js 文件引入到页面全局的模板文件中即可。

<script src="https://cdn.jsdelivr.net/gh/Sanarous/files@master/js/linkcard.js"></script> 

为了使其与 Pjax 兼容,还需要在 module/script.ftl 中如下代码块中增加 LinkCard 的 js 代码。当然也可以更简便的引入,但是需要修改其它的 js 文件,为了通俗,直接就写在了 script.ftl 中方便读者使用。

document.addEventListener('pjax:complete', function (e) { NProgress.done();  //增加 LinkCard 的 js 代码 var LinkCards=document.getElementsByClassName('LinkCard'); if(LinkCards.length != 0){ var LinkCard=LinkCards[0]; var link=LinkCard.href; var title=LinkCard.innerText; LinkCard.innerHTML="<style type=text/css>.LinkCard,.LinkCard:hover{text-decoration:none;border:none!important;color:inherit!important}.LinkCard{position:relative;display:block;margin:1em auto;width:390px;box-sizing:border-box;border-radius:12px;max-width:100%;overflow:hidden;color:inherit;text-decoration:none}.ztext{word-break:break-word;line-height:1.6}.LinkCard-backdrop{position:absolute;top:0;left:0;right:0;bottom:0;background-repeat:no-repeat;-webkit-filter:blur(20px);filter:blur(20px);background-size:cover;background-position:center}.LinkCard,.LinkCard:hover{text-decoration:none;border:none!important;color:inherit!important}.LinkCard-content{position:relative;display:flex;align-items:center;justify-content:space-between;padding:12px;border-radius:inherit;background-color:rgba(246,246,246,0.88)}.LinkCard-text{overflow:hidden}.LinkCard-title{display:-webkit-box;-webkit-line-clamp:2;overflow:hidden;text-overflow:ellipsis;max-height:calc(16px * 1.25 * 2);font-size:16px;font-weight:500;line-height:1.25;color:#1a1a1a}.LinkCard-meta{display:flex;margin-top:4px;font-size:14px;line-height:20px;color:#999;white-space:nowrap}.LinkCard-imageCell{margin-left:8px;border-radius:6px}.LinkCard-image{display:block;width:60px;height:auto;border-radius:inherit}</style><span class=LinkCard-backdrop style=background-image:url(https://zhstatic.zhihu.com/assets/zhihu/editor/zhihu-card-default.svg)></span><span class=LinkCard-content><span class=LinkCard-text><span class=LinkCard-title>"+title+"</span><span class=LinkCard-meta><span style=display:inline-flex;align-items:center><svg class="+"'Zi Zi--InsertLink'"+" fill=currentColor viewBox="+"'0 0 24 24'"+" width=17 height=17><path d="+"'M6.77 17.23c-.905-.904-.94-2.333-.08-3.193l3.059-3.06-1.192-1.19-3.059 3.058c-1.489 1.489-1.427 3.954.138 5.519s4.03 1.627 5.519.138l3.059-3.059-1.192-1.192-3.059 3.06c-.86.86-2.289.824-3.193-.08zm3.016-8.673l1.192 1.192 3.059-3.06c.86-.86 2.289-.824 3.193.08.905.905.94 2.334.08 3.194l-3.059 3.06 1.192 1.19 3.059-3.058c1.489-1.489 1.427-3.954-.138-5.519s-4.03-1.627-5.519-.138L9.786 8.557zm-1.023 6.68c.33.33.863.343 1.177.029l5.34-5.34c.314-.314.3-.846-.03-1.176-.33-.33-.862-.344-1.176-.03l-5.34 5.34c-.314.314-.3.846.03 1.177z'"+" fill-rule=evenodd></path></svg></span>"+link+"</span></span><span class=LinkCard-imageCell><img class=LinkCard-image alt=图标 src=https://site-1258928558.cos.ap-guangzhou.myqcloud.com/linkcard.png></span></span>";  for (var i = LinkCards.length - 1; i >= 1; i--) { LinkCard=LinkCards[i]; title=LinkCard.innerText; link=LinkCard.href; LinkCard.innerHTML="<span class=LinkCard-backdrop style=background-image:url(https://zhstatic.zhihu.com/assets/zhihu/editor/zhihu-card-default.svg)></span><span class=LinkCard-content><span class=LinkCard-text><span class=LinkCard-title>"+title+"</span><span class=LinkCard-meta><span style=display:inline-flex;align-items:center><svg class="+"'Zi Zi--InsertLink'"+" fill=currentColor viewBox="+"'0 0 24 24'"+" width=17 height=17><path d="+"'M6.77 17.23c-.905-.904-.94-2.333-.08-3.193l3.059-3.06-1.192-1.19-3.059 3.058c-1.489 1.489-1.427 3.954.138 5.519s4.03 1.627 5.519.138l3.059-3.059-1.192-1.192-3.059 3.06c-.86.86-2.289.824-3.193-.08zm3.016-8.673l1.192 1.192 3.059-3.06c.86-.86 2.289-.824 3.193.08.905.905.94 2.334.08 3.194l-3.059 3.06 1.192 1.19 3.059-3.058c1.489-1.489 1.427-3.954-.138-5.519s-4.03-1.627-5.519-.138L9.786 8.557zm-1.023 6.68c.33.33.863.343 1.177.029l5.34-5.34c.314-.314.3-.846-.03-1.176-.33-.33-.862-.344-1.176-.03l-5.34 5.34c-.314.314-.3.846.03 1.177z'"+" fill-rule=evenodd></path></svg></span>"+link+"</span></span><span class=LinkCard-imageCell><img class=LinkCard-image alt=图标 src=https://site-1258928558.cos.ap-guangzhou.myqcloud.com/linkcard.png></span></span>"; } } } 

在 md 中写博客中,以如下 html 的方式写入超链接:

<a href="https://bestzuo.cn" class="LinkCard" target="_blank">Sanarous的博客</a> 

其中 class="LinkCard" 中的 LinkCard 大小写一定不能写错,否则识别不了。

6.给菜单栏增加图标

效果如下:

img

这个其实不能给所有主题通用,但是可以根据自己主题的情况进行修改,由于该步骤略显复杂,所以我将尽量以通俗的方式解释如何使用。

首先需要确保引入 FontAwesome 图标的 CSS 文件,因为图标都是引入的 FontAwesome 图标,HanShan 主题已经默认引入了该图标的 CSS 文件,所以不需要再次引入,如果你的主题文件中没有引入 FontAwesome,可以在一个全局模板文件中引入 CSS 如下:

<link type='text/css' rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" media='all'/> 

由于我使用的是 FontAwesome-Animation 图标,也就是会动的 FontAwesome 图标,所以还需要引入如下 CSS 文件(HanShan 主题也需要引入):

<link type='text/css' rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Sanarous/files@master/font-awesome-animation.min.css"> 

FontAwesome-Animation 的使用文档如下:

FontAwesome-Animation 文档

再次以 HanShan 主题为例,其它主题思路类似,需要找到存放菜单栏模板的 ftl 文件,HanShan 主题的文件路径为 module/header.ftl,接下来就是修改这个文件中的内容。

由于 Halo 博客从后台传到前台的值中没有每个菜单的唯一标识,所以只能根据每个菜单的名称或者 URL 来区分不同菜单的图标。以下使用 FreeMarker 中的 <#switch></#swith> 标签完成。

定位代码到这里:

<@menuTag method="tree"> <#list menus?sort_by('priority') as menu> <li class="menu-scroll-item"> //这个代表的是有子菜单的菜单 <#if menu.children?? && menu.children?size gt 0> <a href="javascript:void(0)" data-ajax target="${menu.target!}" onfocus="this.blur();">${menu.name}</a> <#else> //这个代表的是没有子菜单的菜单 <a href="${menu.url!}" data-ajax target="${menu.target!}">${menu.name}</a> </#if> 

其中需要注意该每个菜单是否有子菜单,并按照代码逻辑将如下代码加入到对应的位置,比如如果该菜单有子菜单,那么需要加入到 <#if><#else> 之间:

<#if menu.children?? && menu.children?size gt 0> + <#switch menu.url> + <#case '/about'> + <a href="javascript:void(0)" data-ajax target="${menu.target!}" onfocus="this.blur();"><span class="faa-parent animated-hover"><i class="fa fa-leaf faa-wrench"></i>${menu.name}</span></a> + <#break> + <#case '/laboratory'> + <a href="javascript:void(0)" data-ajax target="${menu.target!}" onfocus="this.blur();"><span class="faa-parent animated-hover"><i class="fab fa-app-store faa-tada" style="font-size:16px"></i>${menu.name}</a></span> + <#break> + <#default> + <a href="javascript:void(0)" data-ajax target="${menu.target!}" onfocus="this.blur();">${menu.name}</a> + </#switch>  - <a href="javascript:void(0)" data-ajax target="${menu.target!}" onfocus="this.blur();">${menu.name}</a> <#else> 

如果没有子菜单,那么将代码加到 <#else><#/if> 之间即可,示例如下:

<#else> + <#switch menu.url> + <#case '/about'> + <a href="${menu.url!}" data-ajax target="${menu.target!}"><span class="faa-parent animated-hover"><i class="fa fa-leaf faa-wrench"></i>${menu.name}</span></a> + <#break> + <#case '/laboratory'> + <a href="javascript:void(0)" data-ajax target="${menu.target!}" onfocus="this.blur();"><span class="faa-parent animated-hover"><i class="fab fa-app-store faa-tada" style="font-size:16px"></i>${menu.name}</a></span> + <#break> + <#default> + <a href="${menu.url!}" data-ajax target="${menu.target!}">${menu.name}</a> + </#switch>  - <a href="${menu.url!}" data-ajax target="${menu.target!}">${menu.name}</a> </#if> 

以上使用的是 menu.url 也就是菜单对应的 URL 地址作为条件判断,也可以使用 menu.name 即菜单名称进行判断,只需要将下面 <#case 'xxx'> 中的 xxx 替换为对应的条件即可。

7.增加文章最近更新提示

效果如下:

img

由于这个我只适配了自己的主题,其它主题样式需要自己去选择合适的位置,所以我只提供 ftl 模板代码:

<#if (.now?long-86400000*5)?number_to_datetime lte post.editTime?datetime && post.editTime?datetime gt post.createTime?datetime> <i class="fa fa-refresh" aria-hidden="true" title="最近有更新" style="color: var(--code-toolbar-color);font-size:11px"></i> </#if> 

上述 if 判断中的语句代表的意思是如果该文章的最后编辑时间距当前时间小于 5 天,且编辑时间大于发布时间,那么就判断该文章最近 5 天内有更新并显示该提示信息。其中 (.now?long-86400000*5)?number_to_datetime 中的 5 可以自行设置,该数值表示天数。

8.给页面增加图片点击放大功能

我随意浏览了一些 halo 博客主题,发现大多数主题并没有自带图片点击放大功能,只能对图片右键打开新标签页查看大图,这对于浏览博客的读者来说是非常的不方便,因此如果你的博客还没有完善该功能的话,你可以参考以下方法去完善。

图片点击放大的功能并不复杂,也不需要自己动手去写 js 实现,当前主流实现的插件很多,这里主要推荐两个插件:viewer.jsfancybox,前者是 HanShan 主题默认的图片放大插件,后者是很多网站都在使用的图片放大插件。

viewer 的使用

首先需要在全局页面模板 ftl 文件中(不同主题的文件名不同)引入对应的 CSS 和 JS 插件:

<link type='text/css' rel="stylesheet" href="https://cdn.jsdelivr.net/npm/viewerjs@1.5.0/dist/viewer.min.css">  <script data-pjax-viewer src="https://cdn.jsdelivr.net/npm/viewerjs@1.5.0/dist/viewer.min.js"></script> 

viewer.js 的使用方法是通过获取某个 div 容器,并对该容器中所有的图片附加图片点击放大功能。它的缺点就是如果这个 div 容器选择的过大,且该容器中包含的图片较多,那么点击图片放大时遮罩层的加载速度会变得非常慢,因此我们选择容器的时候不宜选择范围过大。

举个例子,比如我们主题的文章渲染模板内容如下:

<div class="post-content" id="post-content"> ${post.formatContent!} //后台传到前台的文章内容 </div> 

那么我们可以限制为只给文章内容中的图片实现点击放大功能,所以我们可以在主题的全局模板的 <script></script> 标签中增加如下配置信息:

if (document.getElementById('post-content')) { new Viewer(document.getElementById('post-content'), { toolbar: false, }); } 

其中 post-content 就是上面包含文章的 div 容器的 id 值,需要根据自己主题的设置而定。

fancybox 的使用

viewer.js 二者选一使用即可,使用 fancybox 同样需要引入对应的 CSS 和 JS 文件,与上面类似:

<link type='text/css' rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css">  <script data-pjax-viewer src="https://cdn.bootcdn.net/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js"></script> 

fancybox 的使用方法是为指定的 <img> 标签增加特定的超链接,举例如下:

//原来的图片 <img src="small_1.jpg">  //使用 fancybox 后能点击放大的图片 <a data-fancybox="gallery" href="small_1.jpg"> <img src="small_1.jpg"> </a> 

所以我们使用的方法就是给 <img> 标签添加指定的 <a></a> 标签,这个可以使用 JQuery 完成,同样在一个全局配置的 js 文件中写入如下代码:

$("#post-content").find("img").click(function(){ $(this).wrap("<a data-fancybox='images' href='"+ $(this).src +"'></a>"); }); 

该方法可能存在一些 BUG,如果使用效果不太好,仍然建议使用 viewer.js 实现图片点击放大。

9.添加鼠标点击特效

这个我自己的博客没有加上去,主要是抑制一下页面加载卡顿,预览效果如下:

img

使用方式只需要在全局模板页面中引入任意一个 js 代码即可,如果没有显示则说明你的 js 文件位置没有放置正确。

引入小心心:

//引入小心心特效 <script src="https://cdn.jsdelivr.net/gh/Sanarous/files@1.151/js/clicklove.js"></script> 

引入社会主义核心价值观

//社会主义核心价值观 <script src="https://cdn.jsdelivr.net/gh/Sanarous/files@1.151/js/clicksocialvalue.js"></script> 

引入小花花

//花花特效 <script src="https://cdn.jsdelivr.net/gh/Sanarous/files@1.151/js/click.min.js"></script> 

10.侧边滚动条样式优化

我当前使用的是主题默认样式,与该样式不同,该样式效果如下:

image.png

只需要在主题的 main.css 文件中写入如下 CSS:

/* 滚动条 */ ::-webkit-scrollbar { width: 8px; height: 8px; }  ::-webkit-scrollbar-track { background-color: rgba(73, 177, 245, 0.2); border-radius: 2em; }  ::-webkit-scrollbar-thumb { background-color: #49b1f5; background-image: -webkit-linear-gradient( 45deg, rgba(255, 255, 255, 0.4) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.4) 75%, transparent 75%, transparent ); border-radius: 2em; }  ::-webkit-scrollbar-corner { background-color: transparent; }  ::-moz-selection { color: #fff; background-color: #49b1f5; } 

11.鼠标 * 字跟随变化

同样我并没有使用该特效,其效果如下:

image.png

如果需要全局使用,直接在文章全局模板中引入如下 js 文件:

<script src="https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/Hexo/js/mouse_snow.min.js"></script> 

12.另一种风格的 note 和 tag 标签

效果如下:

绿色

红色

黄色

灰色

蓝色

红色小标签 绿色小标签 蓝色小标签 黄色小标签 灰色小标签

在主题的 CSS 文件中引入如下:

span.inline-tag { display: inline; padding: .2em .6em .3em; font-size: 90%; font-weight: 400; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .1rem; border-radius: 6px; background-color: var(--Color) }  p.red, span.red { --Color: rgb(233, 30, 100); --ColorA: rgba(233, 30, 100, 0.2); }  p.green, span.green { --Color: rgb(139, 195, 74); --ColorA: rgba(139, 195, 74, 0.2); }  p.blue, span.blue { --Color: rgb(3, 169, 244); --ColorA: rgba(3, 169, 244, 0.2); }  p.yellow, span.yellow { --Color: rgb(255, 193, 7); --ColorA: rgba(255, 193, 7, 0.2); }  p.grey, span.grey { --Color: rgb(76, 76, 76); --ColorA: rgba(76, 76, 76, 0.2); }  p.div-border { padding: 10px; border: 1px solid var(--Color,#333); border-radius: 0.4rem; background-color: var(--ColorA, transparent); }  p.left { border-left-width: 5px; border-left-color: var(--Color); }  p.bottom { border-bottom-width: 5px; border-bottom-color: var(--Color); }  p.right { border-right-width: 5px; border-right-color: var(--Color); }  p.top { border-top-width: 5px; border-top-color: var(--Color); } 

使用方式仍然是以 HTML 标签在 md 文档中引入:

/* note语法示例 */ <p class='div-border green'>绿色</p> <p class='div-border red'>红色</p> <p class='div-border yellow'>黄色</p> <p class='div-border grey'>灰色</p> <p class='div-border blue'>蓝色</p>  /* 小tag标签语法示例 */ <span class="inline-tag red">红色小标签</span> <span class="inline-tag green">绿色小标签</span> <span class="inline-tag blue">蓝色小标签</span> <span class="inline-tag yellow">黄色小标签</span> <span class="inline-tag grey">灰色小标签</span> 

13.渐变 note 标签样式

展示效果如下:

注意该样式需要引入 FontAwesome v5.0+ 以上的 CSS 文件,否则图标不会展示。

引入 CSS 链接如下:

<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/5.13.1/css/all.min.css" rel="stylesheet"> 

然后在 main.css 中添加如下样式:

.tip { position: relative; color: #fff; background: #20a0ff; background: -webkit-gradient(linear,left top,right top,from(#20a0ff),to(#20b8ff)); background: -webkit-linear-gradient(left,#20a0ff,#20b8ff); background: linear-gradient(90deg,#20a0ff,#20b8ff); padding: 6px 20px; border-radius: 10px; -webkit-box-shadow: 0 3px 5px rgba(32,160,255,.5); box-shadow: 0 3px 5px rgba(32,160,255,.5); margin-bottom: 20px }  .tip p { margin: 5px 0!important }  .tip:before { background: #20a0ff; background: -webkit-gradient(linear,left bottom,left top,from(#0092ff),to(#20b8ff)); background: -webkit-linear-gradient(bottom,#0092ff,#20b8ff); background: linear-gradient(0deg,#0092ff,#20b8ff); border-radius: 50%; color: #fff; content: "\f129"; font-size: 12px; position: absolute; width: 24px; height: 24px; line-height: 24.5px; left: -12px; top: -12px; -webkit-box-shadow: 0 0 0 2.5px #fff; box-shadow: 0 0 0 2.5px #fff; font-weight: 600; font-family: "Font Awesome 5 Free"; text-align: center }  .btn,.getit a { position: relative }  .well .tip:before { -webkit-box-shadow: 0 0 0 2.5px #f7f8f9; box-shadow: 0 0 0 2.5px #f7f8f9 }  .tip ol { margin: 0 }  .tip.success { background: #61be33; background: -webkit-gradient(linear,left top,right top,from(#61be33),to(#8fce44)); background: -webkit-linear-gradient(left,#61be33,#8fce44); background: linear-gradient(90deg,#61be33,#8fce44); text-shadow: 0 -1px #61be33; -webkit-box-shadow: 0 3px 5px rgba(104,195,59,.5); box-shadow: 0 3px 5px rgba(104,195,59,.5) }  .tip.success:before { background: -webkit-gradient(linear,left bottom,left top,from(#52bb1d),to(#95d34b)); background: -webkit-linear-gradient(bottom,#52bb1d,#95d34b); background: linear-gradient(0deg,#52bb1d,#95d34b); content: "\f00c"; text-shadow: 0 -1px #61be33 }  .tip.warning { background: #ff953f; background: -webkit-gradient(linear,left top,right top,from(#ff953f),to(#ffb449)); background: -webkit-linear-gradient(left,#ff953f,#ffb449); background: linear-gradient(90deg,#ff953f,#ffb449); text-shadow: 0 -1px #ff953f; -webkit-box-shadow: 0 3px 5px rgba(255,154,73,.5); box-shadow: 0 3px 5px rgba(255,154,73,.5) }  .tip.warning:before { background: -webkit-gradient(linear,left bottom,left top,from(#ff8f35),to(#ffc149)); background: -webkit-linear-gradient(bottom,#ff8f35,#ffc149); background: linear-gradient(0deg,#ff8f35,#ffc149); content: "\f12a"; text-shadow: 0 -1px #ff953f }  .tip.error { background: #ff4949; background: -webkit-gradient(linear,left top,right top,from(#ff4949),to(#ff7849)); background: -webkit-linear-gradient(left,#ff4949,#ff7849); background: linear-gradient(90deg,#ff4949,#ff7849); text-shadow: 0 -1px #ff4949; -webkit-box-shadow: 0 3px 5px rgba(255,73,73,.5); box-shadow: 0 3px 5px rgba(255,73,73,.5) }  .tip.error:before { background: -webkit-gradient(linear,left bottom,left top,from(#ff3838),to(#ff7849)); background: -webkit-linear-gradient(bottom,#ff3838,#ff7849); background: linear-gradient(0deg,#ff3838,#ff7849); content: "\f00d"; text-shadow: 0 -1px #ff4949 }  /*夜间适配*/ .night .tip { filter: brightness(0.7); }  /* snote夜间模式 */ .night .tip{ color: #4c4948; } 

同上面所有外挂样式一下,均使用 HTML 标签的方式在 md 中写入:

<div class='tip'><p>默认情况<p></div> <div class='tip success'><p>success<p></div> <div class='tip error'><p>error<p></div> <div class='tip warning'><p>warning<p></div> 

主题深度美化教程

本小节涉及的是深度的美化,需要你掌握一定的前端基础。(持续更新)

1.自定义主题配置文件

如果只修改 CSS 和一些基本样式的话,往往会陷入到代码写的太死的情况,又称作『硬编码』,如果后面需要修改,那么只能去修改源码,这就非常的不方便,所以我们可以将一些公用配置信息写入到主题的配置文件中,从而可以直接将配置信息抽出来,也方便移植或者后续的开源。

image.png

因此深度优化的第一步就是学会修改配置文件,也就是主题中的 settings.yaml 文件,如何配置和引用已经在官方的开发文档中有详细说明,请移步阅读。

2.自定义公共模板文件

最初看到 halo 博客提供的这个自定义模板文件其实相当的模糊,直到现在才明白这个接口提供的意义和使用方法。

我们在 halo 博客中自定义页面往往使用的是在新建页面的编辑页面中写入 html 代码,这是一种常规的使用方式。但是如果现在我们需要在新建页面中使用 FreeMarker 的模板,比如获取文章的阅读量之类的信息,那么只有 html 标签是毫无用处的,此时就需要用到自定义公共模板文件。

那么如何使用呢?官方其实已经提供了教程,比如需要自定义友情链接的模板页面(HanShan 主题定义了两个 links.ftl 模板,不需要再去自定义友链页面),步骤如下:

  1. 使用 Vscode 在主题根目录下新建一个 sheet_xxx.ftl 页面,注意必须是以 sheet_ 为前缀,否则识别不了这个模板是自定义模板。
  2. 编辑页面内容,注意需要引入公共部分(导航栏、背景图片之类的)。
  3. 在页面发布时,在选择自定义模板时可以看到新建的模板文件。

关于第 2 点,以 HanShan 主题为例,我们新建模板页面的时候需要包含以下内容:

<#include "module/macro.ftl"> <@layout title="${sheet.title!} - ${blog_title!} "> <div id="page" class="site"> <main class="site-main" id="main"> <div class="site-content"> <header class="bg-cover page-header"> <#if sheet.thumbnail?? && sheet.thumbnail!=''> <div class="cover-bg"> <img src="${sheet.thumbnail!}" alt="${sheet.title!}"> </div> <#else> <div class="default-cover-bg"> </div> </#if> <div class="cover-content"> <div class="inner"> <div class="post-count"></div> <h1 class="page-title" style="font-size: 46px;">${sheet.title!}</h1> </div> </div> </header> <div class="sheet-content"> //此 div 中填写自定义模板内容,其它保持不变 </div> </div> <#include "module/comment.ftl"> <#if is_post??> <@comment post,"post" /> <#elseif is_sheet??> <@comment sheet,"sheet" /> </#if>  </main> </div> </@layout> 

其它主题同样需要保证该模板的基本样式,当然也可以不遵从该基本模板,而是在这个页面中完全自定义 html 内容。

3. 自定义文章热度排行榜页面

这个就可以使用前一小节所说的自定义模板文件方式,因为需要通过 ftl 文件获取后台提供的接口。

在上述新建了自定义模板后,在模板中的 <div class="sheet-content"></div> 中填入以下调用方式:

<div class="sheet-content"> <ol> <@postTag method="count"> <@postTag method="latest" top="${count}"> <#assign postCunt = 0 /> <#list posts?sort_by("visits")?reverse as post> <li> <a href="${post.fullPath}"> <span class="views-top-time" style="margin-right:10px">【热度:${post.visits}】</span> <span class="views-top-title">${post.title}</span> </a> </li> <#assign postCunt++ /> <#if postCunt == 5> <#break> </#if> </#list> </@postTag> </@postTag> </ol> </div> 

其中 <#if postCunt == 5> 代表显示多少篇文章,可以根据自己的需要修改,以上样式也需要根据自己的喜好进行修改。同样,如果不想使用阅读量作为排序指标,也可以修改为评论数等其它参数。

4.重构标签云页面

这个也不是新鲜东西,是我从 Hexo 博客中带过来的样式,以下对任意主题都可适用。效果如下:

image.png

找到主题的 tags.ftl 文件,将标签云的 ftl 代码修改如下:

<div class="tagcloud tag-page"> <@tagTag method="list"> <#if tags?? && tags?size gt 0> <div class="tags-item" style="padding: 20px 0;"> <#list tags as tag> <a class="tag-item" data-ajax href="${tag.fullPath!}"> <span class="name">🔖&nbsp;${tag.name}</span> <span>&nbsp;${tag.postCount}</span> </a> </#list> </div> </#if> </@tagTag> </div> 

为了增加随机彩色标签云效果,还需要在该 ftl 页面下面写入 js 代码:

<script> var tags = $(".tags-item").children(".tag-item"); for (var i = tags.length - 1; i >= 0; i--) { var r = Math.floor(Math.random() * 75 + 130); var g = Math.floor(Math.random() * 75 + 100); var b = Math.floor(Math.random() * 75 + 80); tags[i].style.background = "rgb(" + r + "," + g + "," + b + ")"; }  var randnum = function (n) { return Math.round(Math.random() * n) }, hexify = function (n) { return ("0" + parseInt(n).toString(16)).slice(-2) }, randex = function () { return "#" + hexify(randnum(255)) + hexify(randnum(255)) + hexify(randnum(255)) }, blender = function () { return Math.round(Math.random()) ? "radial-gradient(circle at " + randnum(100) + "% " + randnum(100) + "%, " + randex() + ", " + randex() + ")" : "linear-gradient(" + randnum(360) + "deg, " + randex() + ", " + randex() + ")" };  $(".flex-book-item").each(function () { var n = blender(); $(this).css("background", n); }) </script> 

然后在主题的 main.css 中增加 CSS 代码如下:

.tagcloud .tag-item { color: #fff; border-radius: 6px; padding: 3px 8px 3px 5px; display: inline-block }  .tagcloud .tag-item:hover { text-decoration: none; color: #fff!important; -webkit-box-shadow: 0 8px 16px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19); -moz-box-shadow: 0 8px 16px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19); box-shadow: 0 8px 16px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19) }  .night .tagcloud .tag-item { -webkit-filter: brightness(.3)!important; filter: brightness(.7)!important } 

5.增加导航工具条

样式如下所示,功能分别是:回到顶部、跳转到留言区、跳转到评论区、切换到神色模式、点击播放/暂停背景音乐。

img

关于该部分的完整解释和代码详见 cunguafa 的 CSDN 博客,我们新建一个 sidetool.ftl 文件,将博客中的完整代码(如下图)复制到文件中。

img

然后以 hanshan 主题为例,在 module/macro.ftl 中引入 sidetool.ftl 模板文件,其它主题思想类似,然后导航条就可以正常使用了,功能上可以自己进行修改。

img

其中的图标如果想要自己替换的话,可以自己制作 SVG 图标或者去阿里的图标库搜索

总结

以上涉及的大多数关于 ftl 模板文件修改的美化教程均只针对于 halo 博客主题有效,小部分纯 CSS 样式适用于任何其它博客框架或主题,最主要的是你一定要知道你的博客主题全局 CSS 样式文件的位置,并能够熟练地进行定位和修改。

同时,博客细节的美化并非一朝一夕就可以完成,我们在美化过程中,除了追求美观外,更应该多留意静态资源的占用情况,使得页面渲染时不至于因为插件加载过多导致加载非常慢,浏览站点的用户多加载等待 1s,浏览量可能会每天平均减少 20 个。即使如此,我自己的站点也做不到极速浏览🤣就很气

0

评论区