前言

这里是搭建博客时遇到的一些问题,因为怕以后忘记了会多走弯路,就记在这里了。

看了众多大佬的魔改博客,自觉像我这样没有前端基础的人,只能是小打小闹一般图一乐,不好好学习是永远不可能达到那样的高度的……

魔改记录存档

记录魔改过程,便于后续恢复与更新

为博文添加统计 ECharts 图表

感谢:Hexo 博客文章统计图 | Eurkon

操作方法

其他步骤与参考博文基本一致。在添加 charts.js 时需要注意路径位置。

[Blogroot]\themes\butterfly\scripts\helpers\ 或者是[Blogroot]\node_modules\hexo-theme-butterfly\scripts\helpers\(具体与安装方式有关)目录下新建 charts.js 文件,然后添加以下内容:

const cheerio = require('cheerio')
const moment = require('moment')

hexo.extend.filter.register('after_render:html', function (locals) {
const $ = cheerio.load(locals)
const post = $('#posts-chart')
const tag = $('#tags-chart')
const category = $('#categories-chart')
const htmlEncode = false

if (post.length > 0 || tag.length > 0 || category.length > 0) {
if (post.length > 0 && $('#postsChart').length === 0) {
if (post.attr('data-encode') === 'true') htmlEncode = true
post.after(postsChart(post.attr('data-start')))
}
if (tag.length > 0 && $('#tagsChart').length === 0) {
if (tag.attr('data-encode') === 'true') htmlEncode = true
tag.after(tagsChart(tag.attr('data-length')))
}
if (category.length > 0 && $('#categoriesChart').length === 0) {
if (category.attr('data-encode') === 'true') htmlEncode = true
category.after(categoriesChart(category.attr('data-parent')))
}

if (htmlEncode) {
return $.root().html().replace(/&#/g, '&#')
} else {
return $.root().html()
}
} else {
return locals
}
}, 15)

function postsChart (startMonth) {
const startDate = moment(startMonth || '2020-01')
const endDate = moment()

const monthMap = new Map()
const dayTime = 3600 * 24 * 1000
for (let time = startDate; time <= endDate; time += dayTime) {
const month = moment(time).format('YYYY-MM')
if (!monthMap.has(month)) {
monthMap.set(month, 0)
}
}
hexo.locals.get('posts').forEach(function (post) {
const month = post.date.format('YYYY-MM')
if (monthMap.has(month)) {
monthMap.set(month, monthMap.get(month) + 1)
}
})
const monthArr = JSON.stringify([...monthMap.keys()])
const monthValueArr = JSON.stringify([...monthMap.values()])

return `
<script id="postsChart">
var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
var postsChart = echarts.init(document.getElementById('posts-chart'), 'light');
var postsOption = {
title: {
text: '文章发布统计图',
x: 'center',
textStyle: {
color: color
}
},
tooltip: {
trigger: 'axis'
},
xAxis: {
name: '日期',
type: 'category',
boundaryGap: false,
nameTextStyle: {
color: color
},
axisTick: {
show: false
},
axisLabel: {
show: true,
color: color
},
axisLine: {
show: true,
lineStyle: {
color: color
}
},
data: ${monthArr}
},
yAxis: {
name: '文章篇数',
type: 'value',
nameTextStyle: {
color: color
},
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: true,
color: color
},
axisLine: {
show: true,
lineStyle: {
color: color
}
}
},
series: [{
name: '文章篇数',
type: 'line',
smooth: true,
lineStyle: {
width: 0
},
showSymbol: false,
itemStyle: {
opacity: 1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(128, 255, 165)'
},
{
offset: 1,
color: 'rgba(1, 191, 236)'
}])
},
areaStyle: {
opacity: 1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(128, 255, 165)'
}, {
offset: 1,
color: 'rgba(1, 191, 236)'
}])
},
data: ${monthValueArr},
markLine: {
data: [{
name: '平均值',
type: 'average',
label: {
color: color
}
}]
}
}]
};
postsChart.setOption(postsOption);
window.addEventListener('resize', () => {
postsChart.resize();
});
postsChart.on('click', 'series', (event) => {
if (event.componentType === 'series') window.location.href = '/archives/' + event.name.replace('-', '/');
});
</script>`
}

function tagsChart (len) {
const tagArr = []
hexo.locals.get('tags').map(function (tag) {
tagArr.push({ name: tag.name, value: tag.length, path: tag.path })
})
tagArr.sort((a, b) => { return b.value - a.value })

const dataLength = Math.min(tagArr.length, len) || tagArr.length
const tagNameArr = []
for (let i = 0; i < dataLength; i++) {
tagNameArr.push(tagArr[i].name)
}
const tagNameArrJson = JSON.stringify(tagNameArr)
const tagArrJson = JSON.stringify(tagArr)

return `
<script id="tagsChart">
var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
var tagsChart = echarts.init(document.getElementById('tags-chart'), 'light');
var tagsOption = {
title: {
text: 'Top ${dataLength} 标签统计图',
x: 'center',
textStyle: {
color: color
}
},
tooltip: {},
xAxis: {
name: '标签',
type: 'category',
nameTextStyle: {
color: color
},
axisTick: {
show: false
},
axisLabel: {
show: true,
color: color,
interval: 0
},
axisLine: {
show: true,
lineStyle: {
color: color
}
},
data: ${tagNameArrJson}
},
yAxis: {
name: '文章篇数',
type: 'value',
splitLine: {
show: false
},
nameTextStyle: {
color: color
},
axisTick: {
show: false
},
axisLabel: {
show: true,
color: color
},
axisLine: {
show: true,
lineStyle: {
color: color
}
}
},
series: [{
name: '文章篇数',
type: 'bar',
data: ${tagArrJson},
itemStyle: {
borderRadius: [5, 5, 0, 0],
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(128, 255, 165)'
},
{
offset: 1,
color: 'rgba(1, 191, 236)'
}])
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(128, 255, 195)'
},
{
offset: 1,
color: 'rgba(1, 211, 255)'
}])
}
},
markLine: {
data: [{
name: '平均值',
type: 'average',
label: {
color: color
}
}]
}
}]
};
tagsChart.setOption(tagsOption);
window.addEventListener('resize', () => {
tagsChart.resize();
});
tagsChart.on('click', 'series', (event) => {
if(event.data.path) window.location.href = '/' + event.data.path;
});
</script>`
}

function categoriesChart (dataParent) {
const categoryArr = []
let categoryParentFlag = false
hexo.locals.get('categories').map(function (category) {
if (category.parent) categoryParentFlag = true
categoryArr.push({
name: category.name,
value: category.length,
path: category.path,
id: category._id,
parentId: category.parent || '0'
})
})
categoryParentFlag = categoryParentFlag && dataParent === 'true'
categoryArr.sort((a, b) => { return b.value - a.value })
function translateListToTree (data, parent) {
let tree = []
let temp
data.forEach((item, index) => {
if (data[index].parentId == parent) {
let obj = data[index];
temp = translateListToTree(data, data[index].id);
if (temp.length > 0) {
obj.children = temp
}
if (tree.indexOf())
tree.push(obj)
}
})
return tree
}
const categoryNameJson = JSON.stringify(categoryArr.map(function (category) { return category.name }))
const categoryArrJson = JSON.stringify(categoryArr)
const categoryArrParentJson = JSON.stringify(translateListToTree(categoryArr, '0'))

return `
<script id="categoriesChart">
var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
var categoriesChart = echarts.init(document.getElementById('categories-chart'), 'light');
var categoryParentFlag = ${categoryParentFlag}
var categoriesOption = {
title: {
text: '文章分类统计图',
x: 'center',
textStyle: {
color: color
}
},
legend: {
top: 'bottom',
data: ${categoryNameJson},
textStyle: {
color: color
}
},
tooltip: {
trigger: 'item'
},
series: []
};
categoriesOption.series.push(
categoryParentFlag ?
{
nodeClick :false,
name: '文章篇数',
type: 'sunburst',
radius: ['15%', '90%'],
center: ['50%', '55%'],
sort: 'desc',
data: ${categoryArrParentJson},
itemStyle: {
borderColor: '#fff',
borderWidth: 2,
emphasis: {
focus: 'ancestor',
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(255, 255, 255, 0.5)'
}
}
}
:
{
name: '文章篇数',
type: 'pie',
radius: [30, 80],
roseType: 'area',
label: {
color: color,
formatter: '{b} : {c} ({d}%)'
},
data: ${categoryArrJson},
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(255, 255, 255, 0.5)'
}
}
}
)
categoriesChart.setOption(categoriesOption);
window.addEventListener('resize', () => {
categoriesChart.resize();
});
categoriesChart.on('click', 'series', (event) => {
if(event.data.path) window.location.href = '/' + event.data.path;
});
</script>`
}

在添加黑夜模式适配时,需要注入相应的代码到底部

function switchPostChart () {
// 这里为了统一颜色选取的是“明暗模式”下的两种字体颜色,也可以自己定义
let color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4C4948' : 'rgba(255,255,255,0.7)'
if (document.getElementById('posts-chart') && postsOption) {
try {
let postsOptionNew = postsOption
postsOptionNew.title.textStyle.color = color
postsOptionNew.xAxis.nameTextStyle.color = color
postsOptionNew.yAxis.nameTextStyle.color = color
postsOptionNew.xAxis.axisLabel.color = color
postsOptionNew.yAxis.axisLabel.color = color
postsOptionNew.xAxis.axisLine.lineStyle.color = color
postsOptionNew.yAxis.axisLine.lineStyle.color = color
postsOptionNew.series[0].markLine.data[0].label.color = color
postsChart.setOption(postsOptionNew)
} catch (error) {
console.log(error)
}
}
if (document.getElementById('tags-chart') && tagsOption) {
try {
let tagsOptionNew = tagsOption
tagsOptionNew.title.textStyle.color = color
tagsOptionNew.xAxis.nameTextStyle.color = color
tagsOptionNew.yAxis.nameTextStyle.color = color
tagsOptionNew.xAxis.axisLabel.color = color
tagsOptionNew.yAxis.axisLabel.color = color
tagsOptionNew.xAxis.axisLine.lineStyle.color = color
tagsOptionNew.yAxis.axisLine.lineStyle.color = color
tagsOptionNew.series[0].markLine.data[0].label.color = color
tagsChart.setOption(tagsOptionNew)
} catch (error) {
console.log(error)
}
}
if (document.getElementById('categories-chart') && categoriesOption) {
try {
let categoriesOptionNew = categoriesOption
categoriesOptionNew.title.textStyle.color = color
categoriesOptionNew.legend.textStyle.color = color
if (!categoryParentFlag) { categoriesOptionNew.series[0].label.color = color }
categoriesChart.setOption(categoriesOptionNew)
} catch (error) {
console.log(error)
}
}
}
document.getElementById("darkmode").addEventListener("click", function () { setTimeout(switchPostChart, 100) })

添加右键菜单

感谢:

自定义右键菜单 | DoraKika

Butterfly魔改:自定义右键菜单 | 张洪Heo (zhheo.com)

右键菜单

添加...butterfly/layout/includes/dorakika/rightmenu.pug

代码如下

#rightMenu
.rightMenu-group.rightMenu-small
a.rightMenu-item(href="javascript:window.history.back();")
i.fa-solid.fa-arrow-left
a.rightMenu-item(href="javascript:window.history.forward();")
i.fa-solid.fa-arrow-right
a.rightMenu-item(href="javascript:window.location.reload();")
i.fa-solid.fa-arrow-rotate-right
a.rightMenu-item(href="javascript:rmf.scrollToTop();")
i.fa-solid.fa-arrow-up
.rightMenu-group.rightMenu-line.hide#menu-text
a.rightMenu-item(href="javascript:rmf.copySelect();")
i.fa-solid.fa-copy
span='复制'
.rightMenu-group.rightMenu-line
a.rightMenu-item(href="javascript:rmf.switchDarkMode();")
i.fa-solid.fa-moon
span='昼夜切换'
a.rightMenu-item(href="javascript:rmf.switchReadMode();")
i.fa-solid.fa-book-open
span='阅读模式'

然后在 ...butterfly/layout/includes/layout.pug 中引入

doctype html
html(lang=config.language data-theme=theme.display_mode class=htmlClassHideAside)
head
include ./head.pug
body
...

else
include ./404.pug

include ./rightside.pug
!=partial('includes/third-party/search/index', {}, {cache: true})
+ !=partial('includes/dorakika/rightmenu',{}, {cache:true})
include ./additional-js.pug

然后将样式表 inject 到头部,脚本 inject 到底部

/* rightMenu */
#rightMenu{
display: none;
position: fixed;
width: 160px;
height: fit-content;
top: 10%;
left: 10%;
background-color: var(--card-bg);
border: 1px solid var(--font-color);
border-radius: 8px;
z-index: 100;
}
#rightMenu .rightMenu-group{
padding: 7px 6px;
}
#rightMenu .rightMenu-group:not(:nth-last-child(1)){
border-bottom: 1px solid var(--font-color);
}
#rightMenu .rightMenu-group.rightMenu-small{
display: flex;
justify-content: space-between;
}
#rightMenu .rightMenu-group .rightMenu-item{
height: 30px;
line-height: 30px;
border-radius: 8px;
transition: 0.3s;
color: var(--font-color);
}
#rightMenu .rightMenu-group.rightMenu-line .rightMenu-item{
display: flex;
height: 40px;
line-height: 40px;
padding: 0 4px;
}
#rightMenu .rightMenu-group .rightMenu-item:hover{
background-color: var(--text-bg-hover);
}
#rightMenu .rightMenu-group .rightMenu-item i{
display: inline-block;
text-align: center;
line-height: 30px;
width: 30px;
height: 30px;
padding: 0 5px;
}
#rightMenu .rightMenu-group .rightMenu-item span{
line-height: 30px;
}

#rightMenu .rightMenu-group.rightMenu-line .rightMenu-item *{
height: 40px;
line-height: 40px;
}
.rightMenu-group.hide{
display: none;
}
let rmf = {};
rmf.showRightMenu = function(isTrue, x=0, y=0){
let $rightMenu = $('#rightMenu');
$rightMenu.css('top',x+'px').css('left',y+'px');

if(isTrue){
$rightMenu.show();
}else{
$rightMenu.hide();
}
}
rmf.switchDarkMode = function(){
const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
if (nowMode === 'light') {
activateDarkMode()
saveToLocal.set('theme', 'dark', 2)
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
} else {
activateLightMode()
saveToLocal.set('theme', 'light', 2)
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
}
// handle some cases
typeof utterancesTheme === 'function' && utterancesTheme()
typeof FB === 'object' && window.loadFBComment()
window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
};
rmf.switchReadMode = function(){
const $body = document.body
$body.classList.add('read-mode')
const newEle = document.createElement('button')
newEle.type = 'button'
newEle.className = 'fas fa-sign-out-alt exit-readmode'
$body.appendChild(newEle)

function clickFn () {
$body.classList.remove('read-mode')
newEle.remove()
newEle.removeEventListener('click', clickFn)
}

newEle.addEventListener('click', clickFn)
}

//复制选中文字
rmf.copySelect = function(){
document.execCommand('Copy',false,null);
//这里可以写点东西提示一下 已复制
}

//回到顶部
rmf.scrollToTop = function(){
btf.scrollToDest(0, 500);
}

// 右键菜单事件
if(! (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))){
window.oncontextmenu = function(event){
$('.rightMenu-group.hide').hide();
//如果有文字选中,则显示 文字选中相关的菜单项
if(document.getSelection().toString()){
$('#menu-text').show();
}

let pageX = event.clientX + 10;
let pageY = event.clientY;
let rmWidth = $('#rightMenu').width();
let rmHeight = $('#rightMenu').height();
if(pageX + rmWidth > window.innerWidth){
pageX -= rmWidth+10;
}
if(pageY + rmHeight > window.innerHeight){
pageY -= pageY + rmHeight - window.innerHeight;
}



rmf.showRightMenu(true, pageY, pageX);
return false;
};

window.addEventListener('click',function(){rmf.showRightMenu(false);});
}

由于右键菜单的切换夜间模式无法与统计图交互,因而需要对前述的 JS 文件进行如下修改:

rmf.switchDarkMode = function(){
const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
if (nowMode === 'light') {
activateDarkMode()
saveToLocal.set('theme', 'dark', 2)
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
+ setTimeout(switchPostChart, 100)
} else {
activateLightMode()
saveToLocal.set('theme', 'light', 2)
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
+ setTimeout(switchPostChart, 100)
}
// handle some cases
typeof utterancesTheme === 'function' && utterancesTheme()
typeof FB === 'object' && window.loadFBComment()
window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
};
右键菜单 2023.1.12 更新

把右下角按钮移植入右键菜单,基本上是把 JS 函数拷贝到了 .pug 文件中,对阅读模式做了修改,阅读模式中将不开启右键菜单(否则再开启阅读模式,将会导致出现多个退出按钮)。

单多栏切换则由于窗口大小改变布局会发生改变,因而通过以下方式做到菜单项与布局匹配( copy 的右边栏的样式,改了下就可以匹配了)。


@media screen and (max-width: 900px) {
#rightMenu #menu-hide-aside{
display: none;
}
}

为适配石蒜模拟器(因为有时候想关掉它,不希望切换页面或者刷新的时候又跑出来),就在右键菜单写了开关,并修改了 hexo-sakana 插件。主要是插件过滤器的 JS 部分加了是否开启的判断,以及淡入淡出的动画。可以说这个右键菜单是我改得最深入的一次了,对于我这个没啥基础的人来说,真的见识大涨哈哈哈。

主题优化

样式表修改

字体修改

引入字体后,结合样式表选择器可以设置某一部分的字体。

详见:基于Hexo的博客网站 | MateMaster

透明度修改及黑夜模式适配

依旧是样式表选择器。之前我把主页的头图和背景图设成了同一幅图,但是刷新之后会有一段时间的错位,看起来很不舒服。用大佬的方法完美地解决了问题。并且,利用样式表选择器可以设置黑夜模式下的透明度和色调。

详见:Custom Beautify | Akilarの糖果屋

鼠标指针修改

通过样式表选择器进行修改。

详见:Custom Beautify | Akilarの糖果屋

github 日历

Github UI 更新频繁,本插件维护又基本处于停滞状态,因此本站不再使用。

详见:教程:hexo-githubcalendar 插件 1.0 | 小冰博客 (zfe.space)

解决 2023.12.18 的 This Serverless Function has crashed 问题

2023.7.20 api 出现问题

This Serverless Function has crashed.
Your connection is working correctly.
Vercel is working correctly.
500: INTERNAL_SERVER_ERROR

rr210 已继续跟进: github正则匹配规则更改问题,可以参考进行修改,测试有效。

解决 2023.7.20 的 This Serverless Function has crashed 问题

2023.7.20 api 出现问题

This Serverless Function has crashed.
Your connection is working correctly.
Vercel is working correctly.
500: INTERNAL_SERVER_ERROR

zkeq 已继续跟进: Github 页面改版,更新 regex 规则,可以参考进行修改,测试有效。

解决 2023.1.7 的 This Serverless Function has crashed 问题

2023.1.7 api 出现问题,改用自建的 API 也同样报错:

This Serverless Function has crashed.
Your connection is working correctly.
Vercel is working correctly.
500: INTERNAL_SERVER_ERROR

目前其他博主也遇到了这个情况 500: INTERNAL_SERVER_ERROR,可以看到 zkeq 已经针对该问题提了 PR: Github 页面改版,更新 regex 规则,可以参考进行修改,测试有效。

解决 2022.9.4 Vercel 的公共域名被墙的问题

2022.9.4 Vercel 的公共域名被墙了,解决方案有以下两种:

具体参见 Zfour/python_github_calendar_api,关于自建 API 的步骤中,他的配图用错了,创建项目的时候,别再用 friends_link_list_api 啦!

github 卡片 ghcard

本插件是 hexo-butterfly-tag-plugins-plus 中的内容。 可以参见:Tag Plugins Plus | Akilarの糖果屋

如果自己部署了 ghcard API 但是卡片显示 Maximum retries exceeded. Please add an env variable called PAT_1 with your github token in vercel ,那可能是你的 token 没设好,或者过期了,重新生成一个就好。

解决 2022.9.4 Vercel 的公共域名被墙的问题

但因为 2022.9.4 Vercel 的公共域名被墙了,所以需要使用 Vercel 的自定义域名绑定。自己部署在 Vercel 上的方法如下 anuraghazra/github-readme-stats ,然后再绑定自定义域名

注意,在部署时,可能会报错:The value for maxDuration must be between 1 second and 10 seconds, in order to increase this limit upgrade your plan 。解决方法见 fix: decrease vercel ‘maxDuration’ to fix free plan deployment

绑定完成后,需要修改 hexo-butterfly-tag-plugins-plus 中相关文件:

// [path]\hexo-butterfly-tag-plugins-plus\lib\scripts 其他的 api 类似,使用全部替换即可
- url += 'https://github-readme-stats.vercel.app/api/?username=' + path
+ url += 'https://yourdomain/api/?username=' + path;

也可以使用我部署已经好的(不会真的有人会用我的吧??)

https://github-readme-stats.zerolacqua.top/api

Magnet

详见:教程:hexo-magnet 插件 1.0 | 小冰博客 (zfe.space)

底部栏添加跳动的鱼背景

详见:jQuery 插件 养鱼 | Ln’s Blog (weilining.github.io)

Akilar 的标签插件 Tag Plugins Plus

本博客的样式使用情况请见:博客样式测试。详细测试例请见:Tag Plugins Plus | Akilarの糖果屋

Waline 评论

这个评论系统有后端的诶真的好棒,看的第一眼就爱上了。不过同样由于 2022.9.4 Vercel 的公共域名被墙了 需要绑定自定义域名。要测试评论可以到这里试试

友链朋友圈

配置效果见朋友圈,需要注意的是,由于 Vercel 受到影响,所以需要使用 Vercel 时,依然要绑定自定义域名。当然也可以使用其他部署方式。文档参见:https://fcircle-doc.yyyzyyyz.cn/

相关功能

关于标签插件

想要将 , 作为参数输入,需要进行转义,使用 &sbquo; 代替 ,

隐藏博客

使用插件 hexo-hide-posts

使用方法

安装插件

npm install hexo-hide-posts --save

在博客的Front-matter中加入如下参数

hidden: false 

可实现对文章的隐藏(虽然还是可以用网址访问就是了)

参考网站:「推荐」本站用到的 hexo 插件 | 若风 (loafing.cn)

文字遮盖效果

使用插件 hexo-hide-posts

使用方法

安装插件

npm install hexo-spoiler --save

站点根目录配置

spoiler:
style: blur # 或者box
color: black # 仅当 style 为 box 时起效
p: false # 没懂啥意思,不管它

使用:

{% spoiler 默认配置效果 %}

效果:

默认配置效果

参考网站:「推荐」本站用到的 hexo 插件 | 若风 (loafing.cn)

跳转站内文章

通过标签插件 {% post_link %}

使用方法
  • 可以使用以下方式

    {% post_link 欢迎光临小店 %}

    其效果是这样的

    欢迎光临小店

  • 可以通过第二个参数修改文字内容

    {% post_link 欢迎光临小店 偷看可是不礼貌的哦 %}

    其效果是这样的

    偷看可是不礼貌的哦

跳转文章锚点

通过<a></a>

使用方法
  • 方法一

    <a href="{% post_path '近景摄影测量编程与实习' %}#影像获取">这是一个跳转</a>

    此方法效果如下

    这是一个跳转

  • 方法二

    [这是一个跳转]({% post_path '近景摄影测量编程与实习' %}#课间实习 )

    此方法目前还无法实现,还没找到问题所在。

    注意:

    1. 如果文章中有图片,可能会出现锚点位置不准确问题,原因是图片加载成功之后会把内容高度撑开。
    2. 如果如果有空格,需要把空格换成连字符 -

    可以参考以下文章

    hexo 链接到站内文章指定锚点 | 极简前端 (jijian.link)

插入视频

使用 html 插入视频

使用方法

嵌入视频

{% raw %}
<div style="position: relative; width: 100%; height: 0; padding-bottom: 75%;"><iframe src="//player.bilibili.com/player.html?aid=460816113&bvid=BV1j5411g7m5&cid=345672460&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="position: absolute; width: 100%; height: 100%; Left: 0; top: 0;" ></iframe></div>
{% endraw %}
<div style="position: relative; padding: 30% 45%;">
<iframe style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;" src="https://player.bilibili.com/player.html?aid=460816113&bvid=BV1j5411g7m5&cid=345672460&page=1" frameborder="no" scrolling="no"></iframe>
</div>

可以参考:

图库

通过标签插件 {% gallery %}

使用方法
{% gallery %}
![](https://cdn.zerolacqua.top/images/blogs/可爱的水伊布/Vaporeon1.png)
{% endgallery %}

效果见可爱的水伊布

ECharts

使用插件 hexo-tag-echarts

使用方法

安装插件

npm install hexo-tag-echarts --save

更新 node_modules\hexo-tag-echarts3\template.html中第二行的

- <script src="https://cdn.bootcss.com/echarts/3.8.0/echarts.common.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/echarts@5.3.0/dist/echarts.min.js"></script>
//或者改为 <script src="https://unpkg.com/echarts@5.3.0/dist/echarts.min.js"></script>
{% echarts 400 '85%' %}
{
tooltip : {
trigger: 'axis',
axisPointer : { // 坐标轴指示器,坐标轴触发有效
type : 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
legend: {
data:['利润', '支出', '收入']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis : [
{
type : 'value'
}
],
yAxis : [
{
type : 'category',
axisTick : {show: false},
data : ['周一','周二','周三','周四','周五','周六','周日']
}
],
series : [
{
name:'利润',
type:'bar',
itemStyle : {
normal: {
label: {show: true, position: 'inside'}
}
},
data:[200, 170, 240, 244, 200, 220, 210]
},
{
name:'收入',
type:'bar',
stack: '总量',
itemStyle: {
normal: {
label : {show: true}
}
},
data:[320, 302, 341, 374, 390, 450, 420]
},
{
name:'支出',
type:'bar',
stack: '总量',
itemStyle: {normal: {
label : {show: true, position: 'left'}
}},
data:[-120, -132, -101, -134, -190, -230, -210]
}
]
};
{% endecharts %}

参考:

mini-sandbox 前端代码可视化

mini-sandbox 官方文档

内容很大部分参考了使用MiniSandbox前端代码可视化 | 轻笑Chuckle

使用方法

通过 CDN 单页引入 MiniSandbox

<script src="https://unpkg.com/mini-sandbox@0.3.11"></script>

创建一个 sandbox.js 文件用于文章中的引入

new MiniSandbox({
el: '#sandbox',//要和 markdown 文件中 div 元素的 id 一致
files: {//各 tab 页面
//index.html=>html
//index.css=>css
//index.js=>js
'index.html': {
title: 'HTML',//tab 上显示的名字
defaultValue: `<h1>JavaScript</h1>
<p id="demo">
JavaScript 能改变 HTML 元素的样式。
</p>
<button>点击这里</button>
`,//HTML的内容/代码
cssLibs: ['index.css'],//html引入的css
jsLibs: ['index.js'],//html引入的js
},
'index.css': {
title: 'CSS',
defaultValue: `h1{
background: red;
}
`
},
'index.js': {
title: 'JS',
//JS的内容/代码
defaultValue: `const btn = document.querySelector('button')
btn.addEventListener('click', () => {
document.getElementById("demo").style.color
="#ff0000";
})
`
}
},
//用来设置一些 Sandbox 的默认配置
defaultConfig: {
height: '300px',//Sandbox 的高度, 默认为 '300px'
autoRun: true,//每次修改后是否自动运行, 默认等于 true
autoRunInterval: 1000,//每次自动运行的时间间隔, 单位为毫秒, 默认等于 300
editorRange: '50%',//编辑区域默认占比, 默认情况下编辑区域占 50%
draggable: true,//是否可以左右拖动布局, 默认为 true
direction: 'row',//控制上下/左右布局, 默认为 'row'。'row' | 'row-reverse' | 'column' | 'column-reverse'
}
})

并在文章末尾引入

<script src="[your_directory]/sandbox.js"></script>

针对 MiniSandbox 的夜间模式,可以参考轻笑Chuckle的 CSS 配置

.mini-sandbox{
box-shadow: none!important;
border-radius: 7px;
}
[data-theme=dark]
.mini-sandbox{
background-color: #151515!important;
}
[data-theme=dark]
.cm-activeLine{
background-color: #252525!important;
}
[data-theme=dark]
.mini-sandbox .sandbox-head{
background: #202020!important;
}
[data-theme=dark]
.mini-sandbox .cm-gutters{
background: #202020!important;
}
.mini-sandbox .sandbox-head .sandbox-tab .sandbox-tab-active{
box-shadow: none!important;
}
[data-theme=dark]
.mini-sandbox .sandbox-head .sandbox-tab .sandbox-tab-active{
background: #363636!important;
}
[data-theme=dark]
.cm-activeLineGutter{
background: #363636!important;
}
[data-theme=dark]
.sandbox-body .sandbox-gutter{
background: #363636!important;
}
[data-theme=dark]
.mini-sandbox .sandbox-gutter{
border-left: 1px solid #404040!important;
border-right: 1px solid #404040!important;
}
[data-theme=dark]
.mini-sandbox .sandbox-head .sandbox-tab .sandbox-tab-active::after{
background: none!important;
}
[data-theme=dark]
.mini-sandbox .sandbox-head .sandbox-tab .sandbox-tab-active::before{
background: none!important;
}
[data-theme=dark]
.mini-sandbox .sandbox-render{
background: #E1E1E1!important;
}
[data-theme=dark]
.mini-sandbox .ͼd{
color: #c3e88d!important;
}
[data-theme=dark]
.mini-sandbox .ͼc{
color: #c3e88d!important;
}
[data-theme=dark]
.mini-sandboxb{
color: #1E90FF!important;
}
[data-theme=dark]
.mini-sandbox .ͼf{
color: #1E90FF!important;
}
[data-theme=dark]
.mini-sandbox .ͼh{
color: #ff5370!important;
}
[data-theme=dark]
.mini-sandboxa{
color: #FF00FF!important;
}
[data-theme=dark]
.mini-sandboxi{
color:#5F9EA0!important;
}
[data-theme=dark]
.mini-sandbox .ͼl{
color:#969896!important;
}
.mini-sandbox{
margin-bottom: 10px;
}

Markdown 与数学公式

数学公式渲染错误

由于 Mathjax 数学公式渲染效果很迷,经常无法复现问题,有时重新部署一下全部都变了,所以 Mathjax 的内容参考价值不大!

果然还是 Katex 用起来更舒服啊,换了 Katex 之后这些问题就不存在了。解决不了问题,就把产生问题的解决掉。

一些解决方法

当然,换了 Katex 还是会有它的问题,就比如加粗

一二**加粗**三四
一二**.加粗**三四
一二**加.粗**三四
一二**加粗.**三四

看看效果吧

一二加粗三四
一二**.加粗三四
一二
加.粗三四
一二
加粗.**三四

  • 所以最好还是不要在内部有字符的时候用加粗斜体
  • 更合适的方法是使用<b></b><i></i>

可以参见这几篇文章进行修改:

但是修改后公式中还是会出现\\被转义成\的问题:

CCW(c,a,b)=
\begin{vmatrix}
a_x&a_y&1\\
b_x&b_y&1\\
c_x&c_y&1\\
\end{vmatrix}=
\begin{vmatrix}
a_x-c_x&a_y-c_y\\
b_x-c_x&b_y-c_y\\
\end{vmatrix}

其效果为:CCW(c,a,b)=axay1bxby1cxcy1=axcxaycybxcxbycyCCW(c,a,b)=\begin{vmatrix}a_x&a_y&1\\b_x&b_y&1\\c_x&c_y&1\\\end{vmatrix}=\begin{vmatrix}a_x-c_x&a_y-c_y\\b_x-c_x&b_y-c_y\\\end{vmatrix}

妥协办法也有,但是太蠢,将就用吧

CCW(c,a,b)=
\begin{vmatrix}
a_x&a_y&1\\\\
b_x&b_y&1\\\\
c_x&c_y&1\\\\
\end{vmatrix}=
\begin{vmatrix}
a_x-c_x&a_y-c_y\\\\
b_x-c_x&b_y-c_y\\\\
\end{vmatrix}

其效果为:CCW(c,a,b)=axay1bxby1cxcy1=axcxaycybxcxbycyCCW(c,a,b)=\begin{vmatrix}a_x&a_y&1\\\\b_x&b_y&1\\\\c_x&c_y&1\\\\\end{vmatrix}=\begin{vmatrix}a_x-c_x&a_y-c_y\\\\b_x-c_x&b_y-c_y\\\\\end{vmatrix}

此外,公式块中 {y}_{t}_会被渲染成斜体

$$

\hat {y}_{t}=\cfrac{1}{2l+1}(y_{t-l}+y_{t-(l-1)}+\cdots+y_{t-1}+y_{t}+y_{t+1}+\cdots+y_{t+l})

$$

其效果为:

y^t=12l+1(ytl+yt(l1)++yt1+yt+yt+1++yt+l)\begin{equation}\hat {y}_{t}=\cfrac{1}{2l+1}(y_{t-l}+y_{t-(l-1)}+\cdots+y_{t-1}+y_{t}+y_{t+1}+\cdots+y_{t+l})\end{equation}

需要改成{y}_ {t}

$$

\hat {y}_ {t}=\cfrac{1}{2l+1}(y_{t-l}+y_{t-(l-1)}+\cdots+y_{t-1}+y_{t}+y_{t+1}+\cdots+y_{t+l})

$$

其效果为

y^t=12l+1(ytl+yt(l1)++yt1+yt+yt+1++yt+l)\hat {y}_ {t}=\cfrac{1}{2l+1}(y_{t-l}+y_{t-(l-1)}+\cdots+y_{t-1}+y_{t}+y_{t+1}+\cdots+y_{t+l})

目前测试发现和{y}_{}有相关,若是\hat y_{t}则不会出现问题。且只会在本地测试时出问题

公式块自动编号

利用 \begin{equation} 进行公式编号

使用方法

可以用以下方式对公式进行自动编号

$$
\begin{equation}
1+1=2
\end{equation}
$$

这是没用的:

1+1=21+1=2

这是用了的:

1+1=2\begin{equation}1+1=2\end{equation}

网站管理

网站验证与提交

使用 hexo-generator-sitemap 插件

使用方法
npm install hexo-generator-sitemap --save

并在 _config.yml 文件中添加:

# hexo-generator-sitemap
sitemap:
path:
- sitemap.xml
- sitemap.txt
template:
template_txt:
rel: false
tags: true
categories: true

各参数可参考该插件的说明。

在各搜索引擎上验证网站,按照各搜索引擎的流程进行验证即可。之后便可以提交网站地图了,需要注意的是,在 Google Search Console 上提交网站地图是,可能会出现无法获取的情况,排除网站地图的问题后,说明可能遇到了 Bug ,状态显示是有误的。参见:Sitemap could not be read in new GSC - Google Search Central Community

网站 RSS 生成

使用 hexo-generator-feed插件

使用方法
npm install hexo-generator-feed --save

并在 _config.yml 文件中添加:

# hexo-generator-feed
feed:
enable: true
type: atom
path: atom.xml
limit: 20
hub:
content:
content_limit: 140
content_limit_delim: ' '
order_by: -date
icon: icon.png
autodiscovery: true
template:

各参数可参考该插件的说明。

之后就可以愉快地订阅啦~

其他参考网站

参考网站