修改记录

2022-06-27

  1. 升级butterfly至4.3.1,并重新进行内部结构调整

2022-06-28

  1. 新增Gitcalendar

2022-07-31

  1. 将Twikoo换成Waline评论插件

2022-08-02

  1. 引入菜单栏、社交栏多色图标

2022-08-04

  1. 首页文章卡片修改
  2. 使用gulp压缩博客静态资源
  3. 删除SAO-UI-PLAN–Card-Player
  4. 删除SAO-UI-PLAN-Card-Widget
  5. 停用随机背景或banner效果
  6. 启用渐变星空Sky粒子背景特效

2022-08-06

  1. 优化本帖布局
  2. 重新启用Twikoo评论并关闭Waline
  3. 美化Twikoo评论卡片
  4. 停用首页卡片Wosjs动画

2022-08-06

  1. 信笺样式留言板

本站部分文章转载自Akilarの糖果屋,如有侵权,请联系作者,本站仅当学习使用!

写在前面-CSS整合

为了追求视觉体验,不要给index.css等涉及首页样式的CSS文件添加异步加载。然而事实上,相比于给css添加异步加载,不如将我们的魔改样式整合到index.css文件内,减少对服务器的请求次数,这样更能节省加载时间。本合集有关css的创建都是采用该方案

点开查看CSS整合方案
  1. 新建[Blogroot]\themes\butterfly\source\css\_custom文件夹,然后把自定义魔改样式的css文件拖动进去。文件目录层级可以表示为以下情况:
    1
    2
    3
    4
    5
    6
    7
    source
    |__ css
    |__ _custom
    |__ custom1.css
    |__ custom2.css
    |__ custom3.css
    |__ index.styl
  2. [[Blogroot]\themes\butterfly\source\css\index.styl最底处中新增一行代码:@import '_custom/*.css',表示引入_custom文件夹下的所有css文件。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        if hexo-config('css_prefix')
    @import 'nib'

    @import '_third-party/normalize.min.css'
    // project
    @import 'var'
    @import '_global/*'
    @import '_highlight/highlight'
    @import '_page/*'
    @import '_layout/*'
    @import '_tags/*'
    @import '_mode/*'
    + @import '_custom/*.css'
  3. 如果是使用的外链css,也可以在这里引入。同样是修改[Blogroot]\themes\butterfly\source\css\index.styl代码,使用@import逐行引入(此处为了确保自定义样式不会被覆盖,放在最底下):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        if hexo-config('css_prefix')
    @import 'nib'

    @import '_third-party/normalize.min.css'
    // project
    @import 'var'
    @import '_global/*'
    @import '_highlight/highlight'
    @import '_page/*'
    @import '_layout/*'
    @import '_tags/*'
    @import '_mode/*'
    + @import 'https://cdn.jsdelivr.net/gh/username/repo/css/example.css'
  4. 这样一来,每个魔改方案的css依然可以在独立的css文件中找到并修改(如果是手动添加整合的话,只能用注释分割,显然很不利于后续查找修改),而在每次提交时,运行hexo g的过程中就会将所有css文件都整合到index.css,可以在主题配置文件的CDN配置项里给index.css加上jsdelivr进一步提升加载速度(注意刷新jsdelivr的缓存)。

字体样式修改

字体样式的修改需要先引入相应的字体文件,此处推荐使用:

  1. 打开谷歌字体库
  2. 输入预览字样,选择喜欢的字体。找到满意的字体后点击进入字体详情页,可以在右侧找到Select this style等字样的按钮,之后能在侧边栏看到引入内容,分别是字体的API引入链接和font-family写法

  3. 首先需要引入样式,在[Blogroot]\themes\butterfly\source\css\_custom新建font.css,写入字体样式API
    1
    @import url('https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300&display=swap');
  4. 为了便于预览,我们可以试试直接在页面按F12,然后在控制台中进行调试。
  5. 在控制台添加的样式是暂时的,我们在预览觉得满意后,就可以把font-family写进来custom.css
    1
    2
    3
    4
    @import url('https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300&display=swap');
    h1#site-title {
    font-family: 'Roboto+Condensed:wght@300', cursive;
    }
  6. 如果需要把某种字体设置为默认字体,可以在[Blogroot]\_config.butterfly.yml补充以下内容
    1
    2
    3
    4
    5
    6
    7
    # Global font settings
    # Don't modify the following settings unless you know how they work (非必要不要修改)
    font:
    global-font-size: 16px
    code-font-size: 16px
    font-family: ZhuZiAYuan
    code-font-family:
  7. font-display属性介绍

font-display属性是一个新的css属性,可以让自定义字体的显示更加顺滑,取值有:

  1. auto:默认值。使用自定义字体的文本会先被隐藏,直到字体加载结束才会显示。
  2. swap:后备文本立即显示直到自定义字体加载完成后再使用自定义字体渲染文本。
  3. fallback:需要使用自定义字体渲染的文本会在较短的时间(100ms according to Google )不可见,如果自定义字体还没有加载结束,那么就先加载无样式的文本。一旦自定义字体加载结束,那么文本就会被正确赋予样式。
  4. optional:效果和fallback几乎一样,都是先在极短的时间内文本不可见,然后再加载无样式的文本。不过optional选项可以让浏览器自由决定是否使用自定义字体,而这个决定很大程度上取决于浏览器的连接速度。如果速度很慢,那你的自定义字体可能就不会被使用。
    此处得益于谷歌字体API自动引入了该属性,我们无需再添加。
  1. 首先需要下载心仪的字体。此处推荐一个免费的字体库网站,支持在线转换预览和免费字体包下载。
  1. 这里我选择的一款叫做甜甜圈海报字体,根据页面按钮找到字体下载。将下载好的字体放至[Blogroot]\themes\butterfly\source\fonts\

不一定是ttf后缀,其他后缀也是完全正常的,例如eototffonfontttcwoffwoff2

  1. 首先需要引入样式,在[Blogroot]\themes\butterfly\source\css\_custom新建font.css,写入字体样式API
    1
    2
    3
    4
    5
    6
    /* 甜甜圈海报字体 */
    @font-face {
    font-family: 'CandyFont'; /* 字体名自定义即可 */
    src: url('/fonts/CandyHome.ttf'); /* 字体文件路径 */
    font-display: swap;
    }
  2. 为了便于预览,我们可以试试直接在页面按F12,然后在控制台中进行调试。
  3. 在控制台添加的样式是暂时的,我们在预览觉得满意后,就可以把font-family写进来custom.css
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* 甜甜圈海报字体 */
    @font-face {
    font-family: 'CandyFont'; /* 字体名自定义即可 */
    src: url('/fonts/CandyHome.ttf'); /* 字体文件路径 */
    font-display: swap;
    }
    #aside-content {
    font-family: CandyFont;
    }
  4. 如果需要把某种字体设置为默认字体,可以在[Blogroot]\_config.butterfly.yml补充以下内容
    1
    2
    3
    4
    5
    6
    7
    # Global font settings
    # Don't modify the following settings unless you know how they work (非必要不要修改)
    font:
    global-font-size: 16px
    code-font-size: 16px
    font-family: ZhuZiAYuan
    code-font-family:
  5. font-display属性介绍

font-display属性是一个新的css属性,可以让自定义字体的显示更加顺滑,取值有:

  1. auto:默认值。使用自定义字体的文本会先被隐藏,直到字体加载结束才会显示。
  2. swap:后备文本立即显示直到自定义字体加载完成后再使用自定义字体渲染文本。
  3. fallback:需要使用自定义字体渲染的文本会在较短的时间(100ms according to Google )不可见,如果自定义字体还没有加载结束,那么就先加载无样式的文本。一旦自定义字体加载结束,那么文本就会被正确赋予样式。
  4. optional:效果和fallback几乎一样,都是先在极短的时间内文本不可见,然后再加载无样式的文本。不过optional选项可以让浏览器自由决定是否使用自定义字体,而这个决定很大程度上取决于浏览器的连接速度。如果速度很慢,那你的自定义字体可能就不会被使用。

夜间模式或阅读模式修改

点击查看夜间模式或阅读模式修改教程

如果熟悉stylus,可以直接修改[Blogroot]\themes\butterfly\source\css\_mode\darkmode.styl来新增夜间模式样式,但此贴依旧是通过css的方式引入。

有一点需要注意,使用gulp压缩css后,不能以[data-theme=dark]开头,不然会出现本地首个样式能生效但部署远端却无法生效的Bug。

  1. 新建[Blogroot]\themes\butterfly\source\css\_custom\dark-read.css
    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
    /* 阅读模式: */
    .read-mode #aside-content .card-widget{
    background: rgba(158, 204, 171, 0.5)!important;
    }
    .read-mode div#post{
    background: rgba(158, 204, 171, 0.5)!important;
    }
    .read-mode div#page{
    background: rgba(158, 204, 171, 0.5)!important;
    }
    .read-mode div#category{
    background: rgba(158, 204, 171, 0.5)!important;
    }
    .read-mode div#tag{
    background: rgba(158, 204, 171, 0.5)!important;
    }
    .read-mode div#archive{
    background: rgba(158, 204, 171, 0.5)!important;
    }
    /* 夜间模式 */
    [data-theme=dark] div#post{
    background: rgba(35,35,35,0.5)!important;
    }
    [data-theme=dark] div#page{
    background: rgba(35,35,35,0.5)!important;
    }
    [data-theme=dark] div#category{
    background: rgba(35,35,35,0.5)!important;
    }
    [data-theme=dark] div#tag{
    background: rgba(35,35,35,0.5)!important;
    }
    [data-theme=dark] div#archive{
    background: rgba(35,35,35,0.5)!important;
    }
    [data-theme=dark] #recent-posts>.recent-post-item {
    background: rgba(35,35,35,0.5)!important;
    }
    [data-theme=dark] #aside-content .card-widget{
    background: rgba(35,35,35,0.5)!important;
    }
    /* 夜间模式下的阅读模式: */
    [data-theme=dark] .read-mode #aside-content .card-widget{
    background: rgba(35,35,35,0.5)!important;
    color: #ffffff;
    }
    [data-theme=dark] .read-mode div#post{
    background: rgba(35,35,35,0.5)!important;
    color: #ffffff;
    }
    [data-theme=dark] .read-mode div#page{
    background: rgba(35,35,35,0.5)!important;
    color: #ffffff;
    }
    [data-theme=dark] .read-mode div#category{
    background: rgba(35,35,35,0.5)!important;
    color: #ffffff;
    }
    [data-theme=dark] .read-mode div#tag{
    background: rgba(35,35,35,0.5)!important;
    color: #ffffff;
    }
    [data-theme=dark] .read-mode div#archive{
    background: rgba(35,35,35,0.5)!important;
    color: #ffffff;
    }

透明度修改

点击查看透明度修改教程
  1. 新建[Blogroot]\themes\butterfly\source\css\_custom\opacity.css,并添加以下内容
    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
    /* 首页文章卡片 */
    #recent-posts > .recent-post-item{
    background:rgba(255, 255, 255, 0.9);
    }
    /* 首页侧栏卡片 */
    .card-widget{
    background:rgba(255, 255, 255, 0.9)!important;
    }
    /* 文章页面正文背景 */
    div#post{
    background: rgba(255, 255, 255, 0.9);
    }
    /* 分页页面 */
    div#page{
    background: rgba(255, 255, 255, 0.9);
    }
    /* 归档页面 */
    div#archive{
    background: rgba(255, 255, 255, 0.9);
    }
    /* 标签页面 */
    div#tag{
    background: rgba(255, 255, 255, 0.9);
    }
    /* 分类页面 */
    div#category{
    background: rgba(255, 255, 255, 0.9);
    }
    /* opacity定义的是全局的透明度,会影响添加该属性的页面元素及其下属元素 */
    #footer{
    opacity: 0.5;
    }
  2. 如果想定义头图或页脚全透明以实现一图流,继续添加如下内容

一图流方案生效的前提是必须先设置默认的全局背景,在主题配置文件[Blogroot]\_config.butterfly.yml中找到background配置项,改为自己心仪的背景图片链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 页脚透明 */
#footer{
background: transparent!important;
}
/* 页脚黑色透明玻璃效果移除 */
#footer::before{
background: transparent!important;
}
/* 头图透明 */
#page-header{
background: transparent!important;
}
/*top-img黑色透明玻璃效果移除,不建议加,除非你执着于完全一图流或者背景图对比色明显 */
#page-header.post-bg:before {
background-color: transparent!important;
}
/*夜间模式伪类遮罩层透明*/
[data-theme="dark"] #footer::before{
background: transparent!important;
}
[data-theme="dark"] #page-header::before{
background: transparent!important;
}

评论卡片优化

点击查看评论卡片优化教程

当前方案为内测尝鲜版,基本逻辑可以实现,但是js代码可读性较差,且不保证没有冗余步骤。

  1. 新建[Blogroot]\themes\butterfly\source\css\_custom\fixed_comment.css,添加以下内容

    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
    div#post-comment.fixedcomment {
    position: fixed;
    top: 0;
    width: 60%;
    right: 0;
    padding: 25px 30px 20px 20px;
    height: 100vh;
    overflow: scroll;
    z-index: 90;
    background: rgba(222, 222, 222, 0.95);
    box-shadow:3px 2px 14px #464340;
    animation: fixedright 0.5s linear;
    }
    div#post-comment.fixedcomment::-webkit-scrollbar {
    width: 0;
    }
    div#quit-board{
    display: none;
    }
    div#quit-board.fixedcomment {
    position: fixed;
    display:block!important;
    left: 0;
    top: 0;
    width: 40%;
    height: 100vh;
    z-index: 89!important;
    background: rgba(25,25,25,0.3);
    filter: blur(4px) !important;
    animation: fixedleft 0.5s linear;
    }
    /*手机端样式适配*/
    @media screen and (max-width: 768px) {
    div#post-comment.fixedcomment {
    width: 90%;
    right: 0;
    }
    div#quit-board.fixedcomment {
    width: 10%;
    }
    }
    /*动画效果*/
    @keyframes fixedright {
    from {right:-50%;}
    to {right:0;}
    }
    @keyframes fixedleft {
    from {left:-50%;}
    to {left:0;}
    }
    /* 夜间模式匹配 */
    [data-theme="dark"] div#post-comment.fixedcomment {
    background: rgba(35, 35, 35, 0.95);
    box-shadow:3px 2px 12px #90a1a4;
    }
    [data-theme="dark"] div#quit-board.fixedcomment {
    background: rgba(147, 146, 128, 0.3);
    }
  2. 新建[Blogroot]\themes\butterfly\source\js\custom\fixed_comment.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
    //移除FixedComment类,保持原生样式,确保不与最新评论跳转冲突
    function RemoveFixedComment() {
    var activedItems = document.querySelectorAll('.fixedcomment');
    if (activedItems) {
    for (i = 0; i < activedItems.length; i++) {
    activedItems[i].classList.remove('fixedcomment');
    }
    }
    }
    //给post-comment添加fixedcomment类
    function AddFixedComment(){
    var commentBoard = document.getElementById('post-comment');
    var quitBoard = document.getElementById('quit-board');
    commentBoard.classList.add('fixedcomment');
    quitBoard.classList.add('fixedcomment');
    }
    //创建一个蒙版,作为退出键使用
    function CreateQuitBoard(){
    var quitBoard = `<div id="quit-board" onclick="RemoveFixedComment()"></div>`
    var commentBoard = document.getElementById('post-comment');
    commentBoard.insertAdjacentHTML("beforebegin",quitBoard)
    }

    function FixedCommentBtn(){
    //第一步,判断当前是否存在FixedComment类,存在则移除,不存在则添加
    // 获取评论区对象
    var commentBoard = document.getElementById('post-comment');
    // 若评论区存在
    if (commentBoard) {
    // 判断是否存在fixedcomment类
    if (commentBoard.className.indexOf('fixedcomment') > -1){
    // 存在则移除
    RemoveFixedComment();
    }
    else{
    // 不存在则添加
    CreateQuitBoard();
    AddFixedComment();
    }
    }
    // 若不存在评论区则跳转至留言板(留言板路径记得改为自己的)
    else{
    // 判断是否开启了pjax,尽量不破坏全局吸底音乐刷新
    if (pjax){
    pjax.loadUrl("/comments/#post-comment");
    }
    else{
    window.location.href = "/comments/#post-comment";
    }
    }
    }
    //切换页面先初始化一遍,确保开始时是原生状态。所以要加pjax重载。
    RemoveFixedComment();
  3. 修改[Blogroot]\_config.butterfly.ymlinject配置项,引入js代码

    1
    2
    3
    4
    5
        inject:
    head:

    bottom:
    + - <script data-pjax defer src="/js/custom/fixed_comment.js"></script>
  4. 理论上给任意元素添加onclick="FixedCommentBtn();"属性就可以实现评论区侧栏开闭。此处我是修改了原生Butterfly主题的直达评论按钮。修改[Blogroot]\themes\butterfly\layout\includes\rightside.pug

    1
    2
    3
          if commentsJsLoad
    - a#to_comment(href="#post-comment" title=_p("rightside.scroll_to_comment"))
    + button#to_comment(type="button" title=_p("rightside.scroll_to_comment") onclick="FixedCommentBtn();")

添加随机背景或banner

点击查看添加随机背景或banner教程

butterfly主题使用idweb_bgdiv来存放背景图片,使用idpage-headerdiv来存放banner图片。只需要通过重设这个div的背景图片属性就可以替换背景图片。

此方案必须要先在主题配置文件_config.butterfly.yml中配置了默认背景才能生效。最终效果为切换页面或刷新页面时,随机替换当前背景。

  1. 新建[Blogroot]\themes\butterfly\source\js\custom\randombg.js,添加以下内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //随机背景图片数组,图片可以换成图床链接,注意最后一条后面不要有逗号
    var backimg =[
    "url(/img/bg1.JPG)",
    "url(/img/bg2.jpg)",
    "url(/img/bg3.jpg)",
    "url(/img/bg4.jpg)"
    ];
    //获取背景图片总数,生成随机数
    var bgindex =Math.floor(Math.random() * backimg.length);
    //重设背景图片
    document.getElementById("web_bg").style.backgroundImage = backimg[bgindex];
    //随机banner数组,图片可以换成图床链接,注意最后一条后面不要有逗号
    var bannerimg =[
    "url(/img/bg1.JPG)",
    "url(/img/bg2.jpg)",
    "url(/img/bg3.jpg)",
    "url(/img/bg4.jpg)"
    ];
    //获取banner图片总数,生成随机数
    var bannerindex =Math.floor(Math.random() * bannerimg.length);
    //重设banner图片
    document.getElementById("page-header").style.backgroundImage = bannerimg[bannerindex];
  2. [Blogroot]\_config.butterfly.yml引入randombg.js
    1
    2
    3
    4
        inject:
    head:
    bottom:
    + - <script async data-pjax src="/js/custom/randombg.js"></script>

站点动态title

站点动态title是通过js监测是否聚焦于当前页面,从而替换标签显示内容。

点击查看添加站点动态title教程
  1. 新建[Blogroot]\themes\butterfly\source\js\custom\diytitle.js,添加以下内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //动态标题
    var OriginTitile = document.title;
    var titleTime;
    document.addEventListener('visibilitychange', function () {
    if (document.hidden) {
    //离开当前页面时标签显示内容
    document.title = 'w(゚Д゚)w 不要走!再看看嘛!';
    clearTimeout(titleTime);
    }
    else {
    //返回当前页面时标签显示内容
    document.title = '♪(^∇^*)欢迎回来!' + OriginTitile;
    //两秒后变回正常标题
    titleTime = setTimeout(function () {
    document.title = OriginTitile;
    }, 2000);
    }
    });
  2. [Blogroot]\_config.butterfly.yml引入diytitle.js
    1
    2
    3
    4
    5
    6
     inject:
    head:
    # - <link rel="stylesheet" href="/xxx.css">
    bottom:
    # - <script src="xxxx"></script>
    + - <script async src="/js/costom/diytitle.js"></script>

引入Iconfont

点击查看Iconfont引入教程
  1. 修改[Blogroot]\_config.butterfly.ymlinject配置项
  2. 新建[Blogroot]\themes\butterfly\source\css\_custom\iconfont.css样式
  3. 新建[Blogroot]\themes\butterfly\scripts\tag\iconfont.js

菜单栏、社交栏多色图标引入

点击查看菜单栏、社交栏多色图标引入教程
  1. 完成InconfontSymbol方式导入
  2. 修改[Blogroot]\themes\butterfly\layout\includes\header\menu_item.pug
  3. 修改[Blogroot]\themes\butterfly\layout\includes\header\social.pug
点击查看Sidebar Card Clock引入教程
  1. 安装插件,在博客根目录[Blogroot]下打开终端,运行以下指令:
    1
    npm install hexo-butterfly-swiper --save
  2. [Blogroot]\_config.butterfly.yml中添加swiper配置项
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # electric_clock
    # see https://akilar.top/posts/4e39cf4a/
    electric_clock:
    enable: true # 开关
    priority: 5 #过滤器优先权
    enable_page: all # 应用页面
    exclude:
    # - /posts/
    # - /about/
    layout: # 挂载容器类型
    type: class
    name: sticky_layout
    index: 0
    loading: https://npm.elemecdn.com/hexo-butterfly-clock/lib/loading.gif #加载动画自定义
    clock_css: https://npm.elemecdn.com/hexo-butterfly-clock/lib/clock.min.css
    clock_js: https://npm.elemecdn.com/hexo-butterfly-clock/lib/clock.min.js
    ip_api: https://pv.sohu.com/cityjson?ie=utf-8
  3. 参数释义
参数备选值/类型释义
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为10
enabletrue/false【必选】控制开关
enable_pagepath/all【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/’,分类页面就填’/categories/’。若要应用于所有页面,就填’all’,默认为all
excludepath【可选】填写想要屏蔽的页面,可以多个。写法见示例。原理是将屏蔽项的内容逐个放到当前路径去匹配,若当前路径包含任一屏蔽项,则不会挂载。
layout.typeid/class【可选】挂载容器类型,填写id或class,不填则默认为id
layout.nametext【必选】挂载容器名称
layout.index0和正整数【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位
loadingURL【可选】电子钟加载动画的图片
clock_cssURL【可选】电子钟样式CDN资源
clock_jsURL【可选】电子钟执行脚本CDN资源
ip_apiURL【可选】获取时钟IP的API

Swiper Bar

点击查看Swiper Bar教程
  1. 安装插件,在博客根目录[Blogroot]下打开终端,运行以下指令:
    1
    npm install hexo-butterfly-swiper --save
  2. [Blogroot]\_config.butterfly.yml中添加swiper配置项
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # hexo-butterfly-swiper
    # see https://akilar.top/posts/8e1264d1/
    swiper:
    enable: true # 开关
    priority: 5 #过滤器优先权
    enable_page: all # 应用页面
    timemode: date #date/updated
    layout: # 挂载容器类型
    type: id
    name: recent-posts
    index: 0
    default_descr: 再怎么看我也不知道怎么描述它的啦!
    swiper_css: https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiper.min.css #swiper css依赖
    swiper_js: https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiper.min.js #swiper js依赖
    custom_css: https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiperstyle.css # 适配主题样式补丁
    custom_js: https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiper_init.js # swiper初始化方法
  3. 参数释义
参数备选值/类型释义
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为10
enabletrue/false【必选】控制开关
enable_pagepath/all【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/’,分类页面就填’/categories/’。若要应用于所有页面,就填’all’,默认为all
timemodedate/updated【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date
layout.typeid/class【可选】挂载容器类型,填写id或class,不填则默认为id
layout.nametext【必选】挂载容器名称
layout.index0和正整数【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位
default_descrtext默认文章描述
swiper_cssURL【可选】自定义的swiper依赖项css链接
swiper_jsURL【可选】自定义的swiper依赖项加js链接
custom_cssURL【可选】适配主题样式补丁
custom_jsURL【可选】swiper初始化方法

Tag Plugins Plus

点击查看教程
  1. 安装插件,在博客根目录[Blogroot]下打开终端,运行以下指令:
    1
    2
    3
    npm install hexo-butterfly-tag-plugins-plus --save
    npm uninstall hexo-renderer-marked --save
    npm install hexo-renderer-kramed --save
  2. [Blogroot]\_config.butterfly.yml中添加tag_plugins配置项

相关推荐版块侧栏卡片化

点击查看相关推荐版块侧栏卡片化教程
  1. 修改[Blogroot]\themes\butterfly\scripts\helpers\related_post.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
      if (relatedPosts.length > 0) {
    - result += '<div class="relatedPosts">'
    - result += `<div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>${headlineLang}</span></div>`
    - result += '<div class="relatedPosts-list">'
    + result += '<div class="card-widget card-recommend-post">'
    + result += `<div class="item-headline"><i class="fas fa-dharmachakra"></i><span>${headlineLang}</span></div>`
    + result += '<div class="aside-list">'
    for (let i = 0; i < Math.min(relatedPosts.length, limitNum); i++) {
    const cover =
    relatedPosts[i].cover === false
    ? relatedPosts[i].randomcover
    : relatedPosts[i].cover
    - result += `<div><a href="${this.url_for(relatedPosts[i].path)}" title="${relatedPosts[i].title}">`
    - result += `<img class="cover" src="${this.url_for(cover)}" alt="cover">`
    + result += `<div class="aside-list-item">`
    + result += `<a class="thumbnail" href="${this.url_for(relatedPosts[i].path)}" title="${relatedPosts[i].title}"><img src="${this.url_for(cover)}" alt="${relatedPosts[i].title}"></a>`
    + result += `<div class="content">`
    + result += `<a class="title" href="${this.url_for(relatedPosts[i].path)}" title="${relatedPosts[i].title}">${relatedPosts[i].title}</a>`
    if (dateType === 'created') {
    - result += `<div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> ${this.date(relatedPosts[i].created, hexoConfig.date_format)}</div>`
    + result += `<time datetime="${this.date(relatedPosts[i].created, hexoConfig.date_format)}" title="发表于 ${this.date(relatedPosts[i].created, hexoConfig.date_format)}">${this.date(relatedPosts[i].created, hexoConfig.date_format)}</time>`
    } else {
    - result += `<div class="content is-center"><div class="date"><i class="fas fa-history fa-fw"></i> ${this.date(relatedPosts[i].updated, hexoConfig.date_format)}</div>`
    + result += `<time datetime="${this.date(relatedPosts[i].updated, hexoConfig.date_format)}" title="发表于 ${this.date(relatedPosts[i].updated, hexoConfig.date_format)}">${this.date(relatedPosts[i].updated, hexoConfig.date_format)}</time>`
    }
    - result += `<div class="title">${relatedPosts[i].title}</div>`
    - result += '</div></a></div>'
    + result += `</div></div>`
    }

    result += '</div></div>'
    return result
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    if (relatedPosts.length > 0) {
    result += '<div class="card-widget card-recommend-post">'
    result += `<div class="item-headline"><i class="fas fa-dharmachakra"></i><span>${headlineLang}</span></div>`
    result += '<div class="aside-list">'
    for (let i = 0; i < Math.min(relatedPosts.length, limitNum); i++) {
    const cover =
    relatedPosts[i].cover === false
    ? relatedPosts[i].randomcover
    : relatedPosts[i].cover
    result += `<div class="aside-list-item">`
    result += `<a class="thumbnail" href="${this.url_for(relatedPosts[i].path)}" title="${relatedPosts[i].title}"><img src="${this.url_for(cover)}" alt="${relatedPosts[i].title}"></a>`
    result += `<div class="content">`
    result += `<a class="title" href="${this.url_for(relatedPosts[i].path)}" title="${relatedPosts[i].title}">${relatedPosts[i].title}</a>`
    if (dateType === 'created') {
    result += `<time datetime="${this.date(relatedPosts[i].created, hexoConfig.date_format)}" title="发表于 ${this.date(relatedPosts[i].created, hexoConfig.date_format)}">${this.date(relatedPosts[i].created, hexoConfig.date_format)}</time>`
    } else {
    result += `<time datetime="${this.date(relatedPosts[i].updated, hexoConfig.date_format)}" title="发表于 ${this.date(relatedPosts[i].updated, hexoConfig.date_format)}">${this.date(relatedPosts[i].updated, hexoConfig.date_format)}</time>`
    }
    result += `</div></div>`
    }
    result += '</div></div>'
    return result
    }
  2. 因为原本的版块是在文章下方,而现在我们需要把它改到侧栏,所以需要修改[Blogroot]\themes\butterfly\layout\post.pug,大约26行的位置先移除在文章底部的推荐版块。
    1
    2
    3
    4
    5
    6
      if theme.post_pagination
    include includes/pagination.pug
    - if theme.related_post && theme.related_post.enable
    - != related_posts(page,site.posts)

    if page.comments !== false && theme.comments && theme.comments.use
  3. 因为感觉文章也最新文章和推荐文章同时存在,最新文章就显得有点多余了,所以我把最新文章的侧栏卡片注释了,修改[Blogroot]\themes\butterfly\layout\includes\widget\index.pug
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #aside-content.aside-content
    //- post
    if is_post()
    if showToc && theme.toc.style_simple
    .sticky_layout
    include ./card_post_toc.pug
    else
    !=partial('includes/custom/SAO_card_player', {}, {cache:theme.fragment_cache})
    !=partial('includes/widget/card_announcement', {}, {cache:theme.fragment_cache})
    !=partial('includes/widget/card_top_self', {}, {cache:theme.fragment_cache})
    .sticky_layout
    if showToc
    include ./card_post_toc.pug
    + if theme.related_post && theme.related_post.enable
    + != related_posts(page,site.posts)
    - - !=partial('includes/widget/card_recent_post', {}, {cache:theme.fragment_cache})
    + //- !=partial('includes/widget/card_recent_post', {}, {cache:theme.fragment_cache})
    !=partial('includes/widget/card_ad', {}, {cache:theme.fragment_cache})

改动以后,是将推荐版块放到了侧栏卡片,所以如果要想看到相关推荐侧栏卡片,至少需要满足以下几个条件:

  1. 关闭了style_simple,这样才不至于在文章页只能看到一个目录卡片。
  2. 当前文章有相同tag的文章可推荐。如果没有相同tag的文章,自然不存在可以推荐的内容,也就不会生成相关推荐版块。
  3. 开启了related_post配置项,推荐都没开的话就别指望有相关推荐版块了吧。

Gitcalendar

点击查看Gitcalendar引入教程
  1. 安装插件,在博客根目录[Blogroot]下打开终端,运行以下指令:

    1
    npm install hexo-filter-gitcalendar --save
  2. [Blogroot]\_config.butterfly.yml中添加gitcalendar配置项

    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
    # hexo-filter-gitcalendar
    # see https://akilar.top/posts/1f9c68c9/
    gitcalendar:
    enable: true # 开关
    priority: 5 #过滤器优先权
    enable_page: / # 应用页面
    # butterfly挂载容器
    layout: # 挂载容器类型
    type: id
    name: recent-posts
    index: 0
    # volantis挂载容器
    # layout:
    # type: class
    # name: l_main
    # index: 0
    # matery挂载容器
    # layout:
    # type: id
    # name: indexCard
    # index: 0
    # mengd挂载容器
    # layout:
    # type: class
    # name: content
    # index: 0
    user: Akilarlxh #git用户名
    apiurl: 'https://gitcalendar.akilar.top'
    minheight:
    pc: 280px #桌面端最小高度
    mibile: 0px #移动端最小高度
    color: "['#e4dfd7', '#f9f4dc', '#f7e8aa', '#f7e8aa', '#f8df72', '#fcd217', '#fcc515', '#f28e16', '#fb8b05', '#d85916', '#f43e06']" #橘黄色调
    # color: "['#ebedf0', '#fdcdec', '#fc9bd9', '#fa6ac5', '#f838b2', '#f5089f', '#c4067e', '#92055e', '#540336', '#48022f', '#30021f']" #浅紫色调
    # color: "['#ebedf0', '#f0fff4', '#dcffe4', '#bef5cb', '#85e89d', '#34d058', '#28a745', '#22863a', '#176f2c', '#165c26', '#144620']" #翠绿色调
    # color: "['#ebedf0', '#f1f8ff', '#dbedff', '#c8e1ff', '#79b8ff', '#2188ff', '#0366d6', '#005cc5', '#044289', '#032f62', '#05264c']" #天青色调
    container: .recent-post-item(style='width:100%;height:auto;padding:10px;') #父元素容器,需要使用pug语法
    gitcalendar_css: https://npm.elemecdn.com/hexo-filter-gitcalendar/lib/gitcalendar.css
    gitcalendar_js: https://npm.elemecdn.com/hexo-filter-gitcalendar/lib/gitcalendar.js
  3. 参数释义

参数备选值/类型释义
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为10,选填
enabletrue/false【必选】控制开关
enable_pagepath/all【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为’/‘
layout.typeid/class【可选】挂载容器类型,填写id或class,不填则默认为id
layout.nametext【必选】挂载容器名称
layout.index0和正整数【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位
usertext【必选】git用户名
apiurlurl【可选】默认使用提供文档提供的api,但还是建议自建api,参考教程:自建API部署
minheight.pc280px【可选】桌面端最小高度,默认为280px
minheight.mobile0px【可选】移动端最小高度,默认为0px
colorlist【可选】一个包含11个色值的数组,文档给出了四款预设值
containerpug【可选】预留的父元素容器,用以适配多主题,需要用pug语法填写,目前已适配butterflyvolantismaterymengd主题,这四个主题,插件会自自动识别_config.yml内填写的theme配置项。其余主题需要自己填写父元素容器。
gitcalendar_cssURL【可选】自定义CSS样式链接
gitcalendar_jsURL【可选】自定义js链接

引入Waline评论

点击查看Waline引入教程
  1. LeanCloud中创建数据库
  2. Vercel部署服务端
  3. 修改[Blogroot]\themes\butterfly\layout\includes\third-praty\comments\waline.pug
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
- const { serverURL, option, pageview } = theme.waline
- const { lazyload, count, use } = theme.comments

script.
function loadWaline () {
function insertCSS () {
const link = document.createElement("link")
link.rel = "stylesheet"
link.href = "!{url_for(theme.asset.waline_css)}"
document.head.appendChild(link)
}

function initWaline () {
const waline = Waline.init(Object.assign({
el: '#waline-wrap',
serverURL: '!{serverURL}',
pageview: !{lazyload ? false : pageview},
dark: 'html[data-theme="dark"]',
path: window.location.pathname,
comment: !{lazyload ? false : count},
imageUploader: function (file) {
let formData = new FormData()
let headers = new Headers()

formData.append('file', file)
headers.append('Authorization', 'Bearer 210|<此处填你的token>')
headers.append('Accept', 'application/json')

return fetch('https://7bu.top/api/v1/upload', {
method: 'POST',
headers: headers,
body: formData,
})
.then((resp) => resp.json())
.then((resp) => resp.data.links.url);
},
}, !{JSON.stringify(option)}))
}

if (typeof Waline === 'function') initWaline()
else {
insertCSS()
getScript('!{url_for(theme.asset.waline_js)}').then(initWaline)
}
}

if ('!{use[0]}' === 'Waline' || !!{lazyload}) {
if (!{lazyload}) btf.loadComment(document.getElementById('waline-wrap'),loadWaline)
else setTimeout(loadWaline, 0)
} else {
function loadOtherComment () {
loadWaline()
}
}

引入Twikoo评论

点击查看Twikoo引入教程

twikoo评论块气泡风格美化

点击查看twikoo评论块气泡风格美化教程
  1. 新建[Blogroot]\themes\butterfly\source\css\_custom\twikoo_beautify.css,添加以下内容
    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
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    /* 自定义twikoo评论输入框高度 */
    .tk-input[data-v-619b4c52] .el-textarea__inner {
    height: 130px !important;
    }
    /* 输入评论时自动隐藏输入框背景图片 */
    .tk-input[data-v-619b4c52] .el-textarea__inner:focus {
    background-image: none !important;
    }
    /* 调整楼中楼样式 ,整体左移,贴合气泡化效果 */
    .tk-replies {
    left: -70px;
    width: calc(100% + 70px);
    }
    /* 头像宽度调整 rem单位与全局字体大小挂钩,需配合自己情况调整大小以保证头像显示完整*/
    .tk-replies .tk-avatar {
    width: 2.5rem !important;
    height: 2.5rem !important;
    }
    .tk-replies .tk-avatar img {
    width: 2.5rem !important;
    height: 2.5rem !important;
    }
    /* 回复框左移,避免窄屏时出框 */
    .tk-comments-container .tk-submit {
    position: relative;
    left: -70px;
    }
    /* 评论块气泡化修改 */
    .tk-content {
    background: linear-gradient( 135deg, #ABDCFF 10%, #0396FF 100%); /*默认模式访客气泡配色*/
    padding: 10px;
    color: #fff; /*默认模式访客气泡字体配色*/
    border-radius: 10px;
    font-size: 16px !important;
    width: fit-content;
    max-width: 100%;
    position: relative !important;
    overflow: visible !important;
    max-height: none !important;
    }
    /* 修复图片出框 */
    .tk-content img {
    max-width: 100% !important;
    }
    /* 修复过长文本出框 */
    .tk-content pre {
    white-space: pre-wrap;
    word-wrap: break-word;
    }
    .tk-content a {
    color: #eeecaa; /*默认模式超链接配色*/
    }
    .tk-content::before {
    content: '';
    width: 0;
    height: 0;
    position: absolute;
    top: 20px;
    left: -13px;
    border-top: 2px solid transparent;
    border-bottom: 20px solid transparent;
    border-right: 15px solid #00a6ff; /*默认模式访客气泡小三角配色*/
    border-left: 0px solid transparent;
    }
    .tk-master .tk-content {
    background: linear-gradient( 135deg, #FAD7A1 10%, #E96D71 100%);/*默认模式博主气泡配色*/
    color: #fff; /*默认模式博主气泡字体配色*/
    width: fit-content;
    max-width: 100%;
    }
    .tk-master .tk-content a {
    color: #eeecaa;
    }
    .tk-master .tk-content::before {
    content: '';
    width: 0;
    height: 0;
    position: absolute;
    top: 20px;
    left: -13px;
    border-top: 2px solid transparent;
    border-bottom: 20px solid transparent;
    border-right: 15px solid #ff8080; /*默认模式博主气泡小三角配色*/
    border-left: 0px solid transparent;
    }
    .tk-row[data-v-d82ce9a0] {
    max-width: 100%;
    width: fit-content;
    }
    .tk-avatar {
    border-radius: 50%;
    margin-top: 10px;
    }

    /* 夜间模式配色,具体比照上方默认模式class */
    [data-theme="dark"] .tk-content {
    background: linear-gradient( 135deg, #6B73FF 10%, #000DFF 100%);
    color: #fff;
    }
    [data-theme="dark"] .tk-content a {
    color: #dfa036;
    }
    [data-theme="dark"] .tk-content::before {
    border-right: 15px solid #000;
    }
    [data-theme="dark"] .tk-master .tk-content {
    background: linear-gradient( 135deg, #FAB2FF 10%, #1904E5 100%);;
    color: #fff;
    }
    [data-theme="dark"] .tk-master .tk-content a {
    color: #dfa036;
    }
    [data-theme="dark"] .tk-master .tk-content::before {
    border-top: 2px solid transparent;
    border-bottom: 20px solid transparent;
    border-right: 15px solid #000;
    border-left: 0px solid transparent;
    }
    /* 自适应内容 */
    @media screen and (min-width: 1024px) {
    /* 设置宽度上限,避免挤压博主头像 */
    .tk-content {
    max-width: 75%;
    width: fit-content;
    }
    .tk-master .tk-content {
    width: 75%;
    }
    .tk-master .tk-content::before {
    left: 100%;
    border-left: 15px solid #ff8080;
    border-right: 0px solid transparent;
    }
    .tk-master .tk-avatar {
    position: relative;
    left: calc(75% + 70px);
    }
    .tk-master .tk-row[data-v-d82ce9a0] {
    position: relative;
    top: 0px;
    left: calc(75% - 230px);
    }
    [data-theme="dark"] .tk-master .tk-content::before {
    border-left: 15px solid #000;
    border-right: 0px solid transparent;
    }
    }
    /* 设备名称常态隐藏,悬停评论时显示 */
    .tk-extras {
    opacity: 0;
    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
    filter: alpha(opacity=0);
    }
    .tk-content:hover + .tk-extras {
    -webkit-animation: tk-extras-fadeIn 0.5s linear;
    -moz-animation: tk-extras-fadeIn 0.5s linear;
    -o-animation: tk-extras-fadeIn 0.5s linear;
    -ms-animation: tk-extras-fadeIn 0.5s linear;
    animation: tk-extras-fadeIn 0.5s linear;
    -webkit-animation-fill-mode: forwards;
    -moz-animation-fill-mode: forwards;
    -o-animation-fill-mode: forwards;
    -ms-animation-fill-mode: forwards;
    animation-fill-mode: forwards;
    }
    @-moz-keyframes tk-extras-fadeIn {
    from {
    opacity: 0;
    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
    filter: alpha(opacity=0);
    }
    to {
    opacity: 1;
    -ms-filter: none;
    filter: none;
    }
    }
    @-webkit-keyframes tk-extras-fadeIn {
    from {
    opacity: 0;
    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
    filter: alpha(opacity=0);
    }
    to {
    opacity: 1;
    -ms-filter: none;
    filter: none;
    }
    }
    @-o-keyframes tk-extras-fadeIn {
    from {
    opacity: 0;
    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
    filter: alpha(opacity=0);
    }
    to {
    opacity: 1;
    -ms-filter: none;
    filter: none;
    }
    }
    @keyframes tk-extras-fadeIn {
    from {
    opacity: 0;
    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
    filter: alpha(opacity=0);
    }
    to {
    opacity: 1;
    -ms-filter: none;
    filter: none;
    }
    }

首页卡片修改

点击查看首页卡片修改教程

样式配色因为采用了大量伪类,所以如果底色采用了半透明配色,可能因为卡片叠加加深导致接合边界非常明显的暴露出来。所以在配色上,不建议加半透明的。因为部分伪类的偏移量是靠计算得出的,为了尽量满足自适应效果,部分位置保留了5%左右的容差。所以在一些极端屏宽比下,还是会出现一些样式不完美问题。

  1. 修改[Blogroot]\themes\butterfly\layout\includes\mixins\post-ui.pug,将整个文件的内容替换为以下代码。
    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
    mixin postUI(posts)
    each article , index in page.posts.data
    .recent-post-item
    -
    let link = article.link || article.path
    let title = article.title || _p('no_title')
    const position = theme.cover.position
    let leftOrRight = position === 'both'
    ? index%2 == 0 ? 'left' : 'right'
    : position === 'left' ? 'left' : 'right'
    let post_cover = article.cover
    let no_cover = article.cover === false || !theme.cover.index_enable ? 'no-cover' : ''
    -
    .recent-post-content(class=leftOrRight)
    a.article-content(href=url_for(link) title=subtitle)
    //- Display the article introduction on homepage
    case theme.index_post_content.method
    when false
    - break
    when 1
    .article-content-text!= article.description
    when 2
    if article.description
    .article-content-text!= article.description
    else
    - const content = strip_html(article.content)
    - let expert = content.substring(0, theme.index_post_content.length)
    - content.length > theme.index_post_content.length ? expert += ' ...' : ''
    .article-content-text!= expert
    default
    - const content = strip_html(article.content)
    - let expert = content.substring(0, theme.index_post_content.length)
    - content.length > theme.index_post_content.length ? expert += ' ...' : ''
    .article-content-text!= expert
    .recent-post-info
    a.article-title(href=url_for(link) title=subtitle)
    .article-title-link= title
    .recent-post-meta
    .article-meta-wrap
    if (is_home() && (article.top || article.sticky > 0))
    span.article-meta
    i.fas.fa-thumbtack.sticky
    span.sticky= _p('sticky')
    span.article-meta-separator |
    if (theme.post_meta.page.date_type)
    span.post-meta-date
    if (theme.post_meta.page.date_type === 'both')
    i.far.fa-calendar-alt
    span.article-meta-label=_p('post.created')
    time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))=date(article.date, config.date_format)
    span.article-meta-separator |
    i.fas.fa-history
    span.article-meta-label=_p('post.updated')
    time.post-meta-date-updated(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated))=date(article.updated, config.date_format)
    else
    - let data_type_updated = theme.post_meta.page.date_type === 'updated'
    - let date_type = data_type_updated ? 'updated' : 'date'
    - let date_icon = data_type_updated ? 'fas fa-history' :'far fa-calendar-alt'
    - let date_title = data_type_updated ? _p('post.updated') : _p('post.created')
    i(class=date_icon)
    span.article-meta-label=date_title
    time(datetime=date_xml(article[date_type]) title=date_title + ' ' + full_date(article[date_type]))=date(article[date_type], config.date_format)
    if (theme.post_meta.page.categories && article.categories.data.length > 0)
    span.article-meta
    span.article-meta-separator |
    i.fas.fa-inbox
    each item, index in article.categories.data
    a(href=url_for(item.path)).article-meta__categories #[=item.name]
    if (index < article.categories.data.length - 1)
    i.fas.fa-angle-right.article-meta-link
    if (theme.post_meta.page.tags && article.tags.data.length > 0)
    span.article-meta.tags
    span.article-meta-separator |
    i.fas.fa-tag
    each item, index in article.tags.data
    a(href=url_for(item.path)).article-meta__tags #[=item.name]
    if (index < article.tags.data.length - 1)
    span.article-meta-link #[='•']

    mixin countBlockInIndex
    - needLoadCountJs = true
    span.article-meta
    span.article-meta-separator |
    i.fas.fa-comments
    if block
    block
    span.article-meta-label= ' ' + _p('card_post_count')

    if theme.comments.card_post_count
    case theme.comments.use[0]
    when 'Disqus'
    when 'Disqusjs'
    +countBlockInIndex
    a(href=full_url_for(link) + '#disqus_thread')
    when 'Valine'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment' itemprop="discussionUrl")
    span.valine-comment-count(data-xid=url_for(link) itemprop="commentCount")
    when 'Waline'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.waline-comment-count(id=url_for(link))
    when 'Twikoo'
    +countBlockInIndex
    a.twikoo-count(href=url_for(link) + '#post-comment')
    when 'Facebook Comments'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.fb-comments-count(data-href=urlNoIndex(article.permalink))
    .recent-post-cover
    img.article-cover(src=url_for(post_cover) onerror=`this.onerror=null;this.src='`+ url_for(theme.error_img.post_page) + `'` alt=subtitle)

    if theme.ad && theme.ad.index
    if (index + 1) % 3 == 0
    .recent-post-item.ads-wrap!=theme.ad.index
  2. 修改[Blogroot]\themes\butterfly\source\css\_page\homepage.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
    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
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    //default color:
    :root
    --recent-post-bgcolor: rgba(255, 255, 255, 0.9)
    --article-content-bgcolor: #49b1f5
    --recent-post-triangle: #fff
    --recent-post-cover-shadow: #ffffff
    [data-theme="dark"]
    --recent-post-bgcolor: rgba(35,35,35,0.5)
    --article-content-bgcolor: #99999a
    --recent-post-triangle: #37e2dd
    --recent-post-cover-shadow: #232323
    .recent-posts
    padding 0 15px 0 15px

    .recent-post-item
    margin-bottom 15px
    width 100%
    background var(--recent-post-bgcolor)
    overflow hidden
    border-radius 15px
    .recent-post-info
    .article-title-link
    display -webkit-box
    -webkit-box-orient vertical
    -webkit-line-clamp 2
    overflow hidden
    .article-content
    background var(--article-content-bgcolor)
    position relative
    display flex
    align-items: center;
    justify-content: center;
    .article-content-text
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    display -webkit-box
    -webkit-box-orient vertical
    -webkit-line-clamp 4
    text-overflow: ellipsis
    overflow hidden
    color #fff
    text-shadow: 1px 2px 3px #000;
    .recent-post-cover
    position relative
    background transparent
    img
    &.article-cover
    height 100%
    width 100%
    object-fit cover

    .recent-post-info
    align-items center
    flex-direction column
    position relative
    background var(--recent-post-bgcolor)
    display flex
    color #000000
    .article-title
    height 50%
    font-size 24px
    display: flex
    align-items: center
    justify-content: flex-end
    flex-direction: column
    .article-title-link
    color: var(--text-highlight-color)
    transition: all .2s ease-in-out
    &:hover
    color: $text-hover
    .recent-post-meta
    height 50%
    display: flex
    align-items: center
    justify-content: flex-start
    flex-direction: column
    .article-meta-wrap
    font-size 12px
    color #969797
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
    a
    color: var(--text-highlight-color)
    transition: all .2s ease-in-out
    color #969797
    &:hover
    color: $text-hover
    &.ads-wrap
    display: block !important
    height: auto !important
    @media screen and (min-width:600px)
    .recent-post-item
    &:hover
    .recent-post-content
    &.both,
    &.right
    transform translateX(21%)
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &::before
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    left: 50px;
    .article-content-text
    margin 20px 20px 20px 60px
    &.left
    transform translateX(-21%)
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &::before
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    right: 50px;
    .article-content-text
    margin 20px 60px 20px 20px


    .recent-post-content
    background var(--recent-post-bgcolor)
    position relative
    height 200px
    width 130%
    z-index 0
    display flex
    overflow hidden
    border 0px solid
    &::before
    content: "";
    width: 0;
    height: 0;
    background: transparent;
    position: absolute;
    z-index: 3;
    top: calc(50% - 10px);
    border-top: 10px solid transparent;
    border-bottom: 10px solid transparent;
    transition: all .5s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &.both,
    &.right
    flex-direction: row;
    left calc(-23.07% - 41px)
    transition: all .5s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &::before
    left: calc(23.07% + 40px);
    border-left: 6px solid var(--recent-post-triangle);
    .recent-post-info
    &::before
    background linear-gradient(to right, var(--recent-post-cover-shadow), transparent)
    left calc(100% - 1px)
    .article-content
    &::before
    right -59px
    border-left 60px solid var(--article-content-bgcolor)
    .article-content-text
    margin 20px 20px 20px 0px
    .article-title
    padding 0px 30px 0px 70px
    .recent-post-meta
    padding 0px 20px 0px 70px
    &.left
    flex-direction: row-reverse;
    right 9px
    transition: all .5s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &::before
    right: calc(23.07% + 40px);
    border-right: 6px solid var(--recent-post-triangle);
    .recent-post-info
    &::before
    background linear-gradient(to left, var(--recent-post-cover-shadow), transparent)
    right calc(100% - 1px)
    .article-content
    &::before
    left -59px
    border-right 60px solid var(--article-content-bgcolor)
    .article-content-text
    margin 20px 0px 20px 20px
    .article-title
    padding 0px 70px 0px 30px
    .recent-post-meta
    padding 0px 70px 0px 20px

    .article-content
    width 30%
    height 200px
    left 0
    align-items center
    &::before
    content ""
    width 0
    height 0
    background transparent
    position absolute
    z-index 2
    top 0
    border-top 100px solid transparent
    border-bottom 100px solid transparent
    .recent-post-info
    width 60%
    height 200px
    &::before
    content ""
    width 200px
    height 200px
    position absolute
    z-index 1
    top 0
    .recent-post-meta
    & > .article-meta-wrap
    margin: 6px 0
    color: $theme-meta-color
    font-size: 90%

    & > .post-meta-date
    cursor: default

    .sticky
    color: $sticky-color

    i
    margin: 0 4px 0 0

    .article-meta-label
    if hexo-config('post_meta.page.label')
    padding-right: 4px
    else
    display: none

    .article-meta-separator
    margin: 0 6px

    .article-meta-link
    margin: 0 4px

    if hexo-config('post_meta.page.date_format') == 'relative'
    time
    display: none

    a
    color: $theme-meta-color

    &:hover
    color: $text-hover
    text-decoration: underline
    .recent-post-cover
    width 40%
    height 200px
    @media screen and (max-width:600px)
    .recent-post-item
    height 400px
    .recent-post-content
    display flex
    flex-direction: column
    height 400px
    .article-content
    pointer-events none
    order: 1;
    height: 200px;
    position: absolute;
    width: calc(100% - 40px);
    z-index: 3;
    background: rgba(22,22,22,0.5);
    border-top-left-radius: 15px;
    border-top-right-radius: 15px;
    display: none
    opacity: 0
    .article-content-text
    height 120px
    color: white;
    width: 80%

    .recent-post-cover
    order: 2
    height 200px
    transition: all .5s
    .recent-post-info
    order: 3
    height 200px
    &::before
    content: '';
    width: 0;
    height: 0;
    position: absolute;
    z-index: 3;
    bottom: calc(100% - 4px);
    left: 0;
    border-bottom: 50px solid var(--recent-post-bgcolor);
    border-right: 300px solid transparent;
    &::after
    content: '';
    width: 0;
    height: 0;
    position: absolute;
    z-index: 3;
    bottom: calc(100% + 150px);
    right: 0;
    border-top: 50px solid var(--recent-post-bgcolor);
    border-left: 300px solid transparent;
    .article-title
    padding: 0px 35px 0px 35px
    .recent-post-meta
    padding: 0px 30px 0px 30px
    &:hover
    .article-content
    display: flex !important;
    animation: shutter-effect-content 0.5s 2 forwards linear
    .recent-post-info
    &::before
    animation: shutter-effect-left 0.5s 1 ease-in-out
    &::after
    animation: shutter-effect-right 0.5s 1 ease-in-out
    .recent-post-cover
    filter blur(2px)
    @keyframes shutter-effect-right {
    0%{
    bottom: calc(100% + 150px);
    border-top: 50px solid var(--recent-post-bgcolor);
    border-left: 300px solid transparent;
    }
    50%{
    bottom: 100%;
    border-top: 200px solid var(--recent-post-bgcolor);
    border-left: 600px solid transparent;
    }
    100%{
    bottom: calc(100% + 150px);
    border-top: 50px solid var(--recent-post-bgcolor);
    border-left: 300px solid transparent;
    }
    }
    @keyframes shutter-effect-left {
    0%{
    bottom: calc(100% - 4px);
    border-bottom: 50px solid var(--recent-post-bgcolor);
    border-right: 300px solid transparent;
    }
    50%{
    bottom: calc(100% - 4px);
    border-bottom: 200px solid var(--recent-post-bgcolor);
    border-right: 600px solid transparent;
    }
    100%{
    bottom: calc(100% - 4px);
    border-bottom: 50px solid var(--recent-post-bgcolor);
    border-right: 300px solid transparent;
    }
    }
    @keyframes shutter-effect-content {
    from {
    opacity: 0
    }
    to {
    opacity: 1
    }
    }

使用gulp压缩博客静态资源

点击查看gulp压缩博客静态资源教程
  1. 安装Gulp插件:在博客根目录[Blogroot]打开终端,输入:
    1
    2
    npm install --global gulp-cli #全局安装gulp指令集
    npm install gulp --save #安装gulp插件
  2. 安装各个下属插件以实现对各类静态资源的压缩
  • 压缩HTML:
    1
    2
    3
    npm install gulp-htmlclean --save-dev
    npm install gulp-html-minifier-terser --save-dev
    # 用gulp-html-minifier-terser可以压缩HTML中的ES6语法
  • 压缩CSS:
    1
    npm install gulp-clean-css --save-dev
  • 压缩JS
    1
    npm install gulp-terser --save-dev
  • 压缩字体包
    1
    npm install gulp-fontmin --save-dev
  1. Gulp创建gulpfile.js任务脚本。在博客根目录[Blogroot]下新建gulpfile.js,打开[Blogroot]\gulpfile.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
    //用到的各个插件
    var gulp = require('gulp');
    var cleanCSS = require('gulp-clean-css');
    var htmlmin = require('gulp-html-minifier-terser');
    var htmlclean = require('gulp-htmlclean');
    var fontmin = require('gulp-fontmin');
    // gulp-tester
    var terser = require('gulp-terser');
    // 压缩js
    gulp.task('compress', () =>
    gulp.src(['./public/**/*.js', '!./public/**/*.min.js'])
    .pipe(terser())
    .pipe(gulp.dest('./public'))
    )
    //压缩css
    gulp.task('minify-css', () => {
    return gulp.src(['./public/**/*.css'])
    .pipe(cleanCSS({
    compatibility: 'ie11'
    }))
    .pipe(gulp.dest('./public'));
    });
    //压缩html
    gulp.task('minify-html', () => {
    return gulp.src('./public/**/*.html')
    .pipe(htmlclean())
    .pipe(htmlmin({
    removeComments: true, //清除html注释
    collapseWhitespace: true, //压缩html
    collapseBooleanAttributes: true,
    //省略布尔属性的值,例如:<input checked="true"/> ==> <input />
    removeEmptyAttributes: true,
    //删除所有空格作属性值,例如:<input id="" /> ==> <input />
    removeScriptTypeAttributes: true,
    //删除<script>的type="text/javascript"
    removeStyleLinkTypeAttributes: true,
    //删除<style>和<link>的 type="text/css"
    minifyJS: true, //压缩页面 JS
    minifyCSS: true, //压缩页面 CSS
    minifyURLs: true //压缩页面URL
    }))
    .pipe(gulp.dest('./public'))
    });
    //压缩字体
    function minifyFont(text, cb) {
    gulp
    .src('./public/fonts/*.ttf') //原字体所在目录
    .pipe(fontmin({
    text: text
    }))
    .pipe(gulp.dest('./public/fontsdest/')) //压缩后的输出目录
    .on('end', cb);
    }

    gulp.task('mini-font', (cb) => {
    var buffers = [];
    gulp
    .src(['./public/**/*.html']) //HTML文件所在目录请根据自身情况修改
    .on('data', function(file) {
    buffers.push(file.contents);
    })
    .on('end', function() {
    var text = Buffer.concat(buffers).toString('utf-8');
    minifyFont(text, cb);
    });
    });
    // 运行gulp命令时依次执行以下任务
    gulp.task('default', gulp.parallel(
    'compress', 'minify-css', 'minify-html','mini-font'
    ))
  2. 在每次运行完hexo generate生成静态页面后,运行gulp对其进行压缩。

  3. 关于font-min的补充说明,在本文中,是通过读取所有编译好的html文件(./public/**/*.html)中的字符,然后匹配原有字体包内./public/fonts/*.ttf字体样式,输出压缩后的字体包到./public/fontsdest/目录。所以最终引用字体的相对路径应该是/fontsdest/*.ttf。而本地测试时,如果没有运行gulp,自然也就不会输出压缩字体包到public目录,也就看不到字体样式。

渐变星空Sky粒子背景特效

点击查看渐变星空Sky粒子背景特效教程

信笺样式留言板

点击查看信笺样式留言板教程
  1. [Blogroot]运行指令
1
npm install hexo-butterfly-envelope --save
  1. 在站点配置文件或者主题配置文件添加配置项(对,两者任一均可。但不要都写)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# envelope_comment
# see https://akilar.top/posts/e2d3c450/
envelope_comment:
enable: true #控制开关
custom_pic:
cover: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg #信笺头部图片
line: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png #信笺底部图片
beforeimg: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png # 信封前半部分
afterimg: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png # 信封后半部分
message: #信笺正文,多行文本,写法如下
- 有什么想问的?
- 有什么想说的?
- 有什么想吐槽的?
- 哪怕是有什么想吃的,都可以告诉我哦~
bottom: 自动书记人偶竭诚为您服务! #仅支持单行文本
height: #1050px,信封划出的高度
path: #【可选】comments 的路径名称。默认为 comments,生成的页面为 comments/index.html
front_matter: #【可选】comments页面的 front_matter 配置
title: 留言板
comments: true