Next 优化与使用技巧

Next主题设置及美化,打造自己的个性化主题。

一、主题美化

1、文章永久链接

Hexo 默认生成的文章地址路径是 【网站名称/年/月/日/文章名称】。

这种链接对搜索爬虫很不友好,它的 url 结构超过三层,太深。
安装 hexo-abbrlink

1
npm install hexo-abbrlink --save

然后配置站点配置文件 _config.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
# URL
url: https://blog.juanertu.com
root: /
#修改
permalink: :categories/:abbrlink.html
permalink_defaults:
pretty_urls:
trailing_index: true
trailing_html: true
#添加
abbrlink:
alg: crc32 # 算法:crc16(default) and crc32
rep: hex # 进制:dec(default) and hex

2、鼠标点击特效

新建特效脚本文件,将其放在站点的 source 目录下(即 hexo/source/js/cursor)而不是主题目录下,如果没有 js 目录,则新建一个。

  • 礼花特效(fireworks.js)

    点击显/隐代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    class Circle {
    constructor({ origin, speed, color, angle, context }) {
    this.origin = origin
    this.position = { ...this.origin }
    this.color = color
    this.speed = speed
    this.angle = angle
    this.context = context
    this.renderCount = 0
    }

    draw() {
    this.context.fillStyle = this.color
    this.context.beginPath()
    this.context.arc(this.position.x, this.position.y, 2, 0, Math.PI * 2)
    this.context.fill()
    }

    move() {
    this.position.x = (Math.sin(this.angle) * this.speed) + this.position.x
    this.position.y = (Math.cos(this.angle) * this.speed) + this.position.y + (this.renderCount * 0.3)
    this.renderCount++
    }
    }

    class Boom {
    constructor ({ origin, context, circleCount = 16, area }) {
    this.origin = origin
    this.context = context
    this.circleCount = circleCount
    this.area = area
    this.stop = false
    this.circles = []
    }

    randomArray(range) {
    const length = range.length
    const randomIndex = Math.floor(length * Math.random())
    return range[randomIndex]
    }

    randomColor() {
    const range = ['8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
    return '#' + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range)
    }

    randomRange(start, end) {
    return (end - start) * Math.random() + start
    }

    init() {
    for(let i = 0; i < this.circleCount; i++) {
    const circle = new Circle({
    context: this.context,
    origin: this.origin,
    color: this.randomColor(),
    angle: this.randomRange(Math.PI - 1, Math.PI + 1),
    speed: this.randomRange(1, 6)
    })
    this.circles.push(circle)
    }
    }

    move() {
    this.circles.forEach((circle, index) => {
    if (circle.position.x > this.area.width || circle.position.y > this.area.height) {
    return this.circles.splice(index, 1)
    }
    circle.move()
    })
    if (this.circles.length == 0) {
    this.stop = true
    }
    }

    draw() {
    this.circles.forEach(circle => circle.draw())
    }
    }

    class CursorSpecialEffects {
    constructor() {
    this.computerCanvas = document.createElement('canvas')
    this.renderCanvas = document.createElement('canvas')

    this.computerContext = this.computerCanvas.getContext('2d')
    this.renderContext = this.renderCanvas.getContext('2d')

    this.globalWidth = window.innerWidth
    this.globalHeight = window.innerHeight

    this.booms = []
    this.running = false
    }

    handleMouseDown(e) {
    const boom = new Boom({
    origin: { x: e.clientX, y: e.clientY },
    context: this.computerContext,
    area: {
    width: this.globalWidth,
    height: this.globalHeight
    }
    })
    boom.init()
    this.booms.push(boom)
    this.running || this.run()
    }

    handlePageHide() {
    this.booms = []
    this.running = false
    }

    init() {
    const style = this.renderCanvas.style
    style.position = 'fixed'
    style.top = style.left = 0
    style.zIndex = '999999999999999999999999999999999999999999'
    style.pointerEvents = 'none'

    style.width = this.renderCanvas.width = this.computerCanvas.width = this.globalWidth
    style.height = this.renderCanvas.height = this.computerCanvas.height = this.globalHeight

    document.body.append(this.renderCanvas)

    window.addEventListener('mousedown', this.handleMouseDown.bind(this))
    window.addEventListener('pagehide', this.handlePageHide.bind(this))
    }

    run() {
    this.running = true
    if (this.booms.length == 0) {
    return this.running = false
    }

    requestAnimationFrame(this.run.bind(this))

    this.computerContext.clearRect(0, 0, this.globalWidth, this.globalHeight)
    this.renderContext.clearRect(0, 0, this.globalWidth, this.globalHeight)

    this.booms.forEach((boom, index) => {
    if (boom.stop) {
    return this.booms.splice(index, 1)
    }
    boom.move()
    boom.draw()
    })
    this.renderContext.drawImage(this.computerCanvas, 0, 0, this.globalWidth, this.globalHeight)
    }
    }

    const cursorSpecialEffects = new CursorSpecialEffects()
    cursorSpecialEffects.init()
  • 爆炸特效(explosion.js)

    点击显/隐代码
    1
    "use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=.1,a.alpha=.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++)e.animatables[t].target.draw()}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++)n.push(createParticule(e,t));anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)}
  • 爱心(love.js)

    点击显/隐代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    !(function (e, t, a) {
    function n() {
    c(
    ".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"
    ),
    o(),
    r()
    }
    function r() {
    for (var e = 0; e < d.length; e++)
    d[e].alpha <= 0
    ? (t.body.removeChild(d[e].el), d.splice(e, 1))
    : (d[e].y--,
    (d[e].scale += 0.004),
    (d[e].alpha -= 0.013),
    (d[e].el.style.cssText =
    'left:' +
    d[e].x +
    'px;top:' +
    d[e].y +
    'px;opacity:' +
    d[e].alpha +
    ';transform:scale(' +
    d[e].scale +
    ',' +
    d[e].scale +
    ') rotate(45deg);background:' +
    d[e].color +
    ';z-index:99999'))
    requestAnimationFrame(r)
    }
    function o() {
    var t = 'function' == typeof e.onclick && e.onclick
    e.onclick = function (e) {
    t && t(), i(e)
    }
    }
    function i(e) {
    var a = t.createElement('div')
    ;(a.className = 'heart'),
    d.push({
    el: a,
    x: e.clientX - 5,
    y: e.clientY - 5,
    scale: 1,
    alpha: 1,
    color: s(),
    }),
    t.body.appendChild(a)
    }
    function c(e) {
    var a = t.createElement('style')
    a.type = 'text/css'
    try {
    a.appendChild(t.createTextNode(e))
    } catch (t) {
    a.styleSheet.cssText = e
    }
    t.getElementsByTagName('head')[0].appendChild(a)
    }
    function s() {
    return (
    'rgb(' +
    ~~(255 * Math.random()) +
    ',' +
    ~~(255 * Math.random()) +
    ',' +
    ~~(255 * Math.random()) +
    ')'
    )
    }
    var d = []
    ;(e.requestAnimationFrame = (function () {
    return (
    e.requestAnimationFrame ||
    e.webkitRequestAnimationFrame ||
    e.mozRequestAnimationFrame ||
    e.oRequestAnimationFrame ||
    e.msRequestAnimationFrame ||
    function (e) {
    setTimeout(e, 1e3 / 60)
    }
    )
    })()),
    n()
    })(window, document)
  • 浮现文字(text.js)

    点击显/隐代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    var a_idx = 0;
    jQuery(document).ready(function($) {
    $("body").click(function(e) {
    var a = new Array("喜欢我", "不喜欢我");
    var $i = $("<span/>").text(a[a_idx]);
    var x = e.pageX,
    y = e.pageY;
    $i.css({
    "z-index": 99999,
    "top": y - 28,
    "left": x - a[a_idx].length * 8,
    "position": "absolute",
    "color": "#ff7a45"
    });
    $("body").append($i);
    $i.animate({
    "top": y - 180,
    "opacity": 0
    }, 1500, function() {
    $i.remove();
    });
    a_idx = (a_idx + 1) % a.length;
    });
    });

hexo/source/_data/body-end.njk 文件中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
{# 鼠标点击特效 #}
{% if theme.cursor_effect == "fireworks" %}
<script async src="/js/cursor/fireworks.js"></script>
{% elseif theme.cursor_effect == "explosion" %}
<canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas>
<script src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script>
<script async src="/js/cursor/explosion.js"></script>
{% elseif theme.cursor_effect == "love" %}
<script async src="/js/cursor/love.js"></script>
{% elseif theme.cursor_effect == "text" %}
<script async src="/js/cursor/text.js"></script>
{% endif %}

然后在Next的配置文件 _config.next.yml 中取消 bodyEnd: source/_data/body-end.njk 的注释。
添加如下配置项:

1
2
3
# 鼠标点击特效
# mouse click effect: fireworks | explosion | love
cursor_effect: fireworks

3、近期文章

hexo/source/_data/sidebar.njk 文件中添加如下代码:

点击显/隐代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% if theme.recent_posts %}
<div class="links-of-blogroll motion-element {{ "links-of-blogroll-" + theme.recent_posts_layout }}">
<div class="links-of-blogroll-title">
<!-- modify icon to fire by szw -->
<i class="fa fa-history fa-{{ theme.recent_posts_icon | lower }}" aria-hidden="true"></i>
{{ theme.recent_posts_title }}
</div>
<ul class="links-of-blogroll-list">
{% set posts = site.posts.sort('-date').toArray() %}
{% for post in posts.slice('0', '5') %}
<li>
<a href="{{ url_for(post.path) }}" title="{{ post.title }}" target="_blank">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}

然后在Next的配置文件 _config.next.yml 中取消 sidebar:source/_data/sidebar.njk 的注释。
添加如下配置项:

1
2
3
recent_posts_title: 近期文章
recent_posts_layout: block
recent_posts: true

4、Live2D模型

参考链接:Hexo添加Live2D看板娘+模型预览

5、文字背景色块

效果如下:
站点配置文件
主题配置文件
站点配置文件
主题配置文件
hexo/source/_data/styles.styl 文件中添加如下代码:

点击显/隐代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 // 颜色块-黄
span#inline-yellow {
display:inline;
padding:.2em .6em .3em;
font-size:80%;
font-weight:bold;
line-height:1;
color:#fff;
text-align:center;
white-space:nowrap;
vertical-align:baseline;
border-radius:0;
background-color: #f0ad4e;
}
// 颜色块-绿
span#inline-green {
display:inline;
padding:.2em .6em .3em;
font-size:80%;
font-weight:bold;
line-height:1;
color:#fff;
text-align:center;
white-space:nowrap;
vertical-align:baseline;
border-radius:0;
background-color: #5cb85c;
}
// 颜色块-蓝
span#inline-blue {
display:inline;
padding:.2em .6em .3em;
font-size:80%;
font-weight:bold;
line-height:1;
color:#fff;
text-align:center;
white-space:nowrap;
vertical-align:baseline;
border-radius:0;
background-color: #2780e3;
}
// 颜色块-紫
span#inline-purple {
display:inline;
padding:.2em .6em .3em;
font-size:80%;
font-weight:bold;
line-height:1;
color:#fff;
text-align:center;
white-space:nowrap;
vertical-align:baseline;
border-radius:0;
background-color: #9954bb;
}
然后在Next的配置文件 `_config.next.yml` 中取消 `style: source/_data/styles.styl` 的注释。 书写格式如下:
1
2
3
4
<span id="inline-blue"> 站点配置文件 </span>
<span id="inline-purple"> 主题配置文件 </span>
<span id="inline-yellow"> 站点配置文件 </span>
<span id="inline-green"> 主题配置文件 </span>

6、点击头像跳转首页

打开 themes\next\layout\_partials\sidebar 目录下 site-overview.njk 文件
将第一个 div 替换为以下内容:

1
2
3
4
5
6
7
8
9
10
<div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
{%- if theme.avatar.url %}
+ <a href="/">
+ <img class="site-author-image" itemprop="image" alt="{{ author }}"
+ src="{{ url_for(theme.avatar.url or theme.images + '/avatar.gif') }}">
+ </a>
{%- endif %}
<p class="site-author-name" itemprop="name">{{ author }}</p>
<div class="site-description" itemprop="description">{{ description }}</div>
</div>

7、修改背景

打开 hexo/source/_data/styles.styl 文件
找到 body 块:

1
2
3
4
5
6
7
8
9
10
body {
background-image: url(/image/site/bg1.jpg);
background-repeat: no-repeat;
background-attachment: fixed;
// background-position: center;
background-size: cover;
+mobile() {
background-size: auto auto;
}
}

8、设置置顶

先卸载 hexo-generator-index 插件:

1
npm uninstall hexo-generator-index

再安装hexo-generator-index-pin-top插件

1
npm install hexo-generator-index-pin-top

hexo/source/_data/post-meta.njk添加如下代码:

1
2
3
4
5
6
7
8
{% if post.top %}
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-thumb-tack"></i>
</span>
<font color=red>置顶</font>
</span>
{% endif %}

此时,文章标题下会显示置顶标识,但不在最前方,这是因为 Next 此部分注入代码位置不在最前方。

打开Hexo\themes\next\layout\_partials\post\post-meta.njk文件,找到以下代码:

1
{{- next_inject('postMeta') }}

将其移动至文件前部位置即可。

二、使用技巧

1、Note 标注功能

效果如下:

Welcome to Hexo!

参考:Note (Bootstrap Callout)

2、居中引用

格式:

1
2
3
{% cq %} 
引用内容
{% endcq %}

效果:

故乡啊,挨着碰着,都是带刺的花。
——小林一茶


参考:
Next官方文档
Next主题仓库

1、Next 主题美化
2、Hexo 搭建个人博客文章汇总(next 8.0)
3、Hexo框架(十三):关于博客主题持续更新的问题和我的新配置方式(next 8.0)
4、Hexo+NexT搭建博客笔记(较全)
5、hexo的next主题个性化教程:打造炫酷网站
6、为 Hexo 的 Next 主题添加 Gitalk 评论
7、Hexo添加Live2D看板娘+模型预览