Hexo-Ocean 主题食用方式 | ܤ
正在掀起波涛

Hexo-Ocean 主题食用方式

Ocean大碗汤煮好啦 快进来尝尝


有点懒 先堆着 以后再说 反正也没人看
因为自己在瞎折腾 四处乱找 有时候忘了保存一些代码和文件
在此感谢各大网友提供的教程和思路 还有重要的代码码😘 君子性非异也 善假于物也

待优化

  • 看板娘指针 言语响应
  • 加载动画 打算试试这个 点点点
  • 标题像 vsc 一样可折叠展开
  • 代码块全屏无法置于最上层
  • 阅读进度条有点炫 就是扒拉不下来 流光进度条

食材

  • Ocean 主题使用的图标查询

  • 首页的无限循环海浪
    来源 找自己喜欢的 然后去 解析下载
    因为原视频会断掉 这我忍不了 四处寻找 还真有很是感动我想要的作品 然后顺藤摸瓜找到了原作者可费了一番功夫捏 还试了好多款 个人感觉这款效果最佳 而且跟蒙板意外地很配 感激不尽 💖💖💖 给作者磕三个响头~~~

  • 一些头像图标啥的我是在 shutterstock 下找的 下载教程

备菜

折腾工具

  • 文件内文字搜索 Anytext 可以查找到文件内的文字 但有几率蓝屏
  • 文件快速搜索 Listary 类似 Everything 快速查找文件 可收藏文件夹用于文件夹直接的快速跳转 也可用来启动应用程序
  • vscode

其它

  • 家里有梯子且会翻墙
  • 手指能动且会按 F12
  • 有办法移动&点击鼠标

大自然的馈赠

来自其他 Ocean 主题使用者

已烹饪

Ocean 用的 ejs 和 styl 格式与一般主题不同小白哭了 呜呜~~ 且不同主题间配置方式都不一样 文件名和路径也不一样 所以有些教程不能直接拿来食用 需要稍微加工一下 去掉头就可以吃了
其实加工流程大部分都是相似的 多看几个就知道了
以下是已经可以使用配置的

因为是自己在瞎折腾 而且是事后再写的 有可能会遗漏一些东西 或者说弄错了
如有遇到相关问题 🎉欢迎指出🎉

Aritalk说说

Artitalk 使用文档 样式选择 一个基于 Artitalk 的项目
如果想说说只在一篇中调用
那就在在要加入说说的文章的 .md 文件加入代码
头像配置完要过段时间才会生效

 我之前的个人配置
<script type="text/javascript" src="https://unpkg.com/artitalk"></script>
<!-- 存放说说的容器 -->
<div id="artitalk_main"></div>
<script>
new Artitalk({
    appId: 'xxx', // 填你的LeanCloud里的
    appKey: 'xxx', // 填你的LeanCloud里的
    pageSize: 2,
    // 目前一定要配置API,不然会出现跨域问题
    serverURL: 'xxx'
    atEmoji:{
        "Mafumafu-199749454": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749454.png",
        "Mafumafu-199749455": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749455.png",
        "Mafumafu-199749456": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749456.png",
        "Mafumafu-199749457": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749457.png",
        "Mafumafu-199749458": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749458.png",
        "Mafumafu-199749459": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749459.png",
        "Mafumafu-199749460": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749460.png",
        "Mafumafu-199749461": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749461.png",
        "Mafumafu-199749462": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749462.png",
        "Mafumafu-199749463": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749463.png",
        "Mafumafu-199749464": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749464.png",
        "Mafumafu-199749465": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749465.png",
        "Mafumafu-199749466": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749466.png",
        "Mafumafu-199749467": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749467.png",
        "Mafumafu-199749468": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749468.png",
        "Mafumafu-199749469": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749469.png",
        "Mafumafu-199749470": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749470.png",
        "Mafumafu-199749471": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749471.png",
        "Mafumafu-199749472": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749472.png",
        "Mafumafu-199749473": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749473.png",
        "Mafumafu-199749474": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749474.png",
        "Mafumafu-199749475": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749475.png",
        "Mafumafu-199749476": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749476.png",
        "Mafumafu-199749477": "https://twikoo-magic.oss-cn-hangzhou.aliyuncs.com/Mafumafu/199749477.png",
        "smallshake-1": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/-6ddd0bf163b88414.jpg",
        "smallshake-2": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/761abad49f1d0845.jpg",
        "smallshake-3": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-23175f4710329ce8.jpg",
        "smallshake-4": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-253ceaa0ad9d00d5.jpg",
        "smallshake-5": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-2be07232a85f4472.jpg",
        "smallshake-6": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-2cf4d6b6f6c9d18d.jpg",
        "smallshake-7": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-2f9ecc265b54c153.jpg",
        "smallshake-8": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-3310ef2a226c4776.jpg",
        "smallshake-9": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-35261cc16e1c7623.jpg",
        "smallshake-10": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-371ab21e64e4344e.jpg",
        "smallshake-11": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-373612606e85580.jpg",
        "smallshake-12": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-4058efd3f267fafc.jpg",
        "smallshake-13": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-43333e8c017d970f.jpg",
        "smallshake-14": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-558225f96f141d7f.jpg",
        "smallshake-15": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-594d91a80ca975fc.jpg",
        "smallshake-16": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-640df94c061cf44c.jpg",
        "smallshake-17": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-6a6dfc93a8452fd0.jpg",
        "smallshake-18": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-770782127c498dcf.jpg",
        "smallshake-19": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null-77425082b7be3318.jpg",
        "smallshake-20": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null134bf8bfc231fde4.jpg",
        "smallshake-21": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null1fbf2df8aace9658.jpg",
        "smallshake-22": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null26a9eca32c4f2629.jpg",
        "smallshake-23": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null28a0952ef6b273b8.jpg",
        "smallshake-24": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null31e2f41186cb17bc.jpg",
        "smallshake-25": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null32683e4e287a7c78.jpg",
        "smallshake-26": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null33d1b7a0ee58ba6.jpg",
        "smallshake-27": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null3b074dfcd6eb852e.jpg",
        "smallshake-28": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null3b5cda546336bced.jpg",
        "smallshake-29": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null42e4c07e6d699079.jpg",
        "smallshake-30": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null47801cf08960ce50.jpg",
        "smallshake-31": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null4bf6cc30c99948c6.jpg",
        "smallshake-32": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null4f2346ac5c8fe1d2.jpg",
        "smallshake-33": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null529a8bd66d7e397f.jpg",
        "smallshake-34": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null5b20902ca111a2e2.jpg",
        "smallshake-35": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null5d68da02952527dd.jpg",
        "smallshake-36": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null5db22a4a42075334.jpg",
        "smallshake-37": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null60dfdc8d15a39d11.jpg",
        "smallshake-38": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null62860349ef635710.gif",
        "smallshake-39": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null6474bc4d5031f1e3.jpg",
        "smallshake-40": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null65faeb7ecb6b72ce.jpg",
        "smallshake-41": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null670847fd57338448.jpg",
        "smallshake-42": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null7128208ad110bf70.jpg",
        "smallshake-43": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/null9ee61c8ed3ad675.jpg",
        "smallshake-44": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/nulld832e3ed8b0b890.jpg",
        "smallshake-45": "https://cdn.jsdelivr.net/gh/2x-ercha/twikoo-magic@master/image/smallshake/nulle6ff0f7f47a7614.jpg",
    },
})
</script>
<style>
.tocbot{
    display: none;
}
#artitalk_main .shuoshuo_time>span:nth-child(1){
    display: none;
}
#artitalk_main .power {
    margin-right: 20px;
}
#operare_artitalk .c2{
    opacity: 1;
}
#artitalk_main .cbp_tmtimeline>li:nth-child(odd) .cbp_tmlabel {
    background: linear-gradient(-45deg, #178f9d,#62b3c1b5,#7db3b933,#e4eae920) 0% 0% / 400% 400%;
    animation: 30s cubic-bezier(0.8, 0, 0.2, 1.0) 0s infinite normal none running gradientBG;
    color: black;
    font-weight: 800;
}
#artitalk_main .cbp_tmtimeline>li .cbp_tmlabel {
    background: linear-gradient(45deg, #d56e97,#b27d98b5,#a57a8e30,#c4b3be20) 0% 0% / 400% 400%;
    animation: 30s cubic-bezier(0.8, 0, 0.2, 1.0) 0s infinite normal none running gradientBG;
    color: black;
    font-weight: 800;
}
#artitalk_main .cbp_tmtimeline>li:nth-child(odd) .cbp_tmlabel:after {
    display: none
}
#artitalk_main .cbp_tmtimeline>li .cbp_tmlabel:after {
    display: none
}
#readmore {
    background: linear-gradient(90deg, #d56e97,#b27d98b5,#62b3c1db,#178f9d) 0% 0% / 400% 400%;
    animation: 30s cubic-bezier(0.8, 0, 0.2, 1.0) 0s infinite normal none running gradientBG;
    color: black;
}
@keyframes gradientBG {
    0% {
        background-position: 0% 50%;
    }
    50% {
        background-position: 100% 50%;
    }
    100% {
        background-position: 0% 50%;
    }
}
</style>

因为 Artitalk 需要使用 LeanCloud
而国内的 LeanCloud 需要备案
国际版的 LeanCloud 需要挂梯子不然会一直’下雨’加载不出来,而且头像需要图床

我在 github 上找了一个项目 Hexo-Artitalk-Static 可以本地使用 Artitalk
对比原本的 Artitalk
优点是可以离线使用,速度很快 还可以自己加插件
缺点是你写的说说不可以评论 虽然也没人会评论

Hexo-Artitalk-Static 官方有配置教程

以下是我现在的配置

打开 git bash 执行
npm install hexo-artitalk-static

source 文件夹下创建 _data 文件夹
(这个 source 文件夹就是平时写文章的文件夹 里面应该还有 _posts 文件夹)

然后在 source/_data 创建文件 artitalk.yml 之后在此文件写说说
(先执行 hexo s 再更新说说会自动生成时间)

## 说说格式
- content: Hexo-Artitalk-Static 真不错,没有后端,没有请求,速度是真的快
- content: |
    如果你的 JavaScript 基础还不错
    你还可以为 Hexo-Artitalk-Static 写自定义插件,还可以自定义说说的模板
    自由度是真的高(也可以安装别人的插件)

在 ocean 主题的 sourcecss 文件夹中 创建文件 artitalk.css
我的css文件内容如下:

/* 偶数项背景 */
/* 偶数项背景 */
#at_main .at_item:nth-child(odd) .at_content_container {
    background: linear-gradient(-45deg, #178f9d,#62b3c1b5,#7db3b933,#e4eae900) 0% 0% / 400% 400%;
    animation: 30s cubic-bezier(0.8, 0, 0.2, 1.0) 0s infinite normal none running gradientBG;
    color: black;
    font-weight: 800;
}
/* 奇数项背景 */
#at_main .at_item .at_content_container {
    background: linear-gradient(45deg, #54ad92,#7ac2adb5,#59a5923d,#5faa9300) 0% 0% / 400% 400%;
    animation: 30s cubic-bezier(0.8, 0, 0.2, 1.0) 0s infinite normal none running gradientBG;
    color: black;
    font-weight: 800;
}


#at_main {
/* 设置父容器为flex纵向布局 */
display: flex;
flex-direction: column;
}

.at_items {
/* 移除默认列表样式 */
padding: 0;
margin: 0;
list-style: none;
}

.at_item {
/* 设置列表项为flex布局 */
display: flex;
align-items: flex-start; /* 顶部对齐 */
margin-bottom: 16px; /* 项间距 */
}

.at_avatar {
width: 60px; /* 头像尺寸 */
height: 60px;
border-radius: 50%; /* 圆形头像 */
margin-right: 12px; /* 头像与内容间距 */
}

.at_content_container {
/* 内容容器自适应剩余宽度 */
flex: 1;
text-align: left; /* 文字左对齐 */
}

.at_more {
/* 加载更多容器 */
display: flex;
justify-content: center; /* 水平居中 */
margin-top: 20px; /* 与列表的间距 */
position: relative;
}


/* 鼠标移动头像时旋转 */
#at_main .at_avatar:hover {  
    transform: rotateZ(360deg);
}

.article-title {
    text-align: center!important;    /* 垂直居中 */
}

/* 隐藏框左边的小尖头 */
.at_content_container::before {
    display: none;
}

/* 更多按钮 */
.at_more_btn {
    background: linear-gradient(90deg, #54ad92,#7ac2addb,#62b3c1db,#178f9d) 0% 0% / 400% 400%;
    animation: 30s cubic-bezier(0.8, 0, 0.2, 1.0) 0s infinite normal none running gradientBG;
    color: black;
}
@keyframes gradientBG {
    0% {
        background-position: 0% 50%;
    }
    50% {
        background-position: 100% 50%;
    }
    100% {
        background-position: 0% 50%;
    }
}

先找一张你需要当说说头像的图片
如果需要webp需要抠图 有个网站可以免费扣图: Erase
然后放在ocean主题的 source/image 文件夹下重命名为 artitalk.webp

在根目录 hexo (不是ocean主题) 的 _config.yml 中添加以下内容:

artitalk:
  enable: true
  title: 碎碎念 ## 标题
  path: talk/index.html ## 生成路径
  pageSize: 5 ## 显示几条说说
  avatar: /images/artitalk.webp ## 说说头像
  plugins: [] ## 插件,详见 https://github.com/Lete114/hexo-artitalk-static
  template: ## 说说模板,可查看仓库中的 main.ejs
  imports: ## 在说说模板的开头和结尾处插入内容
    before:
      - 
    after:
      - <link rel="stylesheet" href="/css/artitalk.css">

如果更新了内容记得执行
hexo clean && hexo s

path: artitalk/index.html 代表你用你的域名加’/artitalk’ 就能打开说说界面了, 如果你需要其他链接打开就修改这里

将碎碎念的入口按钮放置在侧边栏中
打开 layout/_partial/sidebar.ejs 文件


<% } %>

<li class="nav-item"><a class="nav-item-link nav-item-search" title="<%= __('search') %>">
之间插入以下代码

<li class="nav-item">
      <a class="nav-item-link" href="/artitalk" title="<%= __('talk') %>" >
        <i class="fe fe-comment-o"></i>
        <%= __('talk') %>
      </a>
    </li>

网页加密

使用 hexo-blog-encrypt 插件
隐藏encrypt again按钮
如果需要显示,请在文章的body添加class: page-specific

.hbe-button{
  display: none !important; // 默认隐藏
}
.page-specific .hbe-button {
  display: block !important; // 在特定网页上显示
}

加入更新时间

参考教程

修改文章模板
Hexo/scaffolds/post.md 里增加 updated

---
title: {{ title }}
date: {{ date }}
updated: {{ date }}
---

主页文章按 更新时间 排序 (可选)
修改 Hexo 不是 ocean 的配置文件 的 order_by
修改完需要执行一下 hexo clean 命令

index_generator:
  path: ''
  per_page: 10
  order_by: -updated

显示更详细的时间 (可选)
找到 date_format 项 改为

date_format: YYYY-MM-DD HH:mm:ss
time_format: HH:mm:ss

layout/_partial/post 中新建 update.ejs

<% if (!is_current("about", false)){ %>
	<a href="<%- url_for(post.path) %>" class="<%= class_name %>">
	  <%= __('updated') %> <time datetime="<%= date_xml(post.updated) %>" itemprop="dateUpdated"><%= date(post.updated, date_format) %></time>
	</a>
<% } %>

修改 layout/_partial/post 中的 date.ejs

<% if (!is_current("about", false)){ %>
	<a href="<%- url_for(post.path) %>" class="<%= class_name %>">
	  <%= __('published') %> <time datetime="<%= date_xml(post.date) %>" itemprop="datePublished"><%= date(post.date, date_format) %></time>
	</a>
<% } %>

/ocean/language/zh-CN.yml 文件中加入以下内容
如果你用的其它语言改对应语言的文件就行
修改完需要执行一下 hexo clean 命令

published: 发布
updated: 更新

layout/_partial/ 中的 article.ejs 插入 4 , 5 行的内容

<% if (is_post()) { %>
<div class="article-meta">
  <%- partial('post/date', {class_name: 'article-date', date_format: null}) %>
  <br>
  <%- partial('post/updated', {class_name: 'article-date', date_format: null}) %>
  <%- partial('post/category') %>
</div>
<% } %>

首页底部鱼儿游

参考在 Flarum 中文社区 第41条 sunjie7777 的配置

source/js 文件夹下新建 fish.js
记得压缩一下代码

var RENDERER = {
    POINT_INTERVAL: 5,
    FISH_COUNT: 3,
    MAX_INTERVAL_COUNT: 50,
    INIT_HEIGHT_RATE: 0.5,
    THRESHOLD: 50,
    init: function () {
      this.setParameters(),
        this.reconstructMethods(),
        this.setup(),
        this.bindEvent(),
        this.render();
    },
    setParameters: function () {
      (this.$window = $(window)),
        (this.$container = $("#flyfish")),
        (this.$canvas = $("<canvas />")),
        (this.context = this.$canvas
          .appendTo(this.$container)
          .get(0)
          .getContext("2d")),
        (this.points = []),
        (this.fishes = []),
        (this.watchIds = []);
    },
    createSurfacePoints: function () {
      var count = Math.round(this.width / this.POINT_INTERVAL);
      (this.pointInterval = this.width / (count - 1)),
        this.points.push(new SURFACE_POINT(this, 0));
      for (var i = 1; i < count; i++) {
        var point = new SURFACE_POINT(this, i * this.pointInterval),
          previous = this.points[i - 1];
        point.setPreviousPoint(previous),
          previous.setNextPoint(point),
          this.points.push(point);
      }
    },
    reconstructMethods: function () {
      (this.watchWindowSize = this.watchWindowSize.bind(this)),
        (this.jdugeToStopResize = this.jdugeToStopResize.bind(this)),
        (this.startEpicenter = this.startEpicenter.bind(this)),
        (this.moveEpicenter = this.moveEpicenter.bind(this)),
        (this.render = this.render.bind(this));
    },
    setup: function () {
      (this.points.length = 0),
        (this.fishes.length = 0),
        (this.watchIds.length = 0),
        (this.intervalCount = this.MAX_INTERVAL_COUNT),
        (this.width = this.$container.width()),
        (this.height = this.$container.height()),
        (this.fishCount =
          (((this.FISH_COUNT * this.width) / 500) * this.height) / 500),
        this.$canvas.attr({ width: this.width, height: this.height }),
        (this.reverse = !1),
        this.fishes.push(new FISH(this)),
        this.createSurfacePoints();
    },
    watchWindowSize: function () {
      this.clearTimer(),
        (this.tmpWidth = this.$window.width()),
        (this.tmpHeight = this.$window.height()),
        this.watchIds.push(
          setTimeout(this.jdugeToStopResize, this.WATCH_INTERVAL)
        );
    },
    clearTimer: function () {
      for (; this.watchIds.length > 0; ) clearTimeout(this.watchIds.pop());
    },
    jdugeToStopResize: function () {
      var width = this.$window.width(),
        height = this.$window.height(),
        stopped = width == this.tmpWidth && height == this.tmpHeight;
      (this.tmpWidth = width),
        (this.tmpHeight = height),
        stopped && this.setup();
    },
    bindEvent: function () {
      this.$window.on("resize", this.watchWindowSize),
        this.$container.on("mouseenter", this.startEpicenter),
        this.$container.on("mousemove", this.moveEpicenter);
    },
    getAxis: function (event) {
      var offset = this.$container.offset();
      return {
        x: event.clientX - offset.left + this.$window.scrollLeft(),
        y: event.clientY - offset.top + this.$window.scrollTop(),
      };
    },
    startEpicenter: function (event) {
      this.axis = this.getAxis(event);
    },
    moveEpicenter: function (event) {
      var axis = this.getAxis(event);
      this.axis || (this.axis = axis),
        this.generateEpicenter(axis.x, axis.y, axis.y - this.axis.y),
        (this.axis = axis);
    },
    generateEpicenter: function (x, y, velocity) {
      if (
        !(
          y < this.height / 2 - this.THRESHOLD ||
          y > this.height / 2 + this.THRESHOLD
        )
      ) {
        var index = Math.round(x / this.pointInterval);
        index < 0 ||
          index >= this.points.length ||
          this.points[index].interfere(y, velocity);
      }
    },
    controlStatus: function () {
      for (var i = 0, count = this.points.length; i < count; i++)
        this.points[i].updateSelf();
      for (var i = 0, count = this.points.length; i < count; i++)
        this.points[i].updateNeighbors();
      this.fishes.length < this.fishCount &&
        0 == --this.intervalCount &&
        ((this.intervalCount = this.MAX_INTERVAL_COUNT),
        this.fishes.push(new FISH(this)));
    },
    render: function () {
      requestAnimationFrame(this.render),
        this.controlStatus(),
        this.context.clearRect(0, 0, this.width, this.height),
        getComputedStyle(this.$container[0]).color !=
        getComputedStyle(this.$container[0].parentNode).color
          ? (this.context.fillStyle = getComputedStyle(
              this.$container[0]
            ).color)
          : (this.context.fillStyle = "hsl(0, 0%, 95%)"),
        getComputedStyle(this.$container[0]).backgroundColor !=
          getComputedStyle(this.$container[0].parentNode).backgroundColor &&
          (this.context.canvas.style.backgroundColor = getComputedStyle(
            this.$container[0]
          ).backgroundColor);
      for (var i = 0, count = this.fishes.length; i < count; i++)
        this.fishes[i].render(this.context);
      this.context.save(),
        (this.context.globalCompositeOperation = "xor"),
        this.context.beginPath(),
        this.context.moveTo(0, this.reverse ? 0 : this.height);
      for (var i = 0, count = this.points.length; i < count; i++)
        this.points[i].render(this.context);
      this.context.lineTo(this.width, this.reverse ? 0 : this.height),
        this.context.closePath(),
        this.context.fill(),
        this.context.restore();
    },
  },
  SURFACE_POINT = function (renderer, x) {
    (this.renderer = renderer), (this.x = x), this.init();
  };
SURFACE_POINT.prototype = {
  SPRING_CONSTANT: 0.03,
  SPRING_FRICTION: 0.9,
  WAVE_SPREAD: 0.3,
  ACCELARATION_RATE: 0.01,
  init: function () {
    (this.initHeight = this.renderer.height * this.renderer.INIT_HEIGHT_RATE),
      (this.height = this.initHeight),
      (this.fy = 0),
      (this.force = { previous: 0, next: 0 });
  },
  setPreviousPoint: function (previous) {
    this.previous = previous;
  },
  setNextPoint: function (next) {
    this.next = next;
  },
  interfere: function (y, velocity) {
    this.fy =
      this.renderer.height *
      this.ACCELARATION_RATE *
      (this.renderer.height - this.height - y >= 0 ? -1 : 1) *
      Math.abs(velocity);
  },
  updateSelf: function () {
    (this.fy += this.SPRING_CONSTANT * (this.initHeight - this.height)),
      (this.fy *= this.SPRING_FRICTION),
      (this.height += this.fy);
  },
  updateNeighbors: function () {
    this.previous &&
      (this.force.previous =
        this.WAVE_SPREAD * (this.height - this.previous.height)),
      this.next &&
        (this.force.next = this.WAVE_SPREAD * (this.height - this.next.height));
  },
  render: function (context) {
    this.previous &&
      ((this.previous.height += this.force.previous),
      (this.previous.fy += this.force.previous)),
      this.next &&
        ((this.next.height += this.force.next),
        (this.next.fy += this.force.next)),
      context.lineTo(this.x, this.renderer.height - this.height);
  },
};
var FISH = function (renderer) {
  (this.renderer = renderer), this.init();
};
(FISH.prototype = {
  GRAVITY: 0.4,
  init: function () {
    (this.direction = Math.random() < 0.5),
      (this.x = this.direction
        ? this.renderer.width + this.renderer.THRESHOLD
        : -this.renderer.THRESHOLD),
      (this.previousY = this.y),
      (this.vx = this.getRandomValue(4, 10) * (this.direction ? -1 : 1)),
      this.renderer.reverse
        ? ((this.y = this.getRandomValue(
            (1 * this.renderer.height) / 10,
            (4 * this.renderer.height) / 10
          )),
          (this.vy = this.getRandomValue(2, 5)),
          (this.ay = this.getRandomValue(0.05, 0.2)))
        : ((this.y = this.getRandomValue(
            (6 * this.renderer.height) / 10,
            (9 * this.renderer.height) / 10
          )),
          (this.vy = this.getRandomValue(-5, -2)),
          (this.ay = this.getRandomValue(-0.2, -0.05))),
      (this.isOut = !1),
      (this.theta = 0),
      (this.phi = 0);
  },
  getRandomValue: function (min, max) {
    return min + (max - min) * Math.random();
  },
  controlStatus: function (context) {
    (this.previousY = this.y),
      (this.x += this.vx),
      (this.y += this.vy),
      (this.vy += this.ay),
      this.renderer.reverse
        ? this.y > this.renderer.height * this.renderer.INIT_HEIGHT_RATE
          ? ((this.vy -= this.GRAVITY), (this.isOut = !0))
          : (this.isOut && (this.ay = this.getRandomValue(0.05, 0.2)),
            (this.isOut = !1))
        : this.y < this.renderer.height * this.renderer.INIT_HEIGHT_RATE
        ? ((this.vy += this.GRAVITY), (this.isOut = !0))
        : (this.isOut && (this.ay = this.getRandomValue(-0.2, -0.05)),
          (this.isOut = !1)),
      this.isOut ||
        ((this.theta += Math.PI / 20),
        (this.theta %= 2 * Math.PI),
        (this.phi += Math.PI / 30),
        (this.phi %= 2 * Math.PI)),
      this.renderer.generateEpicenter(
        this.x + (this.direction ? -1 : 1) * this.renderer.THRESHOLD,
        this.y,
        this.y - this.previousY
      ),
      ((this.vx > 0 &&
        this.x > this.renderer.width + this.renderer.THRESHOLD) ||
        (this.vx < 0 && this.x < -this.renderer.THRESHOLD)) &&
        this.init();
  },
  render: function (context) {
    context.save(),
      context.translate(this.x, this.y),
      context.rotate(Math.PI + Math.atan2(this.vy, this.vx)),
      context.scale(1, this.direction ? 1 : -1),
      context.beginPath(),
      context.moveTo(-30, 0),
      context.bezierCurveTo(-20, 15, 15, 10, 40, 0),
      context.bezierCurveTo(15, -10, -20, -15, -30, 0),
      context.fill(),
      context.save(),
      context.translate(40, 0),
      context.scale(0.9 + 0.2 * Math.sin(this.theta), 1),
      context.beginPath(),
      context.moveTo(0, 0),
      context.quadraticCurveTo(5, 10, 20, 8),
      context.quadraticCurveTo(12, 5, 10, 0),
      context.quadraticCurveTo(12, -5, 20, -8),
      context.quadraticCurveTo(5, -10, 0, 0),
      context.fill(),
      context.restore(),
      context.save(),
      context.translate(-3, 0),
      context.rotate(
        (Math.PI / 3 + (Math.PI / 10) * Math.sin(this.phi)) *
          (this.renderer.reverse ? -1 : 1)
      ),
      context.beginPath(),
      this.renderer.reverse
        ? (context.moveTo(5, 0),
          context.bezierCurveTo(10, 10, 10, 30, 0, 40),
          context.bezierCurveTo(-12, 25, -8, 10, 0, 0))
        : (context.moveTo(-5, 0),
          context.bezierCurveTo(-10, -10, -10, -30, 0, -40),
          context.bezierCurveTo(12, -25, 8, -10, 0, 0)),
      context.closePath(),
      context.fill(),
      context.restore(),
      context.restore(),
      this.controlStatus(context);
  },
}),
  (window.onload = function () {
    RENDERER.init();
  });

因为我文章底部已经有石蒜模拟器了 所以只在首页引用小鱼儿们
故只插入在 index.ejs

如果想要全显示出来就用这个

<!-- 底部小鱼 -->
<div id="flyfish" class="flyfish"></div>
<script src='js/fish.js'></script>

如果只想像我一样显示一半就用这个

<!-- 底部小鱼 -->
<div class="fishbox">
  <div id="flyfish" class="flyfish"></div>
  <script src='js/fish.js'></script>
</div>  

直接在 layout.styl 中加入样式
emmm 我不知道这个line-height = 0 起什么作用 删了改了好像没啥影响 就留着吧
如果想要全显示出来就用这个

.flyfish{
  z-index: -1;
  line-height: 0px;
  height: 350px;
  color:rgb(125, 200, 225);
  background:rgba(253, 253, 253, 0);
}

如果只想像我一样显示一半就用这个

.fishbox{
  z-index: -1;
  position:relative;
  height: 250px;
  overflow:hidden;
}
.flyfish{
  position:absolute;
  height: 500px;
  width: 100%
  bottom:-245px;
  line-height: 0px;
  color:rgb(125, 200, 225);
  background:rgba(232, 22, 22, 0);
}

代码块

这个折叠展开还不错的代码块
目前有个代码全屏后无法在最上层的bug 所以全屏浏览的时候点击再滚动整体文章会滚动
感觉还是不如 shoka 主题的代码块来得好

官方说
Hexo 内建的 PrismJS 支持浏览器端高亮(preprocess 设置为 false)和服务器端高亮(preprocess 设置为 true)两种方式。
preprocess 设置为 false 时所有 primejs 插件均可用
但我折腾了一番 好像也是没法完全使用 primejs 插件 不过大体功能是有的 无伤大雅

先将 highlight 设置为 false
再开启 prismjs 再设置成如下

highlight:
  enable: false

prismjs:
  enable: true
  preprocess: false
  line_number: true
  tab_replace: ''

prism 主题 本站使用的是 lucario 主题
之前想说加个代码全屏预览功能 后面真找到一个感谢感谢😘 方案
只不过他的 Prism.js 是包含全语言的 有点大 而且感觉不是很必要 (当然你也可以直接用他的啦)
所以我选择从官方 这里订制js
然后在下载下来的js文件末尾加入以下实现 全屏和折叠 的代码 (可选)
最后复制 prism.jssource/js 文件夹下

!(function () {
    function u(t, e) {
      // 全屏/缩小
      var fullscreen = false;
      t.addEventListener("click", function (t) {
        var c = t.currentTarget.parentNode.parentNode.parentNode;
        var s = t.currentTarget.firstChild;
        if (fullscreen) {
          // 替换样式: code-fullscreen → code-toolbar
          var classVal = c.getAttribute("class");
          classVal = classVal.replace("code-fullscreen", "code-toolbar");
          c.setAttribute("class", classVal);
          s.firstChild.textContent = "🔍";
        } else {
          // 替换样式: code-toolbar → code-fullscreen
          var classVal = c.getAttribute("class");
          classVal = classVal.replace("code-toolbar", "code-fullscreen");
          c.setAttribute("class", classVal);
          s.firstChild.textContent = "🧐";
        }
        fullscreen = !fullscreen;
      });
      t.addEventListener("keydown", function (t) {
        var c = t.currentTarget.parentNode.parentNode.parentNode;
        var s = t.currentTarget.firstChild;
        c.setAttribute("tabindex", 0);
        c.onkeydown = function () {
          switch (
            t.keyCode // 获取当前按下键盘键的编码
          ) {
            case 27: // 按下ESC键,退出代码全屏
              var classVal = c.getAttribute("class");
              classVal = classVal.replace("code-fullscreen", "code-toolbar");
              c.setAttribute("class", classVal);
              s.firstChild.textContent = "🔍";
              break;
          }
          fullscreen = false;
        };
      });
    }
    "undefined" != typeof Prism &&
      "undefined" != typeof document &&
      (Prism.plugins.toolbar
        ? Prism.plugins.toolbar.registerButton("code-fullscreen", function (t) {
            var e = t.element,
              o = (function (t) {
                var e = {
                  fullscreen: "🔍"
                };
                for (var o in e) {
                  for (
                    var n = "data-prismjs-" + o, c = t;
                    c && !c.hasAttribute(n);
                  )
                    c = c.parentElement;
                  c && (e[o] = c.getAttribute(n));
                }
                return e;
              })(e),
              n = document.createElement("button");
            (n.className = "code-fullscreen-button"),
              n.setAttribute("type", "button");
            var c = document.createElement("span");
            return n.appendChild(c), i("fullscreen"), u(n), n;
            function i(t) {
              (c.textContent = o[t]), n.setAttribute("data-fullscreen-state", t);
            }
          })
        : console.warn(
            "Code to Fullscreen plugin loaded before Toolbar plugin."
          ));
  })();
  !(function () {
    function u(t, e) {
      // 收缩/展开
      var collapsed = false;
      t.addEventListener("click", function (t) {
        var c = t.currentTarget.parentNode.parentNode.previousSibling.firstChild;
        var s = t.currentTarget.firstChild;
        if (collapsed) {
          c.setAttribute("style", "display:block");
          s.firstChild.textContent = "📌";
        } else {
          c.setAttribute("style", "display:none");
          s.setAttribute("data-collapsed-state", "collapsed");
          s.firstChild.textContent = "📍";
        }
        collapsed = !collapsed;
      });
    }
    "undefined" != typeof Prism &&
      "undefined" != typeof document &&
      (Prism.plugins.toolbar
        ? Prism.plugins.toolbar.registerButton("code-collapsed", function (t) {
            var e = t.element,
              o = (function (t) {
                var e = {
                  collapsed: "📌"
                };
                for (var o in e) {
                  for (
                    var n = "data-prismjs-" + o, c = t;
                    c && !c.hasAttribute(n);
                  )
                    c = c.parentElement;
                  c && (e[o] = c.getAttribute(n));
                }
                return e;
              })(e),
              n = document.createElement("button");
            (n.className = "code-collapsed-button"),
              n.setAttribute("type", "button");
            var c = document.createElement("span");
            return n.appendChild(c), i("collapsed"), u(n), n;
            function i(t) {
              (c.textContent = o[t]), n.setAttribute("data-collapsed-state", t);
            }
          })
        : console.warn("Code to collapsed plugin loaded before Toolbar plugin."));
  })();

layout.ejs 中加入

<script src="/js/prism.js"></script>

css/_partial 中新建 prism.styl

code[class*="language-"],
pre[class*="language-"] {
	color: #ccc;
	background: none;
	font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
	font-size: 1em;
	text-align: left;
	white-space: pre;
	word-spacing: normal;
	word-break: normal;
	word-wrap: normal;
	line-height: 1.5;

	-moz-tab-size: 4;
	-o-tab-size: 4;
	tab-size: 4;

	-webkit-hyphens: none;
	-moz-hyphens: none;
	-ms-hyphens: none;
	hyphens: none;

}

/* Code blocks */
pre[class*="language-"] {
	padding: 1em;
	margin: .5em 0;
	overflow: auto;
}

:not(pre) > code[class*="language-"],
pre[class*="language-"] {
	background: rgb(11, 66, 85);
}

/* Inline code */
:not(pre) > code[class*="language-"] {
	padding: .1em;
	border-radius: .3em;
	white-space: normal;
}

.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
	color: #999;
}

.token.punctuation {
	color: #ccc;
}

.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
	color: #e2777a;
}

.token.function-name {
	color: #6196cc;
}

.token.boolean,
.token.number,
.token.function {
	color: #f08d49;
}

.token.property,
.token.class-name,
.token.constant,
.token.symbol {
	color: #f8c555;
}

.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
	color: #cc99cd;
}

.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
	color: #7ec699;
}

.token.operator,
.token.entity,
.token.url {
	color: #67cdcc;
}

.token.important,
.token.bold {
	font-weight: bold;
}
.token.italic {
	font-style: italic;
}

.token.entity {
	cursor: help;
}

.token.inserted {
	color: green;
}

//以下为插件样式
pre[class*="language-"].line-numbers {
	position: relative;
	padding-left: 3.8em;
	counter-reset: linenumber;
}

pre[class*="language-"].line-numbers > code {
	position: relative;
	white-space: inherit;
}

.line-numbers .line-numbers-rows {
	position: absolute;
	pointer-events: none;
	top: 0;
	font-size: 100%;
	left: -3.8em;
	width: 3em; /* works for line-numbers below 1000 lines */
	letter-spacing: -1px;
	border-right: 1px solid #999;

	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;

}

	.line-numbers-rows > span {
		display: block;
		counter-increment: linenumber;
	}

		.line-numbers-rows > span:before {
			content: counter(linenumber);
			color: #999;
			display: block;
			padding-right: 0.8em;
			text-align: right;
		}

.token a {
	color: inherit;
}
div.code-toolbar {
	position: relative;
}
//缩小时按钮位置
div.code-toolbar > .toolbar {
	position: absolute;
	top: -2em;
	right: 0.4em;
	transition: opacity 0.3s ease-in-out;
	opacity: 0;
}

div.code-toolbar:hover > .toolbar {
	opacity: 1;
}

/* Separate line b/c rules are thrown out if selector is invalid.
   IE11 and old Edge versions don't support :focus-within. */
div.code-toolbar:focus-within > .toolbar {
	opacity: 1;
}

div.code-toolbar > .toolbar > .toolbar-item {
	display: inline-block;
}

div.code-toolbar > .toolbar > .toolbar-item > a {
	cursor: pointer;
}

div.code-toolbar > .toolbar > .toolbar-item > button {
	background: none;
	border: 0;
	color: inherit;
	font: inherit;
	line-height: normal;
	overflow: visible;
	padding: 0;
	-webkit-user-select: none; /* for button */
	-moz-user-select: none;
	-ms-user-select: none;
}

div.code-toolbar > .toolbar > .toolbar-item > a,
div.code-toolbar > .toolbar > .toolbar-item > button,
div.code-toolbar > .toolbar > .toolbar-item > span {
	color: #bbb;
	font-size: 1.2em;
	padding: 0 em;
	background: #f5f2f000;
	background: rgba(224, 224, 224, 0);
	box-shadow: 0 2px 0 0 rgba(246, 237, 237, 0);
	border-radius: 0em;
}

div.code-toolbar > .toolbar > .toolbar-item > a:hover,
div.code-toolbar > .toolbar > .toolbar-item > a:focus,
div.code-toolbar > .toolbar > .toolbar-item > button:hover,
div.code-toolbar > .toolbar > .toolbar-item > button:focus,
div.code-toolbar > .toolbar > .toolbar-item > span:hover,
div.code-toolbar > .toolbar > .toolbar-item > span:focus {
	color: inherit;
	text-decoration: none;
}


/*代码高亮全屏显示*/
.code-fullscreen {
	position: fixed ;
	top: 0;
    right: 0;
    bottom: 0;
    left: 0;
	transition-duration: .25s;
	z-index: 100000
}

.code-fullscreen pre {
	position: absolute !important;
	left: 0;
	right: 0;
	top: 0;
	bottom: 0;
	margin-top: 0 !important;
	margin-bottom: 0 !important;
    border: none
	
}

// .code-fullscreen-body {
// 	overflow: hidden;
// 	max-height: 100vh
// }

//全屏时按钮位置
div.code-fullscreen > .toolbar {
	position: absolute;
	top: 0em;
	right: 3em;
	transition: opacity 0.3s ease-in-out;
	opacity: 0;
}
div.code-fullscreen:hover > .toolbar {
	opacity: 1;
}
/* Separate line b/c rules are thrown out if selector is invalid.
   IE11 and old Edge versions don't support :focus-within. */
div.code-fullscreen:focus-within > .toolbar {
	opacity: 1;
}
div.code-fullscreen > .toolbar > .toolbar-item {
	display: inline-block;
}
//隐藏全屏后的📌按钮
div.code-fullscreen > .toolbar > .toolbar-item:nth-child(3) {
	display: none;
}
div.code-fullscreen > .toolbar > .toolbar-item > a {
	cursor: pointer;
}

div.code-fullscreen > .toolbar > .toolbar-item > button {
	background: none;
	border: 0;
	color: inherit;
	font: inherit;
	line-height: normal;
	overflow: visible;
	padding: 0;
	-webkit-user-select: none; /* for button */
	-moz-user-select: none;
	-ms-user-select: none;
}
div.code-fullscreen > .toolbar > .toolbar-item > a,
div.code-fullscreen > .toolbar > .toolbar-item > button,
div.code-fullscreen > .toolbar > .toolbar-item > span {
	color: #bbb;
	font-size: 1.3em;
	padding: 0 0.5em;
	background: #f5f2f000;
	background: rgba(224, 224, 224, 0);
	box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0);
	border-radius: .5em;
}

div.code-fullscreen > .toolbar > .toolbar-item > a:hover,
div.code-fullscreen > .toolbar > .toolbar-item > a:focus,
div.code-fullscreen > .toolbar > .toolbar-item > button:hover,
div.code-fullscreen > .toolbar > .toolbar-item > button:focus,
div.code-fullscreen > .toolbar > .toolbar-item > span:hover,
div.code-fullscreen > .toolbar > .toolbar-item > span:focus {
	color: inherit;
	text-decoration: none;
}

style.styl 中引入

@import "_partial/prism"

平滑滚动

加入网页平滑滚动优化浏览体验
参考教程

js 文件夹中 新建 smooth-scrolling.js 文件
粘贴教程中的代码

(function(){var defaultOptions={frameRate:150,animationTime:400,stepSize:100,pulseAlgorithm:true,pulseScale:4,pulseNormalize:1,accelerationDelta:50,accelerationMax:3,keyboardSupport:true,arrowScroll:50,fixedBackground:true,excluded:''};var options=defaultOptions;var isExcluded=false;var isFrame=false;var direction={x:0,y:0};var initDone=false;var root=document.documentElement;var activeElement;var observer;var refreshSize;var deltaBuffer=[];var deltaBufferTimer;var isMac=/^Mac/.test(navigator.platform);var key={left:37,up:38,right:39,down:40,spacebar:32,pageup:33,pagedown:34,end:35,home:36};var arrowKeys={37:1,38:1,39:1,40:1};function initTest(){if(options.keyboardSupport){addEvent('keydown',keydown);}}
function init(){if(initDone||!document.body)return;initDone=true;var body=document.body;var html=document.documentElement;var windowHeight=window.innerHeight;var scrollHeight=body.scrollHeight;root=(document.compatMode.indexOf('CSS')>=0)?html:body;activeElement=body;initTest();if(top!=self){isFrame=true;}
else if(isOldSafari&&scrollHeight>windowHeight&&(body.offsetHeight<=windowHeight||html.offsetHeight<=windowHeight)){var fullPageElem=document.createElement('div');fullPageElem.style.cssText='position:absolute; z-index:-10000; '+
'top:0; left:0; right:0; height:'+
root.scrollHeight+'px';document.body.appendChild(fullPageElem);var pendingRefresh;refreshSize=function(){if(pendingRefresh)return;pendingRefresh=setTimeout(function(){if(isExcluded)return;fullPageElem.style.height='0';fullPageElem.style.height=root.scrollHeight+'px';pendingRefresh=null;},500);};setTimeout(refreshSize,10);addEvent('resize',refreshSize);var config={attributes:true,childList:true,characterData:false};observer=new MutationObserver(refreshSize);observer.observe(body,config);if(root.offsetHeight<=windowHeight){var clearfix=document.createElement('div');clearfix.style.clear='both';body.appendChild(clearfix);}}
if(!options.fixedBackground&&!isExcluded){body.style.backgroundAttachment='scroll';html.style.backgroundAttachment='scroll';}}
function cleanup(){observer&&observer.disconnect();removeEvent(wheelEvent,wheel);removeEvent('mousedown',mousedown);removeEvent('keydown',keydown);removeEvent('resize',refreshSize);removeEvent('load',init);}
var que=[];var pending=false;var lastScroll=Date.now();function scrollArray(elem,left,top){directionCheck(left,top);if(options.accelerationMax!=1){var now=Date.now();var elapsed=now-lastScroll;if(elapsed<options.accelerationDelta){var factor=(1+(50/elapsed))/2;if(factor>1){factor=Math.min(factor,options.accelerationMax);left*=factor;top*=factor;}}
lastScroll=Date.now();}
que.push({x:left,y:top,lastX:(left<0)?0.99:-0.99,lastY:(top<0)?0.99:-0.99,start:Date.now()});if(pending){return;}
var scrollRoot=getScrollRoot();var isWindowScroll=(elem===scrollRoot||elem===document.body);if(elem.$scrollBehavior==null&&isScrollBehaviorSmooth(elem)){elem.$scrollBehavior=elem.style.scrollBehavior;elem.style.scrollBehavior='auto';}
var step=function(time){var now=Date.now();var scrollX=0;var scrollY=0;for(var i=0;i<que.length;i++){var item=que[i];var elapsed=now-item.start;var finished=(elapsed>=options.animationTime);var position=(finished)?1:elapsed/options.animationTime;if(options.pulseAlgorithm){position=pulse(position);}
var x=(item.x*position-item.lastX)>>0;var y=(item.y*position-item.lastY)>>0;scrollX+=x;scrollY+=y;item.lastX+=x;item.lastY+=y;if(finished){que.splice(i,1);i--;}}
if(isWindowScroll){window.scrollBy(scrollX,scrollY);}
else{if(scrollX)elem.scrollLeft+=scrollX;if(scrollY)elem.scrollTop+=scrollY;}
if(!left&&!top){que=[];}
if(que.length){requestFrame(step,elem,(1000/options.frameRate+1));}else{pending=false;if(elem.$scrollBehavior!=null){elem.style.scrollBehavior=elem.$scrollBehavior;elem.$scrollBehavior=null;}}};requestFrame(step,elem,0);pending=true;}
function wheel(event){if(!initDone){init();}
var target=event.target;if(event.defaultPrevented||event.ctrlKey){return true;}
if(isNodeName(activeElement,'embed')||(isNodeName(target,'embed')&&/\.pdf/i.test(target.src))||isNodeName(activeElement,'object')||target.shadowRoot){return true;}
var deltaX=-event.wheelDeltaX||event.deltaX||0;var deltaY=-event.wheelDeltaY||event.deltaY||0;if(isMac){if(event.wheelDeltaX&&isDivisible(event.wheelDeltaX,120)){deltaX=-120*(event.wheelDeltaX/Math.abs(event.wheelDeltaX));}
if(event.wheelDeltaY&&isDivisible(event.wheelDeltaY,120)){deltaY=-120*(event.wheelDeltaY/Math.abs(event.wheelDeltaY));}}
if(!deltaX&&!deltaY){deltaY=-event.wheelDelta||0;}
if(event.deltaMode===1){deltaX*=40;deltaY*=40;}
var overflowing=overflowingAncestor(target);if(!overflowing){if(isFrame&&isChrome){Object.defineProperty(event,"target",{value:window.frameElement});return parent.wheel(event);}
return true;}
if(isTouchpad(deltaY)){return true;}
if(Math.abs(deltaX)>1.2){deltaX*=options.stepSize/120;}
if(Math.abs(deltaY)>1.2){deltaY*=options.stepSize/120;}
scrollArray(overflowing,deltaX,deltaY);event.preventDefault();scheduleClearCache();}
function keydown(event){var target=event.target;var modifier=event.ctrlKey||event.altKey||event.metaKey||(event.shiftKey&&event.keyCode!==key.spacebar);if(!document.body.contains(activeElement)){activeElement=document.activeElement;}
var inputNodeNames=/^(textarea|select|embed|object)$/i;var buttonTypes=/^(button|submit|radio|checkbox|file|color|image)$/i;if(event.defaultPrevented||inputNodeNames.test(target.nodeName)||isNodeName(target,'input')&&!buttonTypes.test(target.type)||isNodeName(activeElement,'video')||isInsideYoutubeVideo(event)||target.isContentEditable||modifier){return true;}
if((isNodeName(target,'button')||isNodeName(target,'input')&&buttonTypes.test(target.type))&&event.keyCode===key.spacebar){return true;}
if(isNodeName(target,'input')&&target.type=='radio'&&arrowKeys[event.keyCode]){return true;}
var shift,x=0,y=0;var overflowing=overflowingAncestor(activeElement);if(!overflowing){return(isFrame&&isChrome)?parent.keydown(event):true;}
var clientHeight=overflowing.clientHeight;if(overflowing==document.body){clientHeight=window.innerHeight;}
switch(event.keyCode){case key.up:y=-options.arrowScroll;break;case key.down:y=options.arrowScroll;break;case key.spacebar:shift=event.shiftKey?1:-1;y=-shift*clientHeight*0.9;break;case key.pageup:y=-clientHeight*0.9;break;case key.pagedown:y=clientHeight*0.9;break;case key.home:if(overflowing==document.body&&document.scrollingElement)
overflowing=document.scrollingElement;y=-overflowing.scrollTop;break;case key.end:var scroll=overflowing.scrollHeight-overflowing.scrollTop;var scrollRemaining=scroll-clientHeight;y=(scrollRemaining>0)?scrollRemaining+10:0;break;case key.left:x=-options.arrowScroll;break;case key.right:x=options.arrowScroll;break;default:return true;}
scrollArray(overflowing,x,y);event.preventDefault();scheduleClearCache();}
function mousedown(event){activeElement=event.target;}
var uniqueID=(function(){var i=0;return function(el){return el.uniqueID||(el.uniqueID=i++);};})();var cacheX={};var cacheY={};var clearCacheTimer;var smoothBehaviorForElement={};function scheduleClearCache(){clearTimeout(clearCacheTimer);clearCacheTimer=setInterval(function(){cacheX=cacheY=smoothBehaviorForElement={};},1*1000);}
function setCache(elems,overflowing,x){var cache=x?cacheX:cacheY;for(var i=elems.length;i--;)
cache[uniqueID(elems[i])]=overflowing;return overflowing;}
function getCache(el,x){return(x?cacheX:cacheY)[uniqueID(el)];}
function overflowingAncestor(el){var elems=[];var body=document.body;var rootScrollHeight=root.scrollHeight;do{var cached=getCache(el,false);if(cached){return setCache(elems,cached);}
elems.push(el);if(rootScrollHeight===el.scrollHeight){var topOverflowsNotHidden=overflowNotHidden(root)&&overflowNotHidden(body);var isOverflowCSS=topOverflowsNotHidden||overflowAutoOrScroll(root);if(isFrame&&isContentOverflowing(root)||!isFrame&&isOverflowCSS){return setCache(elems,getScrollRoot());}}else if(isContentOverflowing(el)&&overflowAutoOrScroll(el)){return setCache(elems,el);}}while((el=el.parentElement));}
function isContentOverflowing(el){return(el.clientHeight+10<el.scrollHeight);}
function overflowNotHidden(el){var overflow=getComputedStyle(el,'').getPropertyValue('overflow-y');return(overflow!=='hidden');}
function overflowAutoOrScroll(el){var overflow=getComputedStyle(el,'').getPropertyValue('overflow-y');return(overflow==='scroll'||overflow==='auto');}
function isScrollBehaviorSmooth(el){var id=uniqueID(el);if(smoothBehaviorForElement[id]==null){var scrollBehavior=getComputedStyle(el,'')['scroll-behavior'];smoothBehaviorForElement[id]=('smooth'==scrollBehavior);}
return smoothBehaviorForElement[id];}
function addEvent(type,fn,arg){window.addEventListener(type,fn,arg||false);}
function removeEvent(type,fn,arg){window.removeEventListener(type,fn,arg||false);}
function isNodeName(el,tag){return el&&(el.nodeName||'').toLowerCase()===tag.toLowerCase();}
function directionCheck(x,y){x=(x>0)?1:-1;y=(y>0)?1:-1;if(direction.x!==x||direction.y!==y){direction.x=x;direction.y=y;que=[];lastScroll=0;}}
if(window.localStorage&&localStorage.SS_deltaBuffer){try{deltaBuffer=localStorage.SS_deltaBuffer.split(',');}catch(e){}}
function isTouchpad(deltaY){if(!deltaY)return;if(!deltaBuffer.length){deltaBuffer=[deltaY,deltaY,deltaY];}
deltaY=Math.abs(deltaY);deltaBuffer.push(deltaY);deltaBuffer.shift();clearTimeout(deltaBufferTimer);deltaBufferTimer=setTimeout(function(){try{localStorage.SS_deltaBuffer=deltaBuffer.join(',');}catch(e){}},1000);var dpiScaledWheelDelta=deltaY>120&&allDeltasDivisableBy(deltaY);return!allDeltasDivisableBy(120)&&!allDeltasDivisableBy(100)&&!dpiScaledWheelDelta;}
function isDivisible(n,divisor){return(Math.floor(n/divisor)==n/divisor);}
function allDeltasDivisableBy(divisor){return(isDivisible(deltaBuffer[0],divisor)&&isDivisible(deltaBuffer[1],divisor)&&isDivisible(deltaBuffer[2],divisor));}
function isInsideYoutubeVideo(event){var elem=event.target;var isControl=false;if(document.URL.indexOf('www.youtube.com/watch')!=-1){do{isControl=(elem.classList&&elem.classList.contains('html5-video-controls'));if(isControl)break;}while((elem=elem.parentNode));}
return isControl;}
var requestFrame=(function(){return(window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(callback,element,delay){window.setTimeout(callback,delay||(1000/60));});})();var MutationObserver=(window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver);var getScrollRoot=(function(){var SCROLL_ROOT=document.scrollingElement;return function(){if(!SCROLL_ROOT){var dummy=document.createElement('div');dummy.style.cssText='height:10000px;width:1px;';document.body.appendChild(dummy);var bodyScrollTop=document.body.scrollTop;var docElScrollTop=document.documentElement.scrollTop;window.scrollBy(0,3);if(document.body.scrollTop!=bodyScrollTop)
(SCROLL_ROOT=document.body);else
(SCROLL_ROOT=document.documentElement);window.scrollBy(0,-3);document.body.removeChild(dummy);}
return SCROLL_ROOT;};})();function pulse_(x){var val,start,expx;x=x*options.pulseScale;if(x<1){val=x-(1-Math.exp(-x));}else{start=Math.exp(-1);x-=1;expx=1-Math.exp(-x);val=start+(expx*(1-start));}
return val*options.pulseNormalize;}
function pulse(x){if(x>=1)return 1;if(x<=0)return 0;if(options.pulseNormalize==1){options.pulseNormalize/=pulse_(1);}
return pulse_(x);}
var userAgent=window.navigator.userAgent;var isEdge=/Edge/.test(userAgent);var isChrome=/chrome/i.test(userAgent)&&!isEdge;var isSafari=/safari/i.test(userAgent)&&!isEdge;var isMobile=/mobile/i.test(userAgent);var isIEWin7=/Windows NT 6.1/i.test(userAgent)&&/rv:11/i.test(userAgent);var isOldSafari=isSafari&&(/Version\/8/i.test(userAgent)||/Version\/9/i.test(userAgent));var isEnabledForBrowser=(isChrome||isSafari||isIEWin7)&&!isMobile;var supportsPassive=false;try{window.addEventListener("test",null,Object.defineProperty({},'passive',{get:function(){supportsPassive=true;}}));}catch(e){}
var wheelOpt=supportsPassive?{passive:false}:false;var wheelEvent='onwheel'in document.createElement('div')?'wheel':'mousewheel';if(wheelEvent&&isEnabledForBrowser){addEvent(wheelEvent,wheel,wheelOpt);addEvent('mousedown',mousedown);addEvent('load',init);}
function SmoothScroll(optionsToSet){for(var key in optionsToSet)
if(defaultOptions.hasOwnProperty(key))
options[key]=optionsToSet[key];}
SmoothScroll.destroy=cleanup;if(window.SmoothScrollOptions)
SmoothScroll(window.SmoothScrollOptions);if(typeof define==='function'&&define.amd)
define(function(){return SmoothScroll;});else if('object'==typeof exports)
module.exports=SmoothScroll;else
window.SmoothScroll=SmoothScroll;})();

layout.ejs 的下方引入

<script src="/js/smooth-scrolling.js"></script>

头像旋转

在 sidebar.styl 中加入

.logo
    img {
    transition-property: all;
    transition-duration: 10s;
    transition-timing-function: ease-in-out;
    }
    img:hover{
    transform: rotate(20turn);
    transition-delay 2s;
    }

在 sidebar.styl 中加入

.logo
  img {
  animation: rotate 10s linear infinite;
  -webkit-animation: rotate 10s linear infinite;
  }
  @keyframes img {
      0% {
          transform: rotate(0);
      }

      25% {
          transform: rotate(90deg);
      }

      50% {
          transform: rotate(180deg);
      }

      75% {
          transform: rotate(270deg);
      }

      100% {
          transform: rotate(360deg);
      }
  }
  @-webkit-keyframes img {
      0% {
          transform: rotate(0);
      }

      25% {
          transform: rotate(90deg);
      }

      50% {
          transform: rotate(180deg);
      }

      75% {
          transform: rotate(270deg);
      }

      100% {
          transform: rotate(360deg);
      }
  }

更换评论系统

twikoo 和 waline 差不多
共同点

  • 免费易用
  • 可用 vercel 加速 (国内访问速度 upup)
  • 解决 valine 安全问题
  • 有很多自定义拓展
  • 数据可迁移
  • 文档写得好

小区别

  • twikoo
    自定义的东西更多 (这也是我最后选它的原因)
    集成设置面板 可在网页直接配置 更方便点

  • waline
    有 表情包搜索 文章反应 挂件 等更多功能
    导入表情更方便

waline

先按 官方文档 部署
到了 html 注入部分 就用下面

在 head.ejs 的 head 中加入

<link rel="stylesheet" href="https://unpkg.com/@waline/client@v2/dist/waline.css">

在 layout/_partial/post 中新建 waline.ejs 并写入

<% if (theme.waline.enable) { %>
  <div id="waline"></div>
  <script type="module">
    import { init } from 'https://unpkg.com/@waline/client@v2/dist/waline.mjs';
    init({
      el: '#waline',
      serverURL: 'https://', //写入你新建的 vercel 网址
      //表情包搜索
      search: false,
      //隐藏右下角 power by waline
      copyright: false,
      //表情
      emoji: [
      '//unpkg.com/@waline/emojis@1.1.0/qq',
      '//unpkg.com/@waline/emojis@1.1.0/bmoji',
      '//unpkg.com/@waline/emojis@1.1.0/tieba',
      ],
    });
  </script>
<% } %>

在 article.ejs 中的 if (is_post()) 中插入

<%- partial('post/waline') %>

在主题配置文件中写入

waline:
  enable: true

自定义样式
在 css/_partial 中新建 waline.styl 写入
文档自定义样式css 变量 里的内容
或者自己通过控制台查找更改对应样式

:root {
  /* 字体大小 */
  --waline-font-size: 16px;
  /* 常规颜色 */
  --waline-white: #fff;
  --waline-light-grey: #999;
  --waline-dark-grey: #666;
  /* 主题色 */
  --waline-theme-color: #27ae60;
  --waline-active-color: #2ecc71;
  /* 布局颜色 */
  --waline-color: #444;
  --waline-bgcolor: #fff;
  --waline-bgcolor-light: #f8f8f8;
  --waline-bgcolor-hover: #f0f0f0;
  --waline-border-color: #ddd;
  --waline-disable-bgcolor: #f8f8f8;
  --waline-disable-color: #bbb;
  --waline-code-bgcolor: #282c34;
  /* 特殊颜色 */
  --waline-bq-color: #f0f0f0;
  /* 头像 */
  --waline-avatar-size: 3.25rem;
  --waline-m-avatar-size: calc(var(--waline-avatar-size) * 9 / 13);
  /* 徽章 */
  --waline-badge-color: #3498db;
  --waline-badge-font-size: 0.775em;
  /* 信息 */
  --waline-info-bgcolor: #f8f8f8;
  --waline-info-color: #999;
  --waline-info-font-size: 0.625em;
  /* 渲染选择 */
  --waline-border: 1px solid var(--waline-border-color);
  --waline-avatar-radius: 50%;
  --waline-box-shadow: none;
}
/* 根据用户设置 ↓ */
darkmode-selector {
  /* 常规颜色 */
  --waline-white: #000;
  --waline-light-grey: #666;
  --waline-dark-grey: #999;
  /* 布局颜色 */
  --waline-color: #888;
  --waline-bgcolor: #1e1e1e;
  --waline-bgcolor-light: #272727;
  --waline-border-color: #333;
  --waline-disable-bgcolor: #444;
  --waline-disable-color: #272727;
  /* 特殊颜色 */
  --waline-bq-color: #272727;
  /* 其他颜色 */
  --waline-info-bgcolor: #272727;
  --waline-info-color: #666;
}

在 style.styl 中加入

@import "_partial/waline"

twikoo

先按 官方文档 注册配置喔
久没用的话需要到 MongoDB 中的 Project 0 -> 左侧 Database -> 重新启用
vercel 和 netlify 还需要在github中更新 package.json 文件中的版本号

如果忘记twikoo的管理密码
需要到 MongoDB 中的 Project 0 -> 左侧 Database -> Clusters -> Browse Collections -> config 删除 AKISMET_KEY 行 点击 update 最后到twikoo界面重新设置密码

layout/_partial/post 中新建 twikoo.ejs
更新版本的话 要去更新一下下文中cdn链接中的版本号

<% if (theme.twikoo.enable) { %>
    <div id="tcomment"></div>
    <script src="https://cdn.staticfile.org/twikoo/1.6.31/twikoo.all.min.js"></script>
    <script defer>
    twikoo.init({
      envId: 'https://xxx.vercel.app', // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
      el: '#tcomment', // 容器元素
      // path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
      lang: 'zh-CN', // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
    })
    </script>
  <% } %>

打开 article.ejs
在最底下 <% if (is_post()) { %> 中加入

<%- partial('post/twikoo') %>

看了别人的 魔改 感觉很不错
但是版本有点低 而且大大也不打算更新

用 order 调换 信息框和输入大框的顺序
然后移除了信息框旁的头像 调换顺序后头像框有点突兀而且占空间 (其实主要是我没法调整 得不到就毁掉)

_partial 中新建 twikoo.styl

//调整顺序 好像只能调整评论框和信息框
@media screen {
    .tk-meta-input{
        order: 2;
    }
    .tk-input.el-textarea{
        order: 1;
    }
}  

.tk-row.actions{
    margin-left: 0;
}
.tk-row .tk-avatar //隐藏头像
.tk-footer  //隐藏右下角 power by
{
    display: none;
}
//图片动画
.twikoo .tk-submit .el-textarea__inner {
    min-height: 210px !important;
    background-position: right 2.5% bottom 0 !important;
    background-size: 50px 56px !important;
    transition: all 0.5s cubic-bezier(0.6, -0.28, 0.74, 0.05) 0s, background-position-y 0.5s cubic-bezier(0.42, 0, 0.2, 1.21) 0s !important;
}
.twikoo .tk-submit .el-textarea__inner:focus {
    background-position-y: 240px !important;
    background-size: 10px 56px !important;
}
//头像蒙版
.tk-avatar.tk-has-avatar {
    background-color: rgba(255, 255, 255, 0);
}
// //昵称邮件网址样式
// .twikoo .el-input-group__prepend {
//     color: currentColor;
//     background-clip: padding-box;
//     background-color: rgba(144, 147, 153, 0);
//     border-color: rgba(144, 147, 153, 0);
// }
//输入框
.twikoo .el-input__inner, .twikoo .el-textarea__inner {
    color: currentColor;
    background-color: transparent;
    border-color: #ffffff00;
}
//输入区
.twikoo .el-textarea__inner {
    color: currentColor;
    background-color: transparent;
    border-color: #fdfdff00;
}
/* 设置文字内容 :nth-child(1)的作用是选择第几个 */
.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(1):before {
    content: '输入QQ号会自动获取昵称和头像🐧';
}
.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(2):before {
    content: '收到回复将会发送到您的邮箱📧';
}
.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(3):before {
    content: '可以通过昵称访问您的网站🔗';
}
/* 当用户点击输入框时显示 */
.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::before,
.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::after {
    display: block;
}
/* 主内容区 */
.el-input.el-input--small.el-input-group.el-input-group--prepend::before {
    /* 先隐藏起来 */
    display: none;
    /* 绝对定位 */
    position: absolute;
    /* 向上移动60像素 */
    top: -60px;
    /* 文字强制不换行,防止left:50%导致的文字换行 */
    white-space: nowrap;
    /* 圆角 */
    border-radius: 10px;
    /* 距离左边50% */
    left: 50%;
    /* 然后再向左边挪动自身的一半,即可实现居中 */
    transform: translate(-50%);
    /* 填充 */
    padding: 14px 18px;
    background: #444;
    color: #fff;
}
/* 小角标 */
.el-input.el-input--small.el-input-group.el-input-group--prepend::after {
    display: none;
    content: '';
    position: absolute;
    /* 内容大小(宽高)为0且边框大小不为0的情况下,每一条边(4个边)都是一个三角形,组成一个正方形。
    我们先将所有边框透明,再给其中的一条边添加颜色就可以实现小三角图标 */
    border: 12px solid transparent;
    border-top-color: #444;
    left: 50%;
    transform: translate(-50%, -48px);
}

在 style.styl 中加入

@import "_partial/twikoo"

在主题配置文件中写入

twikoo:
  enable: true

因为大部分的时候只是想浏览文章 并不需要留言 默认不加载感觉更好一些 网页也加载更快
参考教程
修改 twikoo.ejs

<% if (theme.twikoo.enable) { %>
  
  <link rel="prefetch" as="script" href="/js/twikoo/1.6.31/twikoo.all.min.js" >
    <div style="text-align:center;">
      <% if (post.encrypt) { %>
        <button class="twikoobtn" id="load-twikoobtn" style="display:none;" onclick="twikoobtn.load();">加载评论</button>
      <% } else { %>
        <button class="twikoobtn" id="load-twikoobtn" onclick="twikoobtn.load();">加载评论</button>
      <% } %>
    </div>
    <div id="tcomment"></div>
    <script defer>
      var twikoobtn = {        
        load : function twikoobtn(){
          var preloadedScript = document.createElement("script");
          // preloadedScript.src = "https://cdn.staticfile.org/twikoo/1.6.31/twikoo.all.min.js";
          preloadedScript.src = "/js/twikoo/1.6.31/twikoo.all.min.js";
          document.body.appendChild(preloadedScript);
          setTimeout(()=>{
            twikoo.init({
            envId: 'https://twikoo-chi-ten.vercel.app', // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
            el: '#tcomment', // 容器元素
            // path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
            lang: 'zh-CN', // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
          })
            $('#load-twikoobtn').remove(); ///加载后移除按钮
          
          },500)
        }
      }
      // 监听网页是否解密以显示按钮
      window.addEventListener('hexo-blog-decrypt', function() {
        document.getElementById('load-twikoobtn').style.removeProperty('display'); 
      });
    </script>
  <% } %>
  

按钮样式
twikoo.styl 中添加

.twikoobtn {
    display: inline-block;
    padding: 0 111px;
    font-size: 20px;
    color: rgb(59, 155, 190);
    background: rgb(255, 255, 255);
    border: 3px solid rgb(64, 163, 195);
    text-decoration: none;
    border-radius: 24px;
    transition-property: background-color;
    transition-duration: 0.2s;
    transition-timing-function: ease-in-out;
    transition-delay: 0s;
    line-height: 2;
    letter-spacing:5px
}
.twikoobtn:hover {
    border-color: rgb(25, 136, 146);
    color: #fff;
    background: rgb(25, 136, 146);
}

邮件模板

如果是用的 waline 参见 waline 邮件模板 直接用就行
如果用的是 twikoo 模板 的相关参数要改 对应参数详见 此文章
以下是 紫罗兰永恒花园信笺模板 的 twikoo 配置

不知道为何参数在这一项中不起作用
只能手动更改

您好哇 , 您在『(此处为自己的网站名称)』上的评论收到了回复
<div style="background: url(https://npm.elemecdn.com/sarakale-assets@v1/Article/email/bg2.png);padding:20px 0px 20px;margin:0px;background-color:#d6d6d6;width:100%;">
<style type="text/css">@media screen and (max-width:600px){.afterimg,.beforeimg{display:none!important}}</style><div style="border-radius: 10px 10px 10px 10px;font-size:14px;color: #555555;width: 530px;font-family:'Century Gothic','Trebuchet MS','Hiragino Sans GB',微软雅黑,'Microsoft Yahei',Tahoma,Helvetica,Arial,'SimSun',sans-serif;margin:50px auto;max-width:100%;background: ##ffffff;"><img class="beforeimg" style="margin-top: -30px;margin-bottom: -120px;width:530px;height:317px;z-index:-100;pointer-events:none" src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png"><img src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg" style="width:100%;overflow:hidden;pointer-events:none"><div style="width:100%;background:#f8d1ce;color:#9d2850;border-radius: 10px 10px 0 0;background-image: -moz-linear-gradient(0deg, rgb(67, 198, 184), rgb(255, 209, 244));height: 66px;background: url(https://npm.elemecdn.com/sarakale-assets@v1/Article/email/line034_666x66.png) left top no-repeat;"><p style="font-size:16px;font-weight: bold;text-align:center;word-break:break-all;padding: 23px 32px;margin:0;">您在<a style="text-decoration:none;color: #9d2850;" href="${SITE_URL}">『${SITE_NAME}』</a>上的留言有新回复啦!</p></div><div class="formmain" style="background:#fff;width:96%;max-width:800px;margin:auto auto;border-radius:5px;border: 1px solid #564f4f59;overflow:hidden;pointer-events:none"><div style="margin:40px auto;width:90%;"><p>Hi,${PARENT_NICK},您曾在文章上发表评论:</p><div style="background: #eee;margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;">${PARENT_COMMENT}</div><p><strong>${NICK}</strong> 给您的回复如下:</p><div style="background: #eee;margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;">${COMMENT}</div><p>您可以点击<a style="text-decoration:none; color:#cf5c83" href="${POST_URL}" target="_blank"> 查看回复的完整內容 </a>,欢迎再次光临<a style="text-decoration:none; color:#cf5c83" href="${SITE_URL}" target="_blank"> ${SITE_NAME} </a>。<hr /><p style="font-size:14px;color:#b7adad;text-align:center">本邮件为系统自动发送,请勿直接回复邮件哦,可到博文内容回复。<br /> 此处为你的网站地址 </p></p><img src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png" style="width:100%;margin:25px auto 5px auto;display:block;pointer-events:none"><p class="bottomhr" style="font-size:12px;text-align:center;color:#999">自动书记人偶竭诚为您服务!</p></div></div><img class="afterimg" style="width:530px;height:317px;margin-top: -155px;z-index:100;"src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png"></div></div>
${SITE_NAME} 上有新评论了
<div style="background: url(https://npm.elemecdn.com/sarakale-assets@v1/Article/email/bg2.png);padding:20px 0px 20px;margin:0px;background-color:#d6d6d6;width:100%;"><style type="text/css">@media screen and (max-width:600px){.afterimg,.beforeimg{display:none!important}}</style><div style="border-radius: 10px 10px 10px 10px;font-size:14px;color: #555555;width: 530px;font-family:'Century Gothic','Trebuchet MS','Hiragino Sans GB',微软雅黑,'Microsoft Yahei',Tahoma,Helvetica,Arial,'SimSun',sans-serif;margin:50px auto;max-width:100%;background: ##ffffff;"><img class="beforeimg" style="margin-top: -30px;margin-bottom: -120px;width:530px;height:317px;z-index:-100;pointer-events:none" src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png"><img src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg" style="width:100%;overflow:hidden;pointer-events:none"><div style="width:100%;background:#f8d1ce;color:#9d2850;border-radius: 10px 10px 0 0;background-image: -moz-linear-gradient(0deg, rgb(67, 198, 184), rgb(255, 209, 244));height: 66px;background: url(https://npm.elemecdn.com/sarakale-assets@v1/Article/email/line034_666x66.png) left top no-repeat;"><p style="font-size:16px;font-weight: bold;text-align:center;word-break:break-all;padding: 23px 32px;margin:0;">您在<a style="text-decoration:none;color: #9d2850;" href="${SITE_URL}"target="_blank">${SITE_NAME}</a>上的文章有了新的评论</p></div><div class="formmain" style="background:#fff;width:96%;max-width:800px;margin:auto auto;border-radius:5px;border: 1px solid #564f4f59;overflow:hidden;pointer-events:none"><div style="margin:40px auto;width:90%;"><p><strong>${NICK}</strong> 回复说:</p><div style="background: #eee;margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;">${COMMENT}</div><p style="text-align:center;">您可以点击<a style="text-decoration:none;color:#cf5c83" href="${POST_URL}" target="_blank">查看回复的完整內容</a></p><img src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png" style="width:100%;margin:25px auto 5px auto;display:block;pointer-events:none"><p class="bottomhr" style="font-size:12px;text-align:center;color:#999">自动书记人偶竭诚为您服务!</p></div></div><img class="afterimg" style="width:530px;height:317px;margin-top: -155px;z-index:100;"src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png"></div></div>

石蒜模拟器

项目地址 什么?你说你没看到?拉到最底下后等一会

/layout/_partial/ 文件夹中新建 shisuan.ejs

这个是隐藏款石蒜 要是想要一直显示可以删掉监测滚动和隐藏代码

代码第二行的 js 文件建议下到本地再引用 可以改控制台显示

代码最下面的是防抖节流函数 避免一直检测当前页面位置 参考

<% if (theme.shisuan) { %>
  <script src="https://cdn.jsdelivr.net/npm/sakana"></script>  
  <!-- 石蒜模拟器 -->
  <div class="sakana-box" id="shisuan"></div>
  <script  type="text/javascript">
  function shisuan(){
    Sakana.setMute(true);

    Sakana.init({
      el:         '.sakana-box',     // 启动元素 node 或 选择器
      character:  'chisato',          // 启动角色 'chisato','takina' 
      inertia:    0.03,              // 惯性
      decay:      0.999,              // 衰减
      r:          1,                // 启动角度
      y:          50,                // 启动高度
      translateY: 0,                 // 位移高度
      scale:      0.5,                 // 缩放倍数
      canSwitchCharacter: true,     // 允许换角色
    });
  }
  </script>

  <!-- 底部隐藏 -->
  <script type="text/javascript">
    var showed=0;
  function throttle(fn, wait) {
    let previous = 0, timer = null
    return function (...args) {
      let now = +new Date()
      if (now - previous < wait) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            previous = now
            fn.apply(this, args)
          }, wait)
      } else {
        // 第一次执行
        // 或者时间间隔超出了设定的时间间隔,执行函数 fn
        previous = now
        fn.apply(this, args)
      }
    }
  }
  window.addEventListener("scroll",
  throttle ( function(event)
  {
    if(showed == 0){
      event = event || window.event;
      var scrollTop = $(this).scrollTop(); 
      var documentHeight = $(document).height(); 
      var windowHeight = $(window).height(); 
      if (scrollTop + windowHeight  >= documentHeight - 5 )
      {
        shisuan();
        showed=1; //石蒜出现后不再检测
      }
    } 
  },10000)) ;
  </script>
<% } %>

这步是给底部留足够的空间
修改 footer.styl 中的 padding 值

.footer
  padding 17rem 0

在最底下 <% if (is_post()) { %> 加入

<%- partial('shisuan') %>
<%- partial('_partial/footer') %> 

配置石蒜的位置
在 layout.styl 中加入

.sakana-box{
  position: absolute;
  bottom 0%
  left: 35%;  
  z-index: 9;
  // transform-origin: 100% 100%; /* 从右下开始变换 */
}

右下角可爱返回

参考教程

其他返回顶部按钮

在 js 文件夹中新建 righttotop.js

(function () {
// 移动端不显示
//   if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
//     return
//   }

  var isShow = false, lock = false;
  var $btn = $('.back-to-top');

  $(document).scroll(function () {
    if (lock) return

    if ($(this).scrollTop() >= 800) {  
      if (!isShow) $btn.addClass('load')
      isShow = true
    } else {
      if (isShow) {
        $btn.removeClass('load')
        isShow = false
      }
    }
  })

  $btn.click(function () {
    lock = true
    $btn.addClass('ani-leave')

    $("html, body").animate({ scrollTop: 0 }, 800);

    setTimeout(function () {
      $btn.removeClass('ani-leave').addClass('leaved')
    }, 390)

    setTimeout(function () {
      $btn.addClass('ending')
    }, 120)

    setTimeout(function () {
      $btn.removeClass('load')
    }, 1500);

    setTimeout(function () {
      lock = false
      isShow = false
      $btn.removeClass('leaved ending')
    }, 2000);
  })
})();

在最后加入

<a href="javascript:void(0);"  class="back-to-top"  target="_self"></a>
<script defer src="/js/righttotop.js"></script>

在 css/_partial 文件夹下新建 righttotop.styl

在教程里 第 11 行使用 transition 简写 我这边不知道为什么无法使用简写
折腾半天后面发现 ocean 主题使用简写需要将简写部分加上 () 或者你也可以展开写
建议将图片保存在本地 调用其他网站的不稳定
然后更换下 url 的路径 例如我的路径是 …/images/totop.png

.back-to-top {
  position: fixed;
  z-index: 20;
  right: -108px;
  bottom: 0;
  width: 108px;
  height: 150px;
  background: url("https://cdn.muyu.love/Blog/Handsome/img/back-to-top.png?v=1") no-repeat 0 0;
  background-size: 108px 450px;
  opacity: 0.6;
  transition-property: opacity, right;
  transition-duration: 0.3s, 0.8s;
  transition-timing-function: ease, ease;
  transition-delay: 0s, 0s;
}
.back-to-top:hover {
  background-position: 0 -150px;
  opacity: 1;
}
.back-to-top.load {
  right: 0;
}
.back-to-top.ani-leave {
  background-position: 0 -150px;
  animation: ani-leave 350ms ease-in-out;
  animation-fill-mode: forwards;
}
.back-to-top.leaved {
  pointer-events: none;
  background: none;
  transition: none;
}
.back-to-top.ending {
  pointer-events: none;
}
.back-to-top.ending::after {
  opacity: 1;
}
.back-to-top::after {
  content: '';
  position: fixed;
  z-index: 2;
  right: 0;
  bottom: 0;
  width: 108px;
  height: 150px;
  background: url(../images/totop.png) no-repeat 0 0;
  background-size: 108px 450px;
  background-position: 0 -300px;
  transition: opacity 0.3s;
  opacity: 0;
  pointer-events: none;
}

在 style.styl 中引入

@import "_partial/righttotop"

感觉有了这个返回侧边栏的返回可以取消了 虽然平时也用不到 哈哈
但是我忘了我删了啥 相关的有个 totop.styl 应该还有个 ejs 文件
你们找找看吧 抱歉哇

星空动态背景

星空特效 相关教程

在 js 文件夹下创建 universe.js
原作者让代码在暗色主题下才生效 把 == 改为!=就可一直生效 我已经改好了
至于自定义颜色 更改 a r d 三者的 rgb 值即可

function dark() {
  window.requestAnimationFrame =
    window.requestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.msRequestAnimationFrame;
  var n,
    e,
    i,
    h,
    t = 0.05,
    s = document.getElementById("universe"),
    o = !0,
    a = "230, 163, 18",  /*极个别星星颜色*/
    r = "38, 143, 145",  /*大多数星星颜色*/
    d = "227, 177, 193", /*流星*/
    c = [];
  function f() {
    (n = window.innerWidth),
      (e = window.innerHeight),
      (i = 0.216 * n),
      s.setAttribute("width", n),
      s.setAttribute("height", e);
  }
  function u() {
    h.clearRect(0, 0, n, e);
    for (var t = c.length, i = 0; i < t; i++) {
      var s = c[i];
      s.move(), s.fadeIn(), s.fadeOut(), s.draw();
    }
  }
  function y() {
    (this.reset = function () {
      (this.giant = m(3)),
        (this.comet = !this.giant && !o && m(10)),
        (this.x = l(0, n - 10)),
        (this.y = l(0, e)),
        (this.r = l(1.1, 2.6)),
        (this.dx = l(t, 6 * t) + (this.comet + 1 - 1) * t * l(50, 120) + 2 * t),
        (this.dy = -l(t, 6 * t) - (this.comet + 1 - 1) * t * l(50, 120)),
        (this.fadingOut = null),
        (this.fadingIn = !0),
        (this.opacity = 0),
        (this.opacityTresh = l(0.2, 1 - 0.4 * (this.comet + 1 - 1))),
        (this.do = l(5e-4, 0.002) + 0.001 * (this.comet + 1 - 1));
    }),
      (this.fadeIn = function () {
        this.fadingIn &&
          ((this.fadingIn = !(this.opacity > this.opacityTresh)),
          (this.opacity += this.do));
      }),
      (this.fadeOut = function () {
        this.fadingOut &&
          ((this.fadingOut = !(this.opacity < 0)),
          (this.opacity -= this.do / 2),
          (this.x > n || this.y < 0) && ((this.fadingOut = !1), this.reset()));
      }),
      (this.draw = function () {
        if ((h.beginPath(), this.giant))
          (h.fillStyle = "rgba(" + a + "," + this.opacity + ")"),
            h.arc(this.x, this.y, 2, 0, 2 * Math.PI, !1);
        else if (this.comet) {
          (h.fillStyle = "rgba(" + d + "," + this.opacity + ")"),
            h.arc(this.x, this.y, 1.5, 0, 2 * Math.PI, !1);
          for (var t = 0; t < 30; t++)
            (h.fillStyle =
              "rgba(" +
              d +
              "," +
              (this.opacity - (this.opacity / 20) * t) +
              ")"),
              h.rect(
                this.x - (this.dx / 4) * t,
                this.y - (this.dy / 4) * t - 2,
                2,
                2
              ),
              h.fill();
        } else
          (h.fillStyle = "rgba(" + r + "," + this.opacity + ")"),
            h.rect(this.x, this.y, this.r, this.r);
        h.closePath(), h.fill();
      }),
      (this.move = function () {
        (this.x += this.dx),
          (this.y += this.dy),
          !1 === this.fadingOut && this.reset(),
          (this.x > n - n / 4 || this.y < 0) && (this.fadingOut = !0);
      }),
      setTimeout(function () {
        o = !1;
      }, 50);
  }
  function m(t) {
    return Math.floor(1e3 * Math.random()) + 1 < 10 * t;
  }
  function l(t, i) {
    return Math.random() * (i - t) + t;
  }
  f(),
    window.addEventListener("resize", f, !1),
    (function () {
      h = s.getContext("2d");
      for (var t = 0; t < i; t++) (c[t] = new y()), c[t].reset();
      u();
    })(),
    (function t() {
      document.getElementsByTagName("html")[0].getAttribute("data-theme") !=
        "dark" && u(),
        window.requestAnimationFrame(t);
    })();
}
dark();

在 css 文件夹下创建 universe.css

/* 背景宇宙星光  */
#universe{
display: block;
position: fixed;
margin: 0;
padding: 0;
border: 0;
outline: 0;
left: 0;
top: 0;
width: 100%;
height: 100%;
pointer-events: none;
/* 这个是调置顶的优先级的,-1 在文章页下面,背景上面,个人推荐这种 */
z-index: -1;
}

在 layout.ejs 的 <body>标签下加入

<canvas id="universe"></canvas>
<script defer src="/js/universe.js"></script>

在 head.ejs 的<head>标签下加入

<link rel="stylesheet" href="/css/universe.css">

看板娘

  • 目前使用 爆胎 的看板娘

    • 配置 想要自定义内容和一些样式的话需要添加到自己的仓库
    • npm 使用 npm i live2dnew 命令下载
      生效的只有里面的 live2dnew 文件
  • 扒拉api用这个方法读取浏览器缓存文件 根据文件路径放置相对应的文件夹
    (这方式有点慢,但我不会爬api 只能这么搞…)
    扒拉完记得在 model_list.json 加入文件路径
    更多看板娘信息

  • 糖果屋(大佬的空间特效很多,炫🤩得我打开都得卡很久。)
    提供教程和api 有时候 爆胎 的 api 不稳定 我就换成这位的了

  • api 的一个常见错误
    文档CTRL + F 搜索 点击页面或模型,控制台就会出现报错 可以查看原因

  • live2d V3/V4 模型
    小白教程 对应的 视频教程
    更多内容

  • 樱花庄 原作者
    只有两只 但是非常可爱

    1. caught Error: WebGL unsupported in this browser
      则需要浏览器开启硬件加速
    2. 关闭后看板娘后 再刷新网页 初次图标出现在右下角
      pio_sdk4.js 默认 pio_alignmentright 将其默认改成 left 就可以了
    3. 关闭后看板娘后 再刷新网页 控制台报了个有关 insertAdjacentHTML 的错误
      这个是因为关闭看板娘后无法找到 closeBtn
      只需要再加个判断 if (closeBtn != null) 后 再将 insertAdjacentHTML返回顶部 两部分放到 这个 if 下就行了
    4. 两模型位置不能重叠 不然会出错

双看板娘切换

一个看板娘很多 我就取名lot 一个只有两只我就取名two
实现功能是
有按钮可以手动切换两个看板娘
点击lot的X切换至two 点击two的连接按钮切换至lot 点击two的X收起看板娘
若没有主动按按钮切换,则按模式来
明亮模式冒出的是lot
黑暗模式冒出的是two
tip:模式切换完后需要刷新一下才会切换看板娘

修改 two 看板娘的连接按钮 (X旁边那个)
按下后就two隐藏至左侧屏幕外
然后lot冒出来

//关于我
elements.info.onclick = function () {
  // window.open(prop.content.link || "https://paugram.com/coding/add-poster-girl-with-plugin.html");
  document.getElementById("pio-container").style.left = '-400px';
  two_loaded = 0
  if ( lot_loaded !== 0){
      loadScript("/live2d/live2d-lot/left/autoload.js")
  }
  else {
      localStorage.removeItem("waifu-display");
      document.getElementById("waifu").style.display = "";
      setTimeout(() => {
      document.getElementById("waifu").style.bottom = 0;
      }, 0);
  }
  localStorage.setItem('live2d-click','to-lot');
};

修改lot的关闭按钮
打开搜索 .fa-times 修改为如下

document
  .querySelector("#waifu-tool .fa-times")
  .addEventListener("click", () => {
    // localStorage.setItem("waifu-display", Date.now());
    showMessage("愿你有一天能与重要的人重逢。", 2000, 11);
    document.getElementById("waifu").style.bottom = "-500px";
    setTimeout(() => {
      document.getElementById("waifu").style.display = "none";
      // document
      //   .getElementById("waifu-toggle")
      //   .classList.add("waifu-toggle-active");
      lot_loaded = 0
      if ( two_loaded !== 0){
        loadScript("/live2d/live2d-two/load.js")
      }
      else {
        document.getElementById("pio-container").style.left = 0;
      }
      localStorage.setItem('live2d-click','to-two'); //用于记录用户选择的看板娘类型
    }, 3000);
  });

修复Pio的黑暗模式按钮的功能
思路就是复制黑暗模式的onClick.js 然后修改绑定的按钮

(function() {
    const colorToggleButtonName = "pio-night";
    const button = document.getElementsByClassName(colorToggleButtonName)[0];
    if (button) {
        const BODY = document.getElementsByTagName("body")[0];
        button.addEventListener("mouseup", () => {
            const neko = BODY.createChild("div", {
                id: "neko",
                innerHTML:
        "<div class=\"planet\"><div class=\"sun\"></div><div class=\"moon\"></div></div><div class=\"body\"><div class=\"face\"><section class=\"eyes left\"><span class=\"pupil\"></span></section><section class=\"eyes right\"><span class=\"pupil\"></span></section><span class=\"nose\"></span></div></div>"
            });
            const hideNeko = function() {
                window.CSSTransition(
                    neko,
                    {
                        delay: 2500,
                        opacity: 0
                    },
                    function() {
                        BODY.removeChild(neko);
                    }
                );
            };
            let c;
            if(!document.body.classList.contains('dark')){
                c = function() {
                    neko.addClass("dark");
                    hideNeko();
                };
            } else {
                neko.addClass("dark");
                c = function() {
                    neko.removeClass("dark");
                    hideNeko();
                };
            }
            window.CSSTransition(neko, 1, function() {
                setTimeout(c, 700);
            });
        });
    }
})();

/js/ 文件夹中新建此文件

//标识 避免重复请求资源
var two_loaded = 1 //标识,用于判断two是否已加载过 用于two的js的载入  
var lot_loaded = 1 //标识,用于判断lot是否已加载过 用于lot的js的载入 

//两live2d的切换 优先点击,否则按'亮/暗主题'来
if (localStorage.getItem('live2d-click') === 'to-lot'){
  localStorage.setItem('live2d','lot');
}
else if (localStorage.getItem('live2d-click') === 'to-two'){
  localStorage.setItem('live2d','two');
}
else if (localStorage.getItem('theme') === 'light') {
  localStorage.setItem('live2d','lot');
}
else if (localStorage.getItem('theme') === 'dark') {
  localStorage.setItem('live2d','two');
}

if (localStorage.getItem('live2d') === 'lot'){
  loadScript("/live2d/live2d-lot/left/autoload.js") //看板娘lot
}
if (localStorage.getItem('live2d') === 'two'){
  loadScript("/live2d/live2d-two/load.js") //看板娘two
}

加入 loadscript 函数 (不止看板娘会用到)
引入 live2d.js


var pio_catloaded = 1 //标识,用于判断pio是否已加载过 用于cat切换动画js的载入 
function loadScript(url, cb, isMoudule) {
      var script = document.createElement('script');
      script.src = url;
      if (cb) script.onload = cb; //callback
      if (isMoudule) script.type = 'module';
      script.async = true;
      document.body.appendChild(script);
    };
<script src="/js/live2d.js"></script>

鼠标指针

  • 指针样式
    找个喜欢的指针样式 致美化
    不支持 .ani 动态鼠标 好像是浏览器不支持 ani 转 cur 软件
    指针编辑工具
    调整指针分辨率
    先打开想要编辑的文件–>左上角工具栏–>调整–>图像大小
    建议 32 x 32 大小的指针 不然感觉太大了
    然后在 style.style 中修改相应的路径

  • 点击特效
    参见 松林羊

滚动条

侧边滚动条
其他样式
复制代码加入到 layou.styl 中

顶部滚动条
单色进度条
彩色进度条

head.ejs 文件中加入

<div id="percentageCounter"></div>

新建一个 js 文件 比如我创建的是 gun.js
如果你创建的文件名不同 记得替换为自己创建的 js 文件名
粘贴教程 js 的内容

$(document).ready(function () {
  $(window).scroll(function(){
    $("#percentageCounter").attr("style", "width: " + ($(this).scrollTop() / ($(document).height() - $(this).height()) * 100) + "%; display: block;");
  });
});

layou.styl 中加入 进度条样式代码

#percentageCounter {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 9999;
    display: none;
    width: 0;
    height: 3px;
    background: #6d6d6d;
}

如果是要使用彩色进度条就采用以下配置

#percentageCounter {
  position: fixed;
  left: 0;
  top: 0;
  height: 3px;
  z-index: 99999;
  background-image: linear-gradient(to right, #339933, #ff6666);
  border-radius: 5px;
}

after-footer.ejs 中加入以下代码

<% if (theme.gunjs){ %>
<%- js('/js/gun.js') %>
<% } %> 

在 ocean 主题配置文件中写入

gunjs: true

音乐播放器

我用的是Aplayer播放器
Hexo 有对应插件 hexo-tag-aplayer

使用 hexo-tag-aplayer 首先需要在博客目录下右键打开 Git Bash 输入以下命令安装:

npm install --save hexo-tag-aplayer

以下为 hexo-tag-aplayer 插件使用方式

要修改的是 Hexo 的配置文件, 不是主题的配置文件

这玩意有三种使用方式

  1. 在线直接解析网络上已有的歌单 (只能在线)需开启Meting.js
aplayer:
  meting: true
  1. 读取单独一首歌 (可本地、可在线)
aplayer:
  meting: false
  1. 读取一首一首的歌组成歌单 (可本地、可在线)
aplayer:
  meting: false

如果是本地音乐要播放可以开启 Hexo 的 文章资源文件夹 功能
_config.yml 中找到以下这个选项 将 false 改成 true 即可

post_asset_folder: true

当开启 Hexo 的 文章资源文件夹 功能时,可以将图片、音乐文件、歌词文件放入与文章对应的资源文件夹中,然后直接引用

如果你用的是第一种 Meting.js 的方式加载音乐
那就跳过这步 移步下一项 在网页中插入播放器

我是通过下面这个软件下载音乐资源
洛雪音乐

  1. 选择 电脑版或手机版 一般是用电脑版操作 进入 github 发行界面 往下拉 点击’show all 27 assets’显示所有资源 然后下载对应版本

  2. 首先打开设置 --> 下载设置, 勾选 ‘是否启用下载功能’、‘封面嵌入’、‘歌词下载–>是否启用、同时将翻译歌词写入到歌词文件中’

  3. 再打开设置 --> 基本设置, 勾选’音源名字–>原名’ 然后点击’自定义源管理’ 下载网友收集的可用音源并导入, 导入完再点击相应源初始化一下 , 具体可参照六音音源导入教程

  4. 然后去搜想要的歌曲点看看能不能播放 如果搜不到可以换平台搜(建议不要用聚合搜索,有的有但用聚合搜索搜不出来,一个平台一个平台搜) 一般能搜到能播放的都能下载 如果无法播放或者无法下载就换源

  5. 下载完之后 播放一会mp3文件 目录中就会出现 一个Folder.jpg文件 这个就是歌曲封面了 然后给它改个名字 可以跟歌曲同名方便找

其他下载方式

六音无损音乐下载 各平台搜得到但就是无法下载 可以试试这个网站

Spotify Downloader 有的歌曲各平台好像都没有 去spotify(需要翻墙)找看看 如果有就用这个插件下载 这个插件需要篡改猴扩展支持

LRC歌词下载(需翻墙) 各别歌曲可能歌词无法下载下来 可以用这个网站下下看

  1. 在线直接解析
<!-- 简单示例 (id, server, type)  -->
{% meting "60198" "netease" "playlist" %}

<!-- 进阶示例 -->
{% meting "60198" "netease" "playlist" "autoplay" "mutex:false" "listmaxheight:340px" "preload:none" "theme:#ad7a86"%}
  1. 读取单独一首歌 (可本地、可在线)
    如果开启了 ‘文章资源文件夹’ 功能 就新建一个跟md文件同名的文件夹 然后把资源全部复制到新建的文件夹中 最后在md文件中加入以下内容即可(记得改具体文件名称 就不需要指定路径了)
{% aplayer "歌名" "作者" "歌曲.mp3" "封面.jpg"  "lrc:歌词.lrc"  "width:xx%" %}

如果没有开启’文章资源文件夹’ 功能 那就将对应的文件名换成 歌曲的url链接 或者 本地文件路径

  1. 读取一首一首的歌组成歌单 (可本地、可在线)

如果开启了 ‘文章资源文件夹’ 功能 就新建一个跟md文件同名的文件夹 然后把资源全部复制到新建的文件夹中 最后在md文件中加入以下内容即可(记得改具体文件名称 就不需要指定路径了)

!!!注意注意!!! 这边不要将官方文档中的中文注释也复制进去 不然会报错注释了,但没有完全注释

要弄成以下这样 (其中music的pic、lrc是可选的)

{% aplayerlist %}
{
    "narrow": false,                          
    "autoplay": true,                         
    "mode": "random",                         
    "showlrc": 3,                             
    "mutex": true,                           
    "theme": "#e6d0b2",	                      
    "preload": "metadata",                    
    "listmaxheight": "513px",    
    "music": [
        {
            "title": "CoCo",
            "author": "Jeff Williams",
            "url": "caffeine.mp3",
            "pic": "caffeine.jpg",
            "lrc": "caffeine.lrc"
        },
        {
            "title": "アイロニ",
            "author": "鹿乃",
            "url": "irony.mp3",
            "pic": "irony.jpg"
        }
    ]
}
{% endaplayerlist %}

如果没有开启’文章资源文件夹’ 功能 那就将对应的文件名换成 歌曲的url链接 或者 本地文件路径没试过,应该可行

首先复制 node_modules\hexo-tag-aplayer\node_modules\aplayer\dist 路径中的 APlayer.min.css 文件 放到 themes/ocean/source/css
我是先将 aplayer.min.css 格式化成 aplayer.css 放在同目录下方便修改配置
然后Minify压缩 aplayer.css 后就会自己就更新 min文件

具体想改什么自己F12自己改喏

然后在Hexo 的配置文件的aplayer下加入

aplayer:
  style_cdn: /css/aplayer.min.css

最后记得在终端输 hexo clean 清除一下再重新编译

加载动画

参考教程
不同点:

/layout/_partial 中新建 loadingbox.ejs

<% if (theme.preloaderbox.enable) { %>
    <div id="loading-box">
        <div class="loading-left-bg"></div>
        <div class="loading-right-bg"></div>
        <div class="spinner-box">
            <div class="configure-border-1">
                <div class="configure-core"></div>
            </div>
            <div class="configure-border-2">
                <div class="configure-core"></div>
            </div>
            <div class="loading-word">正在掀起波涛</div>
        </div>
    </div>
    <script>
        window.addEventListener('load', function(){
            document.body.style.overflow = 'auto';
            document.getElementById('loading-box').classList.add("loaded")
        }, false)
    </script>
    <% } %>

css 文件夹中新建 loadingbox.css

#loading-box .loading-left-bg,
#loading-box .loading-right-bg {
  position: fixed;
  z-index: 10000;
  width: 50%;
  height: 100%;
  background-color: #cfe0df;
  transition: all 0.5s;
}
#loading-box .loading-right-bg {
  right: 0;
}
#loading-box > .spinner-box {
  position: fixed;
  z-index: 10001;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100vh;
}
#loading-box .spinner-box .configure-border-1 {
  position: absolute;
  padding: 6px;
  width: 115px;
  height: 115px;
  background: #4eb8b8d1;
  animation: configure-clockwise 3s ease-in-out 0s infinite alternate;
}
#loading-box .spinner-box .configure-border-2 {
  left: -115px;
  padding: 6px;
  width: 115px;
  height: 115px;
  background: rgba(55, 131, 158, 0.837);
  transform: rotate(45deg);
  animation: configure-xclockwise 3s ease-in-out 0s infinite alternate;
}
#loading-box .spinner-box .loading-word {
  position: absolute;
  color: #0c97c5;
  font-size: 1.5rem;
}
#loading-box .spinner-box .configure-core {
  width: 100%;
  height: 100%;
  background-color: #d5e1dd;
}
div.loaded div.loading-left-bg {
  transform: translate(-100%, 0);
}
div.loaded div.loading-right-bg {
  transform: translate(100%, 0);
}
div.loaded div.spinner-box {
  display: none !important;
}
@keyframes configure-clockwise {
  0% {
    transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
@keyframes configure-xclockwise {
  0% {
    transform: rotate(45deg);
  }
  25% {
    transform: rotate(-45deg);
  }
  50% {
    transform: rotate(-135deg);
  }
  75% {
    transform: rotate(-225deg);
  }
  100% {
    transform: rotate(-315deg);
  }
}

教程head引入部分 的 href 好像有问题 我做了点修改
打开 head.ejs 加入以下内容

<link rel="stylesheet" type="text/css" href="/css/loading.css">

引入路径不同
layout.ejs 中加入

<%- partial('_partial/loadingbox') %>

在主题配置文件中加入

preloaderbox:
  enable:  true

黑暗模式

参考教程一
参考教程二

修改:

  • 原教程有个问题是 强制跟随系统暗黑模式 如果按了切换按钮再刷新还是系统的暗黑模式
    所以加了个 click 用于判断是否点击过按钮 使开关调节的优先级最高
    要是读者没有点击发现切换按钮 就按时间和系统 亮/暗 切换
  • 用 theme 判断 主要是方便后续的 亮/暗切换动画配置
  • 用 setTimeout 延迟添加 .dark 使颜色不立即改变 主要用于后续的等待 亮/暗切换动画 不然会闪一下
if (localStorage.getItem('theme') === 'dark') {
document.body.classList.add('dark');
}
else if (localStorage.getItem('click') === '1') {
document.body.classList.remove('dark');
}
else if (new Date().getHours() >= 22 || new Date().getHours() < 7 ) {
document.body.classList.add('dark');
} 
else if (matchMedia('(prefers-color-scheme: dark)').matches ) {
document.body.classList.add('dark');
}

function switchNightMode() {
    setTimeout(function(){
        var body = document.body;
        if(body.classList.contains('dark')){
        document.body.classList.remove('dark');
        localStorage.setItem('theme','light');
        localStorage.setItem('click','1');
        return;
        } else {
        document.body.classList.add('dark');
        localStorage.setItem('theme','dark');
        localStorage.setItem('click','0');
        return;
        }
    },500) 
}

打开 layout.ejs 加入以下内容
一定要加在 <body> 下的最上面 不然会出现闪烁

<script src="/js/darkmode.js"></script>

css/_partial 文件夹中新建 dark.styl

/* 背景颜色变黑色 */
body.dark 
    background-color: #262626;
//视频蒙版
body.dark .video-frame img
   filter brightness(0.15)//图片亮度
//字体颜色
body.dark .article-header .article-title //主页标题
    color: #19a282
body.dark .article-title //文章标题
body.dark .article-entry
body.dark .search-result
body.dark .twikoo
body.dark .video-inner a
{
    color: rgb(159, 210, 220);
}

//字体样式

body.dark a 
    color 	#00cea5
    &:hover
      color rgb(210, 178, 62)
body.dark .navbar .nav .nav-item-link //侧边栏字体
    color #00cea5
    &:hover
      color rgb(214, 180, 77)
body.dark strong,//粗体
    color: rgb(55, 162, 158);
body.dark s//划掉
    color: rgba(96, 221, 194, 0.57);
body.dark em,//斜体
    color: rgb(210, 179, 126);
//标题
body.dark h1,
body.dark h2,
body.dark h3,
body.dark h4,
body.dark h5,
body.dark h6,
{
    color: rgba(236, 94, 23, 0.9);
}
//搜索样式
body.dark .local-search
    background-color: rgba(38, 38, 38, 0.7);
body.dark .search-result .search-keyword
  color: #e33a3a
//列表前的标记点
body.dark ::marker
    color: rgb(55, 162, 158);
/* 改变透明度 */
body.dark .aplayer{background: #2f3742;}
body.dark img, body.dark strong {
    filter: brightness(1);
}
//代码和代码块
body.dark .article-entry pre
body.dark .highlight 
body.dark .highlight pre
body.dark code
{
    background: rgb(39, 76, 75);
}
body.dark .highlight .line
body.dark code
{
    color: #c8ede4;
}
body.dark .navbar-toggle // 侧边折叠按钮
body.dark .logo
{
    filter: invert(1);
}
//加载界面
body.dark #loading-box .spinner-box .loading-word 
    color: #0ca47b
body.dark #loading-box .loading-left-bg
body.dark #loading-box .loading-right-bg
{
    background-color: #2d4848;
}
//toc
body.dark a.toc-link
  color: rgb(18, 142, 93);
body.dark .toc-link::before
  background-color: #262626;
body.dark .is-active-link::before
  background-color #e9a321
//底部鱼儿
body.dark .flyfish{
  color:rgb(13, 102, 65);
}
//Artitalk
body.dark #artitalk_main .cbp_tmtimeline>li:nth-child(odd) .cbp_tmlabel {
    background: linear-gradient(-45deg, #16333700,#15282b7d,#132621ba,#088d69) 0% 0% / 400% 400%;
    color: rgb(217, 138, 53);
}
body.dark #artitalk_main .cbp_tmtimeline>li .cbp_tmlabel {
    background: linear-gradient(45deg, #dbd4ce00,#332f297a,#4d4a2bad,#d36811) 0% 0% / 400% 400%;
    color: rgb(12, 155, 115);
}
body.dark #readmore {
    background: linear-gradient(90deg, #098967e3,#2b5a58b5,#674f2d96,#c46b06e6) 0% 0% / 400% 400%;
    color: rgb(241, 234, 234);
}

//Hexo-Artitalk-Static 
/* 偶数项背景 */
body.dark #at_main .at_item:nth-child(odd) .at_content_container {
    background: linear-gradient(-45deg, #16333700,#15282b7d,#132621ba,#088d69) 0% 0% / 400% 400%;
    color: rgb(217, 138, 53);
}
/* 奇数项背景 */
body.dark #at_main .at_item .at_content_container {
    background: linear-gradient(45deg, #dbd4ce00,#332f297a,#4d4a2bad,#d36811) 0% 0% / 400% 400%;
    color: rgb(12, 155, 115);
}
/* 更多按钮 */
body.dark .at_more_btn {
    background: linear-gradient(90deg, #098967e3,#2b5a58b5,#674f2d96,#c46b06e6) 0% 0% / 400% 400%;
    color: rgb(241, 234, 234);
}

//twikoo加载按钮
body.dark .twikoobtn {
    color: #1ba162df;
    background: rgb(255, 255, 255);
    border: 3px solid #1ba162c1;
}
body.dark .twikoobtn:hover {
    border-color: rgb(25, 136, 146);
    color: #fff;
    background: rgb(25, 136, 146);
}
//顶部进度条
body.dark #percentageCounter
  background-image: linear-gradient(to right, #339933, #ff6666);

style.styl 中引入

@import "_partial/dark"

在标签中插入 onclick="switchNightMode()"
我绑定的是头像 所以是在 sidebar.ejs更改 属性为

<div class="logo">
  <a onclick="switchNightMode()" id="darklogo"><img src="<%- theme.brand %>" alt="<%= config.title %>" ></a>
</div>

亮/暗切换动画

采用 shoka 主题切换动画 参考教程
对应的暗黑模式切换原理 大佬版

因为实现黑暗模式原理不同 所以 onClick.js 的代码不需要那么复杂

js 文件比较多 我新建了一个 darkcat 文件夹放 js 文件 方便调试和管理

新建文件夹 darkcat
在里面新建 global.js 文件

Object.assign(HTMLElement.prototype, {
    createChild: function(tag, obj, positon) {
        const child = document.createElement(tag);
        Object.assign(child, obj);
        switch (positon) {
        case "after":
            this.insertAfter(child);
            break;
        case "replace":
            this.innerHTML = "";
        // eslint-disable-next-line no-fallthrough
        default:
            this.appendChild(child);
        }
        return child;
    },
    wrap: function(obj) {
        const box = document.createElement("div");
        Object.assign(box, obj);
        this.parentNode.insertBefore(box, this);
        this.parentNode.removeChild(this);
        box.appendChild(this);
    },
    height: function(h) {
        if (h) {
            this.style.height = typeof h === "number" ? h + "rem" : h;
        }
        return this.getBoundingClientRect().height;
    },
    width: function(w) {
        if (w) {
            this.style.width = typeof w === "number" ? w + "rem" : w;
        }
        return this.getBoundingClientRect().width;
    },
    top: function() {
        return this.getBoundingClientRect().top;
    },
    left: function() {
        return this.getBoundingClientRect().left;
    },
    attr: function(type, value) {
        if (value === null) {
            return this.removeAttribute(type);
        }

        if (value) {
            this.setAttribute(type, value);
            return this;
        } else {
            return this.getAttribute(type);
        }
    },
    insertAfter: function(element) {
        const parent = this.parentNode;
        if (parent.lastChild === this) {
            parent.appendChild(element);
        } else {
            parent.insertBefore(element, this.nextSibling);
        }
    },
    display: function(d) {
        if (d == null) {
            return this.style.display;
        } else {
            this.style.display = d;
            return this;
        }
    },
    child: function(selector) {
        return $(selector, this);
    },
    find: function(selector) {
        return $.all(selector, this);
    },
    _class: function(type, className, display) {
        const classNames = className.indexOf(" ")
            ? className.split(" ")
            : [className];
        const that = this;
        classNames.forEach(function(name) {
            if (type === "toggle") {
                that.classList.toggle(name, display);
            } else {
                that.classList[type](name);
            }
        });
    },
    addClass: function(className) {
        this._class("add", className);
        return this;
    },
    removeClass: function(className) {
        this._class("remove", className);
        return this;
    },
    toggleClass: function(className, display) {
        this._class("toggle", className, display);
        return this;
    },
    hasClass: function(className) {
        return this.classList.contains(className);
    }
});

新建文件夹 darkcat
在里面新建 custom-utils.js 文件

// eslint-disable-next-line no-undef
window.CSSTransition = function(target, type, complete) {
    let animation = {};
    let display = "none";
    switch (type) {
    case 0:
        animation = { opacity: [1, 0] };
        break;
    case 1:
        animation = { opacity: [0, 1] };
        display = "block";
        break;
    case "bounceUpIn":
        animation = {
            begin: function(anim) {
                target.display("block");
            },
            translateY: [
                { value: -60, duration: 200 },
                { value: 10, duration: 200 },
                { value: -5, duration: 200 },
                { value: 0, duration: 200 }
            ],
            opacity: [0, 1]
        };
        display = "block";
        break;
    case "shrinkIn":
        animation = {
            begin: function(anim) {
                target.display("block");
            },
            scale: [
                { value: 1.1, duration: 300 },
                { value: 1, duration: 200 }
            ],
            opacity: 1
        };
        display = "block";
        break;
    case "slideRightIn":
        animation = {
            begin: function(anim) {
                target.display("block");
            },
            translateX: [100, 0],
            opacity: [0, 1]
        };
        display = "block";
        break;
    case "slideRightOut":
        animation = {
            translateX: [0, 100],
            opacity: [1, 0]
        };
        break;
    default:
        animation = type;
        display = type.display;
        break;
    }
    // eslint-disable-next-line no-undef
    anime(
        Object.assign(
            {
                targets: target,
                duration: 200,
                easing: "linear"
            },
            animation
        )
    ).finished.then(function() {
        target.display(display);
        complete && complete();
    });
};

新建文件夹 darkcat
在里面新建 onClick.js 文件
因为实现黑暗模式原理不同 所以代码不需要那么复杂
colorToggleButtonName 是你绑定黑暗模式按钮的id

(function() {
    const colorToggleButtonName = "darklogo";
    const button = document.getElementById(colorToggleButtonName);
    if (button) {
        const BODY = document.getElementsByTagName("body")[0];
        button.addEventListener("mouseup", () => {
            const neko = BODY.createChild("div", {
                id: "neko",
                innerHTML:
        "<div class=\"planet\"><div class=\"sun\"></div><div class=\"moon\"></div></div><div class=\"body\"><div class=\"face\"><section class=\"eyes left\"><span class=\"pupil\"></span></section><section class=\"eyes right\"><span class=\"pupil\"></span></section><span class=\"nose\"></span></div></div>"
            });
            const hideNeko = function() {
                window.CSSTransition(
                    neko,
                    {
                        delay: 2500,
                        opacity: 0
                    },
                    function() {
                        BODY.removeChild(neko);
                    }
                );
            };
            let c;
            if(!document.body.classList.contains('dark')){
                c = function() {
                    neko.addClass("dark");
                    hideNeko();
                };
            } else {
                neko.addClass("dark");
                c = function() {
                    neko.removeClass("dark");
                    hideNeko();
                };
            }
            window.CSSTransition(neko, 1, function() {
                setTimeout(c, 700);
            });
        });
    }
})();

layout.ejs 最后加入
这里调用的是本地的 /js/anime.min.js 因为用高版本的 anime 鼠标点击爆炸特效不兼容
onClick.js 的引入要放在最后

<script defer src="/js/anime.min.js"></script>
<script defer src="/js/darkcat/global.js"></script>
<script defer src="/js/darkcat/custom-utils.js"></script>
<script defer src="/js/darkcat/onClick.js"></script>

css/_partial 文件夹中新建 custom-theme.styl

// custom-theme
#neko {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: none;
  background: linear-gradient(to top, #fddb92 0%, #d1fdff 80%);
  z-index: 999999;

  .planet {
    position: fixed;
    left: -50%;
    top: -50%;
    width: 200%;
    height: 200%;
    animation: rotate 2s cubic-bezier(.7, 0, 0, 1);
    transform-origin: center bottom;
  }

  &:before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    opacity: 0;
    background: linear-gradient(to top, #30cfd0 0%, #330867 100%);
    transition: 2s ease all;
  }

  .sun, .moon {
    position: absolute;
    border-radius: 100%;
    left: 55%;
    top: 32%;
  }

  .sun {
    height: 40px;
    width: 40px;
    background: #ffee94;
    box-shadow: 0px 0px 40px #ffee94;
    opacity: 1;
  }

  .moon {
    height: 24px;
    width: 24px;
    background: #eee;
    box-shadow: 0px 0px 20px #fff;
    opacity: 0;
  }

  .body {
    display: block;
    position: absolute;
    bottom: -20px;
    height: 140px;
    width: 135px;
    left: 50%;
    margin-left: -100px;
    background: #777;
    transition-property: all;
    transition-duration: .25s;
    transition-timing-function: ease-in-out;
    transition-delay: 0s, 0s;
    animation: slideUpBigIn 1s;

    &:before, &:after  {
      position: absolute;
      content: "";
      width: 0;
      height: 0;
      border-bottom: 20px solid #777;
      top: -20px;
      transition-property: all;
      transition-duration: .25s;
      transition-timing-function: ease-in-out;
      transition-delay: 0s, 0s;
    }
    &:before {
      border-left: 0px solid transparent;
      border-right: 30px solid transparent;
      left: 0;
    }
    &:after {
      border-right: 0px solid transparent;
      border-left: 30px solid transparent;
      right: 0;
    }

    .eyes {
      display: block;
      position: absolute;
      background: #ffee94;
      height: 40px;
      width: 40px;
      border-radius: 100%;
      bottom: 80px;
    }

    .eyes.left {
      left: 12px;
    }

    .eyes.right {
      right: 12px;
    }

    .eyes .pupil, .nose {
      display: block;
      position: relative;
      background: #ffb399;
      border-radius: 100%;
      margin: 0 auto;
    }

    .eyes .pupil {
      height: 100%;
      width: 5px;
      
      transition-property: width;
      transition-duration: 1s;
      transition-timing-function: ease-in-out;
      transition-delay: 0.5s;
    }

    .nose {
      top: 45px;
      height: 10px;
      width: 10px;
    }
  }

  &.dark {
    &:before {
      opacity: 1;
    }

    .sun {
      opacity: 0;
    }

    .moon {
      opacity: 1;
    }

    .body {
      background: #444;
      &:before {
        border-bottom: 20px solid #444;
      }
      &:after {
        border-bottom: 20px solid #444;
      }

      .eyes .pupil {
        height: 90%;
        width: 34px;
        margin: 5% auto;
      }
    }
  }
} !important

.col-12.col-md-4.m-auto.index-img img {
    background: #364151;
}

.link-avatar.my-auto img {
    background: #364151;
    object-fit: contain;
}

.about-avatar img {
    background: #364151;
}

css/_partial 文件夹中新建 animation.styl

// animation
// animation: name duration timing-function delay iteration-count direction;
.rotate {
  animation: rotate 6s linear infinite;
}

.beat {
  animation: beat 1.33s ease-in-out infinite;
}

.flash {
  animation: flash 6s cubic-bezier(.22, .61, .36, 1) infinite;
}

.shake {
  animation: shake 1s;
}

.fade-in {
  animation: fadeIn .5s;
}

.fade-out {
  animation: fadeOut .3s;
}

.up-down {
  animation: UpDown 2s infinite;
}

.down-up {
  animation: DownUp 2s infinite;
}

.slide {
  animation: slide .5s;
}

.slide-up-in {
  animation: slideUpIn .3s;
}

.slide-up-big-in {
  animation: slideUpBigIn .5s;
}

.slide-right-in {
  animation: slideRightIn .3s;
}

.slide-left-in {
  animation: slideLeftIn .3s;
}

.slide-down-in {
  animation: slideDownIn .3s;
}

.blur {
  animation: blur .8s ease-in-out forwards;
}

.elastic {
  animation: elastic 1s;
}

@keyframes rotate {
  from {
    transform: rotate(0)
  }

  to {
    transform: rotate(360deg)
  }
}

@keyframes rotating {
  from {
    transform: rotate(720deg);
  }

  to {
    transform: none;
  }
}

@keyframes rotate-needle-pause {
    0% {
        transform: rotateZ(-35deg)
    }
    100% {
        transform: rotateZ(-60deg)
    }
}

@keyframes rotate-needle-resume {
    0% {
        transform: rotateZ(-60deg)
    }

    100% {
        transform: rotateZ(-35deg)
    }
}

@keyframes beat {

  0%,
  100% {
    transform: scale(1);
  }

  10%,
  30% {
    transform: scale(.9);
  }

  20%,
  40%,
  60%,
  80% {
    transform: scale(1.1);
  }

  50%,
  70% {
    transform: scale(1.1);
  }
}

@keyframes flash {

  0%,
  50%,
  to {
    opacity: 1
  }

  25%,
  75% {
    opacity: 0
  }
}

@keyframes shake {
  from,
  to {
    transform: translate3d(0, 0, 0);
  }

  10%,
  30%,
  50%,
  70%,
  90% {
    transform: translate3d(-10px, 0, 0);
  }

  20%,
  40%,
  60%,
  80% {
    transform: translate3d(10px, 0, 0);
  }
}

@keyframes fadeIn {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

@keyframes fadeOut {
  0% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

@keyframes blur {
    0% {
        filter: blur(10px)
    }

    to {
        filter: blur(0)
    }
}

@keyframes blur-dark {
    0% {
        filter: blur(10px) brightness(.9)
    }

    to {
        filter: blur(0) brightness(.9)
    }
}

@keyframes UpDown {
  0%, 100% {
    opacity: .8;
    transform: translateY(10px);
  }
  50% {
    opacity: .4;
    transform: translateY(0);
  }
}

@keyframes DownUp {
  0%, 100% {
    opacity: .8;
    transform: rotate(180deg) translateY(0);
  }
  50% {
    opacity: .4;
    transform: rotate(180deg) translateY(-10px);
  }
}

@keyframes slide {
  0% {
    opacity: 0;
    transform: scaleY(0);
  }

  100% {
    opacity: 1;
    transform: scaleY(1);
  }
}

@keyframes slideRightIn {
    0% {
      opacity: 0;
      transform: translateX(50%);
    }

    to {
      opacity: 1;
      transform: translateX(0);
    }
}

@keyframes slideLeftIn {
    0% {
      opacity: 0;
      transform: translateX(-50%);
    }

    to {
      opacity: 1;
      transform: translateX(0);
    }
}

@keyframes slideUpIn {
    0% {
      opacity: 0;
      transform: translateY(10px);
    }

    to {
      opacity: 1;
      transform: translateY(0);
    }
}

@keyframes slideUpBigIn {
    0% {
        opacity: 0;
        transform: translateY(80px)
    }

    100% {
        opacity: 1;
        transform: translateY(0)
    }
}

@keyframes slideDownIn {
  0% {
    opacity: 0;
    transform: translateY(-18px)
  }
  100% {
    opacity: 1;
    transform: translateY(0)
  }
}

@keyframes elastic {
    0% {
        transform: scale(0)
    }

    55% {
        transform: scale(1)
    }

    70% {
        transform: scale(.98)
    }

    100% {
        transform: scale(1)
    }
}

style.styl 中引入

@import "_partial/animation"
@import "_partial/custom-theme"

网页加载优化

  1. lighthouse 扩展
    首先下个用于测试
    需要关掉其他扩展以防评分不准确 建议可以开启无痕浏览模式然后停掉其他扩展测试 或者使用另一个不常用的浏览器进行测试

  2. performance (控制台DevTools的性能performance)
    Chrome 开发工具 Performance 使用实战 Performance使用指南前端性能排查
    performance 是浏览器自带的调试工具 中文界面的话叫’性能’

  • 优化参考文章
    天下武功,唯快不破

  • 预加载
    参考文章
    有两种方式

    引入的 js 的 link 标签需要插在 <meta charset="utf-8"> 下面 不然有可能导致网页乱码

    1. preload

    2. prefetch

    没有合法 https 证书的站点无法使用 prefetch,预提取的资源不会被缓存(可能是种安全策略…)

  • 图片使用webp格式
    png透明背景可以用这个网站直接转成webp 转完透明背景不会丢失

佐料

  • 引用样式在 _extend.styl 中修改 不在 blockquote.styl 中修改 一个坑

  • 链接颜色在 style.styl 中的 a 修改
  • 图标替换
    侧边折叠按钮 在 layou.styl.navbar-toggle 的 before
    置顶图标 在 topping.ejs
    蒙版图标 在 ocean.ejs 的最底下
  • 侧边栏图标间隔
    在 navbar.styl 中的第 9 行中更改 .nav-item-linkpadding
  • 禁用文章里的上一篇和下一篇
    article.ejs 中删掉
<% if (!index){ %>
<%- partial('post/nav') %>
<% } %>
  • 禁用tag标签功能
    article.ejs 中删掉
<footer class="article-footer">
  <%- partial('post/tag') %>  //删除这行
</footer>
  • 禁用最底下用于备案等的容器
    layou.ejs 中删除
<%- partial('_partial/footer') %>
  • 更改文章水平位置
    在 layou.styl 中的 .content 中的 margin-right 项中更改

  • 主页文章标题居中显示
    在 articles.styl 中的 articles 下的 article 下添加一行 text-align:center

  • 调整主页置顶图标的位置
    在 article.styl 中更改 .article-topping 项的 top 和 left 的值

  • 禁用主页的上一页和下一页
    在 archive.styl 中 .page-nav下的 .prev 和 .next 下的 float 行都替换为 display: none

  • 文章总占宽度调整
    更改 _variables.styl 中的 wrap-width 项的数值

  • toc 文章目录
    toc的初始化设置项在 after-footer.ejs
    自带的4.7版本 有个小bug : 当 单个文章设置为 toc: false 时toc会初始化失败 控制台会报错
    可以试试升级下版本 : 从这里下载 tocbot.min.js 替换原有的 js 文件夹下的同名文件

    1. 展开到几级标题
      tocbot.min.js 中搜索 collapseDepth 改成你想要的值 (默认为 0) 我设为2 始终展开到二级标题
    2. 全部展开
      tocbot.styl 中把 .is-collapsed 下的 max-height 值删掉 (默认为 0)
    3. 更改不同级别标题的间隔 区分得更明显
      tocbot.styl.toc-list 下的 padding-left 改成 2rem