实践出真知

  • 边看边调,记录点有意思的东西

学习hexo设计

hexo本体

文档 | Hexo
多看文档总不会错,把自定义下面的内容边看边改就差不多了

学习主题设计

butterfly主题

Butterfly - A Simple and Card UI Design theme for Hexo
简单好使

Next主题

Documentation | NexT (theme-next.js.org)
next是老牌主题,功能全,但是美观度得靠自己魔改
只是纯英文doc有点难受

学习主题魔改

Akilarの糖果屋 - Akilar.top
Fomalhaut🥝
还得是前人的智慧有用,只是版本有点旧,得自己重复一遍,抄不了作业

魔改记录

记录一下自己干了什么,方便后续维护

留言板

  1. 由插件提供服务
    1
    npm install hexo-butterfly-envelope --save
  2. 在_config.yml内配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # 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

网站一图流

其实通过设置transparent就能实现,但是魔改可以有更多选择且减少数据加载

  1. [BlogRoot]\source文件夹下新建一个文件夹css,该文件夹用于存放自定义的css样式,再新建一个名为custom.css,在里面写入以下代码:

    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;
    }
    #page-header {
    background: transparent !important;
    }

    /* 白天模式遮罩透明 */
    #footer::before {
    background: transparent !important;
    }
    #page-header::before {
    background: transparent !important;
    }

    /* 夜间模式遮罩透明 */
    [data-theme="dark"] #footer::before {
    background: transparent !important;
    }
    [data-theme="dark"] #page-header::before {
    background: transparent !important;
    }
  2. 在主题配置文件[BlogRoot]\_config.butterfly.yml文件中的inject配置项的head子项加入以下代码,代表引入刚刚创建的custom.css文件

    1
    2
    3
    inject:  
    head:
    - <link rel="stylesheet" href="/css/custom.css" media="defer" onload="this.media='all'">
  3. 在主题配置文件[BlogRoot]\_config.butterfly.yml文件中配置加载项,减少冗余

    1
    2
    3
    4
    5
    # The banner image of home page  
    index_img:
    # Footer Background
    footer_bg: false
    background: url(<背景地址>) # 只加载背景

页脚github徽标

  1. 插件提供服务
    1
    npm install hexo-butterfly-footer-beautify --save
  2. 在站点配置文件_config.yml中配置
    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
    # footer_beautify  
    # 页脚计时器:[Native JS Timer](https://akilar.top/posts/b941af/)
    # 页脚徽标:[Add Github Badge](https://akilar.top/posts/e87ad7f8/)
    footer_beautify:
    enable:
    timer: false # 计时器开关 ——本人不想打开,没什么用
    bdage: true # 徽标开关
    priority: 5 #过滤器优先权
    enable_page: all # 应用页面
    exclude: #屏蔽页面
    # - /posts/
    # - /about/
    layout: # 挂载容器类型
    type: id
    name: footer-wrap
    index: 0
    # 计时器部分配置项
    runtime_js: https://npm.elemecdn.com/[email protected]/lib/runtime.js
    runtime_css: https://npm.elemecdn.com/[email protected]/lib/runtime.css
    # 徽标部分配置项
    swiperpara: 0 #若非0,则开启轮播功能,每行徽标个数
    bdageitem:
    - link: https://hexo.io/ #徽标指向网站链接
    shields: https://img.shields.io/badge/Frame-Hexo-blue?style=flat&logo=hexo #徽标API
    message: 博客框架为Hexo_v6.2.0 #徽标提示语
    - link: https://butterfly.js.org/
    shields: https://img.shields.io/badge/Theme-Butterfly-6513df?style=flat&logo=bitdefender
    message: 主题版本Butterfly_v4.3.1
    - link: http://creativecommons.org/licenses/by-nc-sa/4.0/
    shields: https://img.shields.io/badge/Copyright-BY--NC--SA%204.0-d42328?style=flat&logo=Claris
    message: 本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可
    # 自由添加
    swiper_css: https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiper.min.css
    swiper_js: https://npm.elemecdn.com/hexo-butterfly-swiper/lib/swiper.min.js
    swiperbdage_init_js: https://npm.elemecdn.com/hexo-butterfly-footer-beautify/lib/swiperbdage_init.min.js
  3. shield获取
    前往Shields.io | Shields.io设计获取即可
    也可以直接修改url,模板如下
    1
    2
    https://img.shields.io/badge/<前半字符>-<后半字符>-<颜色>?style=flat&logo=<名称>
    可能找不到logo,所以还是到网站去预览一下比较好,style也可以去预览看看

首页文章置顶滚动栏

  1. 插件提供服务
    1
    npm install hexo-butterfly-swiper --save
  2. 在站点配置文件_config.yml中配置
    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. 在文章的front_matter中添加swiper_index配置项
    1
    2
    3
    4
    5
    ---  
    cover: 文章封面
    description: 文章描述
    swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前
    ---

霓虹灯

在自定义的custom.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
    /* 日间模式不生效 */  
    [data-theme="light"] #site-name,
    [data-theme="light"] #site-title,
    [data-theme="light"] #site-subtitle,
    [data-theme="light"] #post-info {
    animation: none;
    }
    /* 夜间模式生效 */
    [data-theme="dark"] #site-name,
    [data-theme="dark"] #site-title {
    animation: light_15px 10s linear infinite;
    }
    [data-theme="dark"] #site-subtitle {
    animation: light_10px 10s linear infinite;
    }
    [data-theme="dark"] #post-info {
    animation: light_5px 10s linear infinite;
    }
    /* 关键帧描述 */
    @keyframes light_15px {
    0% {
    text-shadow: #5636ed 0 0 15px;
    }
    12.5% {
    text-shadow: #11ee5e 0 0 15px;
    }
    25% {
    text-shadow: #f14747 0 0 15px;
    }
    37.5% {
    text-shadow: #f1a247 0 0 15px;
    }
    50% {
    text-shadow: #f1ee47 0 0 15px;
    }
    50% {
    text-shadow: #b347f1 0 0 15px;
    }
    62.5% {
    text-shadow: #002afa 0 0 15px;
    }
    75% {
    text-shadow: #ed709b 0 0 15px;
    }
    87.5% {
    text-shadow: #39c5bb 0 0 15px;
    }
    100% {
    text-shadow: #5636ed 0 0 15px;
    }
    }

    @keyframes light_10px {
    0% {
    text-shadow: #5636ed 0 0 10px;
    }
    12.5% {
    text-shadow: #11ee5e 0 0 10px;
    }
    25% {
    text-shadow: #f14747 0 0 10px;
    }
    37.5% {
    text-shadow: #f1a247 0 0 10px;
    }
    50% {
    text-shadow: #f1ee47 0 0 10px;
    }
    50% {
    text-shadow: #b347f1 0 0 10px;
    }
    62.5% {
    text-shadow: #002afa 0 0 10px;
    }
    75% {
    text-shadow: #ed709b 0 0 10px;
    }
    87.5% {
    text-shadow: #39c5bb 0 0 10px;
    }
    100% {
    text-shadow: #5636ed 0 0 10px;
    }
    }

    @keyframes light_5px {
    0% {
    text-shadow: #5636ed 0 0 5px;
    }
    12.5% {
    text-shadow: #11ee5e 0 0 5px;
    }
    25% {
    text-shadow: #f14747 0 0 5px;
    }
    37.5% {
    text-shadow: #f1a247 0 0 15px;
    }
    50% {
    text-shadow: #f1ee47 0 0 5px;
    }
    50% {
    text-shadow: #b347f1 0 0 5px;
    }
    62.5% {
    text-shadow: #002afa 0 0 5px;
    }
    75% {
    text-shadow: #ed709b 0 0 5px;
    }
    87.5% {
    text-shadow: #39c5bb 0 0 5px;
    }
    100% {
    text-shadow: #5636ed 0 0 5px;
    }
    }

星空背景和流星特效

  1. [BlogRoot]/source/js目录下新建universe.js,输入以下代码:
    1
    2
    function dark() {window.requestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame;var n,e,i,h,t=.05,s=document.getElementById("universe"),o=!0,a="180,184,240",r="226,225,142",d="226,225,224",c=[];function f(){n=window.innerWidth,e=window.innerHeight,i=.216*n,s.setAttribute("width",n),s.setAttribute("height",e)}function u(){h.clearRect(0,0,n,e);for(var t=c.length,i=0;i<t;i++){var s=c[i];s.move(),s.fadeIn(),s.fadeOut(),s.draw()}}function y(){this.reset=function(){this.giant=m(3),this.comet=!this.giant&&!o&&m(10),this.x=l(0,n-10),this.y=l(0,e),this.r=l(1.1,2.6),this.dx=l(t,6*t)+(this.comet+1-1)*t*l(50,120)+2*t,this.dy=-l(t,6*t)-(this.comet+1-1)*t*l(50,120),this.fadingOut=null,this.fadingIn=!0,this.opacity=0,this.opacityTresh=l(.2,1-.4*(this.comet+1-1)),this.do=l(5e-4,.002)+.001*(this.comet+1-1)},this.fadeIn=function(){this.fadingIn&&(this.fadingIn=!(this.opacity>this.opacityTresh),this.opacity+=this.do)},this.fadeOut=function(){this.fadingOut&&(this.fadingOut=!(this.opacity<0),this.opacity-=this.do/2,(this.x>n||this.y<0)&&(this.fadingOut=!1,this.reset()))},this.draw=function(){if(h.beginPath(),this.giant)h.fillStyle="rgba("+a+","+this.opacity+")",h.arc(this.x,this.y,2,0,2*Math.PI,!1);else if(this.comet){h.fillStyle="rgba("+d+","+this.opacity+")",h.arc(this.x,this.y,1.5,0,2*Math.PI,!1);for(var t=0;t<30;t++)h.fillStyle="rgba("+d+","+(this.opacity-this.opacity/20*t)+")",h.rect(this.x-this.dx/4*t,this.y-this.dy/4*t-2,2,2),h.fill()}else h.fillStyle="rgba("+r+","+this.opacity+")",h.rect(this.x,this.y,this.r,this.r);h.closePath(),h.fill()},this.move=function(){this.x+=this.dx,this.y+=this.dy,!1===this.fadingOut&&this.reset(),(this.x>n-n/4||this.y<0)&&(this.fadingOut=!0)},setTimeout(function(){o=!1},50)}function m(t){return Math.floor(1e3*Math.random())+1<10*t}function l(t,i){return Math.random()*(i-t)+t}f(),window.addEventListener("resize",f,!1),function(){h=s.getContext("2d");for(var t=0;t<i;t++)c[t]=new y,c[t].reset();u()}(),function t(){document.getElementsByTagName('html')[0].getAttribute('data-theme')=='dark'&&u(),window.requestAnimationFrame(t)}()};  
    dark()

这里可以进行二次魔改,在函数调用上有一个检测机制(最后面几行)是

1
function t(){document.getElementsByTagName('html')[0].getAttribute('data-theme')=='dark'&&u(),window.requestAnimationFrame(t)}()}

我们可以删除掉对data-theme的检测来实现长期有效,这样和背景图的配合更好

  1. [BlogRoot]/source/css目录下新建universe.css,输入以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /* 背景宇宙星光  */  
    #universe{
    display: block;
    position: fixed;
    margin: 0;
    padding: 0;
    border: 0;
    outline: 0;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    /* 这个是调置顶的优先级的,-1在文章页下面,背景上面,个人推荐这种 */
    z-index: -1;
    }
  2. 在主题配置文件_config.butterfly.yml中配置注入项inject

    1
    2
    3
    4
    5
    6
    inject:  
    head:
    - <link rel="stylesheet" href="/css/universe.css">
    bottom:
    - <canvas id="universe"></canvas>
    - <script defer src="/js/universe.js"></script>

页面样式调整

通过css样式调节各个页面透明度、模糊度(亚克力效果)、圆角、边框样式等

  1. 向自定义的custom.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
    :root {  
    --trans-light: rgba(255, 255, 255, 0.68);
    --trans-dark: rgba(25, 25, 25, 0.68);
    --border-style: 1px solid rgb(169, 169, 169);
    --backdrop-filter: blur(5px) saturate(150%);
    }
    /*
    - `--trans-light`:白天模式带透明度的背景色,如`rgba(255, 255, 255, 0.88)`底色是纯白色,其中0.88就透明度,在0-1之间调节,值越大越不透明;

    - `--trans-dark`: 夜间模式带透明度的背景色,如`rgba(25, 25, 25, 0.88)`底色是柔和黑色,其中0.88就透明度,在0-1之间调节,值越大越不透明;

    - `--border-style`: 边框样式,`1px solid rgb(169, 169, 169)`指宽度为1px的灰色实体边框;

    - `--backdrop-filter`: 背景过滤器,如`blur(5px) saturate(150%)`表示饱和度为150%的、高斯模糊半径为5px的过滤器,这是亚克力效果的一种实现方法;
    */

    /* 首页文章卡片 */
    #recent-posts > .recent-post-item {
    background: var(--trans-light);
    backdrop-filter: var(--backdrop-filter);
    border-radius: 25px;
    border: var(--border-style);
    }

    /* 首页侧栏卡片 */
    #aside-content .card-widget {
    background: var(--trans-light);
    backdrop-filter: var(--backdrop-filter);
    border-radius: 18px;
    border: var(--border-style);
    }

    /* 文章页、归档页、普通页面 */
    div#post,
    div#page,
    div#archive {
    background: var(--trans-light);
    backdrop-filter: var(--backdrop-filter);
    border: var(--border-style);
    border-radius: 20px;
    }

    /* 导航栏 */
    #page-header.nav-fixed #nav {
    background: rgba(255, 255, 255, 0.75);
    backdrop-filter: var(--backdrop-filter);
    }

    [data-theme="dark"] #page-header.nav-fixed #nav {
    background: rgba(0, 0, 0, 0.7) !important;
    }

    /* 夜间模式遮罩 */
    [data-theme="dark"] #recent-posts > .recent-post-item,
    [data-theme="dark"] #aside-content .card-widget,
    [data-theme="dark"] div#post,
    [data-theme="dark"] div#archive,
    [data-theme="dark"] div#page {
    background: var(--trans-dark);
    }


    /* 夜间模式页脚页头遮罩透明 */
    [data-theme="dark"] #footer::before {
    background: transparent !important;
    }
    [data-theme="dark"] #page-header::before {
    background: transparent !important;
    }

    /* 阅读模式 */
    .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;
    }

    /* 夜间模式下的阅读模式 */
    [data-theme="dark"] .read-mode #aside-content .card-widget {
    background: rgba(25, 25, 25, 0.9) !important;
    color: #ffffff;
    }
    [data-theme="dark"] .read-mode div#post {
    background: rgba(25, 25, 25, 0.9) !important;
    color: #ffffff;
    }

版权美化

  1. 修改\butterfly\layout\includes\post\post-copyright.pug,直接复制以下内容替换原文件内容。此处多次用到了三元运算符作为默认项设置,在确保有主题配置文件的默认项的情况下,也可以在相应文章的front-matter中重新定义作者,原文链接,开源许可协议等内容。

    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
    if theme.post_copyright.enable && page.copyright !== false  
    - let author = page.copyright_author ? page.copyright_author : config.author
    - let url = page.copyright_url ? page.copyright_url : page.permalink
    - let license = page.license ? page.license : theme.post_copyright.license
    - let license_url = page.license_url ? page.license_url : theme.post_copyright.license_url
    .post-copyright
    .post-copyright__title
    span.post-copyright-info
    h #[=page.title]
    .post-copyright__type
    span.post-copyright-info
    a(href=url_for(url))= theme.post_copyright.decode ? decodeURI(url) : url
    .post-copyright-m
    .post-copyright-m-info
    .post-copyright-a
    h 作者
    .post-copyright-cc-info
    h=author
    .post-copyright-c
    h 发布于
    .post-copyright-cc-info
    h=date(page.date, config.date_format)
    .post-copyright-u
    h 更新于
    .post-copyright-cc-info
    h=date(page.updated, config.date_format)
    .post-copyright-c
    h 许可协议
    .post-copyright-cc-info
    a.icon(rel='noopener' target='_blank' title='Creative Commons' href='https://creativecommons.org/')
    i.fab.fa-creative-commons
    a(rel='noopener' target='_blank' title=license href=url_for(license_url))=license
  2. 修改\butterfly\source\css\_layout\post.styl,直接复制以下内容,替换原文件,这个文件就是自己调节样式的。其中,184行是白天模式的背景色,这里默认是我网站的渐变色,大家可以根据自己的喜好调节;253行是夜间模式的发光光圈颜色

    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
    beautify()  
    headStyle(fontsize)
    padding-left: unit(fontsize + 12, 'px')

    &:before
    margin-left: unit((-(fontsize + 6)), 'px')
    font-size: unit(fontsize, 'px')

    &:hover
    padding-left: unit(fontsize + 18, 'px')

    h1,
    h2,
    h3,
    h4,
    h5,
    h6
    transition: all .2s ease-out

    &:before
    position: absolute
    top: calc(50% - 7px)
    color: $title-prefix-icon-color
    content: $title-prefix-icon
    line-height: 1
    transition: all .2s ease-out
    @extend .fontawesomeIcon

    &:hover
    &:before
    color: $light-blue

    h1
    headStyle(20)

    h2
    headStyle(18)

    h3
    headStyle(16)

    h4
    headStyle(14)

    h5
    headStyle(12)

    h6
    headStyle(12)

    ol,
    ul
    p
    margin: 0 0 8px

    li
    &::marker
    color: $light-blue
    font-weight: 600
    font-size: 1.05em

    &:hover
    &::marker
    color: var(--pseudo-hover)

    ul > li
    list-style-type: circle

    #article-container
    word-wrap: break-word
    overflow-wrap: break-word

    a
    color: $theme-link-color

    &:hover
    text-decoration: underline

    img
    display: block
    margin: 0 auto 20px
    max-width: 100%
    transition: filter 375ms ease-in .2s

    p
    margin: 0 0 16px

    iframe
    margin: 0 0 20px

    if hexo-config('anchor')
    a.headerlink
    &:after
    @extend .fontawesomeIcon
    float: right
    color: var(--headline-presudo)
    content: '\f0c1'
    font-size: .95em
    opacity: 0
    transition: all .3s

    &:hover
    &:after
    color: var(--pseudo-hover)

    h1,
    h2,
    h3,
    h4,
    h5,
    h6
    &:hover
    a.headerlink
    &:after
    opacity: 1

    ol,
    ul
    ol,
    ul
    padding-left: 20px

    li
    margin: 4px 0

    p
    margin: 0 0 8px

    if hexo-config('beautify.enable')
    if hexo-config('beautify.field') == 'site'
    beautify()
    else if hexo-config('beautify.field') == 'post'
    &.post-content
    beautify()

    > :last-child
    margin-bottom: 0 !important

    #post
    .tag_share
    .post-meta
    &__tag-list
    display: inline-block

    &__tags
    display: inline-block
    margin: 8px 8px 8px 0
    padding: 0 12px
    width: fit-content
    border: 1px solid $light-blue
    border-radius: 12px
    color: $light-blue
    font-size: .85em
    transition: all .2s ease-in-out

    &:hover
    background: $light-blue
    color: var(--white)

    .post_share
    display: inline-block
    float: right
    margin: 8px 0
    width: fit-content

    .social-share
    font-size: .85em

    .social-share-icon
    margin: 0 4px
    width: w = 1.85em
    height: w
    font-size: 1.2em
    line-height: w

    .post-copyright
    position: relative
    margin: 40px 0 10px
    padding: 10px 16px
    border: 1px solid var(--light-grey)
    transition: box-shadow .3s ease-in-out
    overflow: hidden
    border-radius: 12px!important
    background: linear-gradient(45deg, #f6d8f5, #c2f1f0, #f0debf);

    &:before
    background var(--heo-post-blockquote-bg)
    position absolute
    right -26px
    top -120px
    content '\f25e'
    font-size 200px
    font-family 'Font Awesome 5 Brands'
    opacity .2

    &:hover
    box-shadow: 0 0 8px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .5)

    .post-copyright
    &-meta
    color: $light-blue
    font-weight: bold

    &-info
    padding-left: 6px

    a
    text-decoration: none
    word-break: break-word

    &:hover
    text-decoration: none

    .post-copyright-cc-info
    color: $theme-color;

    .post-outdate-notice
    position: relative
    margin: 0 0 20px
    padding: .5em 1.2em
    border-radius: 3px
    background-color: $noticeOutdate-bg
    color: $noticeOutdate-color

    if hexo-config('noticeOutdate.style') == 'flat'
    padding: .5em 1em .5em 2.6em
    border-left: 5px solid $noticeOutdate-border

    &:before
    @extend .fontawesomeIcon
    position: absolute
    top: 50%
    left: .9em
    color: $noticeOutdate-border
    content: '\f071'
    transform: translateY(-50%)

    .ads-wrap
    margin: 40px 0
    .post-copyright-m-info
    .post-copyright-a,
    .post-copyright-c,
    .post-copyright-u
    display inline-block
    width fit-content
    padding 2px 5px
    [data-theme="dark"]
    #post
    .post-copyright
    background #07080a
    text-shadow #bfbeb8 0 0 2px
    border 1px solid rgb(19 18 18 / 35%)
    box-shadow 0 0 5px var(--theme-color)
    animation flashlight 1s linear infinite alternate
    .post-copyright-info
    color #e0e0e4

    #post
    .post-copyright__title
    font-size 22px
    .post-copyright__notice
    font-size 15px
    .post-copyright
    box-shadow 2px 2px 5px

音乐播放器aplayer

这里虽然有插件,但是插件太久没更新,兼容性很差,不方便后续调整

  1. 指定js和css地址
    1
    2
    3
    4
    5
    6
    # 在_config.butterfly.yml的option配置
    option:
    aplayer_js: https://cdn.jsdelivr.net/npm/[email protected]/dist/APlayer.min.js
        artalk_css: https://cdn.bootcss.com/aplayer/1.10.1/APlayer.min.css
        meting_js: https://cdn.jsdelivr.net/npm/[email protected]/dist/Meting.min.js>
    # 虽然有更新版本的meting,但是与aplayer不兼容
  2. 配置aplayer注入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 在_config.butterfly.yml里配置
    aplayerInject:
      enable: true
      per_page: true
    inject:
    bottom:
    - <div class="aplayer no-destroy" data-id="9512979254" data-server="netease" data-type="playlist" data-fixed="true" data-mini="true" data-autoplay="false"> </div>

    # data-id为网易云歌单的id,网页端打开歌单,使用F12搜索data-matcher即可得到
    # data-type等配置详细可见aplayer的docs(年久失修,懒得调了)

    # 在_config.yml里添加
    # aplayer
    # https://butterfly.js.org/posts/507c070f/#%E5%89%8D%E8%A8%80
    aplayer:
      meting: true
      asset_inject: false
  3. pjax配置
    butterfly主题自带pjax,可以提供网页部分刷新的服务,即切换网页不重载播放器
    但是这个功能bug多还不一定生效,后续再填坑了

顶部渐变条色加载条

  1. 新建[BlogRoot]\source\css\progress_bar.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
    .pace {  
    -webkit-pointer-events: none;
    pointer-events: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
    z-index: 2000;
    position: fixed;
    margin: auto;
    top: 4px;
    left: 0;
    right: 0;
    height: 8px;
    border-radius: 8px;
    width: 7rem;
    background: #eaecf2;
    border: 1px #e3e8f7;
    overflow: hidden
    }

    .pace-inactive .pace-progress {
    opacity: 0;
    transition: .3s ease-in
    }

    .pace .pace-progress {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    -o-box-sizing: border-box;
    box-sizing: border-box;
    -webkit-transform: translate3d(0, 0, 0);
    -moz-transform: translate3d(0, 0, 0);
    -ms-transform: translate3d(0, 0, 0);
    -o-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    max-width: 200px;
    position: absolute;
    z-index: 2000;
    display: block;
    top: 0;
    right: 100%;
    height: 100%;
    width: 100%;
    /* linear-gradient(to right, #3494e6, #ec6ead) */
    background: linear-gradient(to right, #43cea2, #3866ca);
    animation: gradient 2s ease infinite;
    background-size: 200%
    }

    .pace.pace-inactive {
    opacity: 0;
    transition: .3s;
    top: -8px
    }
  2. 在主题配置文件_config.butterfly.ymlinject配置项配置注入
    1
    2
    3
    4
    5
    inject:  
    head:
    - <link rel="stylesheet" href="/css/progress_bar.css" media="defer" onload="this.media='all'">
    bottom:
    - <script async src="//npm.elemecdn.com/[email protected]/pace.min.js"></script>

Bilibili嵌入式视频

  1. [BlogRoot]\source\css\custom.css自定义样式的文件中引入如下代码
    可以随意调整参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /*哔哩哔哩视频适配*/  
    .aspect-ratio {
    position: relative;
    width: 90%;
    height: auto;
    padding-bottom: 75%;
    margin: 3% auto;
    text-align: center;
    }
    .aspect-ratio iframe {
    position: absolute;
    width: 100%;
    height: 86%;
    left: 0;
    top: 0;
    }
  2. 使用嵌入模板
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <div align=center class="aspect-ratio">  
    <iframe src="https://player.bilibili.com/player.html?aid=474023258&&page=1&as_wide=1&high_quality=1&danmaku=0"
    scrolling="no"
    border="0"
    frameborder="no"
    framespacing="0"
    high_quality=1
    danmaku=1
    allowfullscreen="true">
    </iframe>
    </div>
  3. 到Bilibili获取嵌入式参数
    现在已登录的b站已经无法获取嵌入式代码了,所以可以开一个无痕窗口来获取
    image.png
    在分享选项中有一个嵌入代码选项,点击就能获取到一条代码
    1
    <iframe src="//player.bilibili.com/player.html?aid=1302115271&bvid=BV1gu4m1T7Uz&cid=1479032623&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
    把中间的链接拿去替换模板的参数即可,或者也可以直接替换整个iframe

挂绳

  1. [BlogRoot]/node_modules/hexo-theme-butterfly/layout/includes/head.pug最后一行加入如下代码:

    1
    #myscoll
  2. [BlogRoot]/node_modules/hexo-theme-butterfly/source/js文件夹下新建一个cat.js,将以下代码复制到文件中
    可以选择在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
    if (document.body.clientWidth > 992) {  
    function getBasicInfo() {
    /* 窗口高度 */
    var ViewH = $(window).height();
    /* document高度 */
    var DocH = $("body")[0].scrollHeight;
    /* 滚动的高度 */
    var ScrollTop = $(window).scrollTop();
    /* 可滚动的高度 */
    var S_V = DocH - ViewH;
    var Band_H = ScrollTop / (DocH - ViewH) * 100;
    return {
    ViewH: ViewH,
    DocH: DocH,
    ScrollTop: ScrollTop,
    Band_H: Band_H,
    S_V: S_V
    }
    };
    function show(basicInfo) {
    if (basicInfo.ScrollTop > 0.001) {
    $(".neko").css('display', 'block');
    } else {
    $(".neko").css('display', 'none');
    }
    }
    (function ($) {
    $.fn.nekoScroll = function (option) {
    var defaultSetting = {
    top: '0',
    scroWidth: 6 + 'px',
    z_index: 9999,
    zoom: 0.9,
    borderRadius: 5 + 'px',
    right: 60 + 'px',
    // 这里可以换为你喜欢的图片,但是要抠图
    nekoImg: "https://bu.dusays.com/2022/07/20/62d812db74be9.png",
    hoverMsg: "喵喵喵~", //鼠标放上去的信息显示
    color: "#6f42c1",
    during: 500,
    blog_body: "body",
    };
    var setting = $.extend(defaultSetting, option);
    var getThis = this.prop("className") !== "" ? "." + this.prop("className") : this.prop("id") !== "" ? "#" +
    this.prop("id") : this.prop("nodeName");
    if ($(".neko").length == 0) {
    this.after("<div class=\"neko\" id=" + setting.nekoname + " data-msg=\"" + setting.hoverMsg + "\"></div>");
    }
    let basicInfo = getBasicInfo();
    $(getThis)
    .css({
    'position': 'fixed',
    'width': setting.scroWidth,
    'top': setting.top,
    'height': basicInfo.Band_H * setting.zoom * basicInfo.ViewH * 0.01 + 'px',
    'z-index': setting.z_index,
    'background-color': setting.bgcolor,
    "border-radius": setting.borderRadius,
    'right': setting.right,
    'background-image': 'url(' + setting.scImg + ')',
    'background-image': '-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.1) 75%, transparent 75%, transparent)', 'border-radius': '2em',
    'background-size': 'contain'
    });
    $("#" + setting.nekoname)
    .css({
    'position': 'fixed',
    'top': basicInfo.Band_H * setting.zoom * basicInfo.ViewH * 0.01 - 50 + 'px',
    'z-index': setting.z_index * 10,
    'right': setting.right,
    'background-image': 'url(' + setting.nekoImg + ')',
    });
    show(getBasicInfo());
    $(window)
    .scroll(function () {
    let basicInfo = getBasicInfo();
    show(basicInfo);
    $(getThis)
    .css({
    'position': 'fixed',
    'width': setting.scroWidth,
    'top': setting.top,
    'height': basicInfo.Band_H * setting.zoom * basicInfo.ViewH * 0.01 + 'px',
    'z-index': setting.z_index,
    'background-color': setting.bgcolor,
    "border-radius": setting.borderRadius,
    'right': setting.right,
    'background-image': 'url(' + setting.scImg + ')',
    'background-image': '-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.1) 75%, transparent 75%, transparent)', 'border-radius': '2em',
    'background-size': 'contain'
    });
    $("#" + setting.nekoname)
    .css({
    'position': 'fixed',
    'top': basicInfo.Band_H * setting.zoom * basicInfo.ViewH * 0.01 - 50 + 'px',
    'z-index': setting.z_index * 10,
    'right': setting.right,
    'background-image': 'url(' + setting.nekoImg + ')',
    });
    if (basicInfo.ScrollTop == basicInfo.S_V) {
    $("#" + setting.nekoname)
    .addClass("showMsg")
    } else {
    $("#" + setting.nekoname)
    .removeClass("showMsg");
    $("#" + setting.nekoname)
    .attr("data-msg", setting.hoverMsg);
    }
    });
    this.click(function (e) {
    btf.scrollToDest(0, 500)
    });
    $("#" + setting.nekoname)
    .click(function () {
    btf.scrollToDest(0, 500)
    });
    return this;
    }
    })(jQuery);

    $(document).ready(function () {
    //部分自定义
    $("#myscoll").nekoScroll({
    bgcolor: 'rgb(0 0 0 / .5)', //背景颜色,没有绳子背景图片时有效
    borderRadius: '2em',
    zoom: 0.9
    }
    );
    //自定义(去掉以下注释,并注释掉其他的查看效果)
    /*
    $("#myscoll").nekoScroll({
    nekoname:'neko1', //nekoname,相当于id
    nekoImg:'img/猫咪.png', //neko的背景图片
    scImg:"img/绳1.png", //绳子的背景图片
    bgcolor:'#1e90ff', //背景颜色,没有绳子背景图片时有效
    zoom:0.9, //绳子长度的缩放值
    hoverMsg:'你好~喵', //鼠标浮动到neko上方的对话框信息
    right:'100px', //距离页面右边的距离
    fontFamily:'楷体', //对话框字体
    fontSize:'14px', //对话框字体的大小
    color:'#1e90ff', //对话框字体颜色
    scroWidth:'8px', //绳子的宽度
    z_index:100, //在页面上的层数
    during:1200, //从顶部到底部滑动的时长
    });
    */
    })
    }
  3. [BlogRoot]/node_modules/hexo-theme-butterfly/source/css文件夹下新建一个cat.css,将以下代码复制到文件中
    本人直接修改了源码,可以在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
      
    body::-webkit-scrollbar {
    width: 0;
    }

    .neko {
    width: 64px;
    height: 64px;
    background-image: url("https://bu.dusays.com/2022/07/20/62d812db74be9.png"); //猫猫图
    position: absolute;
    right: 32px;
    background-repeat: no-repeat;
    background-size: contain;
    transform: translateX(50%);
    cursor: pointer;
    font-family: tzy;
    font-weight: 600;
    font-size: 16px;
    color: #6f42c1;
    display: none;
    }

    .neko::after {
    display: none;
    width: 100px;
    height: 100px;
    background-image: url("https://bu.dusays.com/2022/07/20/62d812d95e6f5.png"); //触碰后显示
    background-size: contain;
    z-index: 9999;
    position: absolute;
    right: 50%;
    text-align: center;
    line-height: 100px;
    top: -115%;

    }

    .neko.showMsg::after {
    content: attr(data-msg);
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    }

    .neko:hover::after {
    content: attr(data-msg);
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    }

    .neko.fontColor::after {
    color: #333;
    }

    /**
    * @description: 滚动条样式 跟猫二选一
    */
    @media screen and (max-width:992px) {
    ::-webkit-scrollbar {
    width: 8px !important;
    height: 8px !important
    }

    ::-webkit-scrollbar-track {
    border-radius: 2em;
    }

    ::-webkit-scrollbar-thumb {
    background-color: rgb(255 255 255 / .3);
    background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.1) 75%, transparent 75%, transparent);
    border-radius: 2em
    }

    ::-webkit-scrollbar-corner {
    background-color: transparent
    }
    }
  4. 在主题配置文件_config.butterfly.yml中引入cat.jscat.css,当然还有在bottom的最前面引入jQuery,因为cat.js的语法依赖jQuery

    1
    2
    3
    4
    5
    6
    inject:  
    head:
    - <link rel="stylesheet" href="/css/cat.css">
    bottom:
    - <script defer src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script>
    - <script defer data-pjax src="/js/cat.js"></script>

右边按钮阅读进度

  1. 修改文件[BlogRoot]\themes\butterfly\layout\includes\rightside.pug,在最下面插入如下两行代码

    1
    2
    3
    4
    button#go-up(type="button" title=_p("rightside.back_to_top"))  
    i.fas.fa-arrow-up
    + span#percent 0
    + span %
  2. 新建文件[BlogRoot]\source\js\readPercent.js,在自定义js文件中加入如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    window.onscroll = percent;// 执行函数  
    // 页面百分比
    function percent() {
    let a = document.documentElement.scrollTop || window.pageYOffset, // 卷去高度
    b = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight) - document.documentElement.clientHeight, // 整个网页高度
    result = Math.round(a / b * 100), // 计算百分比
    up = document.querySelector("#go-up") // 获取按钮

    if (result <= 95) {
    up.childNodes[0].style.display = 'none'
    up.childNodes[1].style.display = 'block'
    up.childNodes[1].innerHTML = result;
    } else {
    up.childNodes[1].style.display = 'none'
    up.childNodes[0].style.display = 'block'
    }
    }
  3. 创建css文件[BlogRoot]\source\css\readPercent.css写入如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /* 返回顶部 */  

    button#go-up #percent {
    display: none;
    font-weight: bold;
    font-size: 15px !important;
    }

    button#go-up span {
    font-size: 12px!important;
    margin-right: -1px;
    }

    /* 鼠标滑动到按钮上时显示返回顶部图标 */
    button#go-up:hover i {
    display: block !important;
    }

    button#go-up:hover #percent {
    display: none !important;
    }
  4. 引入js文件与css文件

    1
    2
    3
    4
    5
    inject:  
    head:
    - <link rel="stylesheet" href="/css/readPercent.css">
    bottom:
    - <script defer data-pjax src="/js/readPercent.js"></script>

引入vue和Element

在主题配置文件[BlogRoot]\_config.butterfly.yml中 引入VueElement相关依赖:
为后续部分操作提供依赖

1
2
3
4
5
6
inject:  
head:
- <link rel="stylesheet" href="https://cdn1.tianli0.top/npm/[email protected]/packages/theme-chalk/lib/index.css"> # 引入组件库(f12)
bottom:
- <script async src="https://cdn1.tianli0.top/npm/[email protected]/dist/vue.min.js"></script> # 引入VUE(f12)
- <script async src="https://cdn1.tianli0.top/npm/[email protected]/lib/index.js"></script> # 引入ElementUI(f12)

直达底部按钮

  1. \butterfly\layout\includes\rightside.pug做以下修改:
    1
    2
    3
    4
    5
    button#go-up(type="button" title=_p("rightside.back_to_top"))  
    i.fas.fa-arrow-up

    +button#go-down(type="button" title="直达底部" onclick="btf.scrollToDest(document.body.scrollHeight, 500)")
    + i.fas.fa-arrow-down

统计图表页

还可以把统计图表部署到各个页面,见Hexo 博客文章统计图 | Eurkon

  1. 新建 charts 页面

    1
    hexo new page charts
  2. 引入 ECharts.js
    在 [Blogroot]\_config.butterfly.yml 的 inject 配置项中引入 echart.js 文件(全局引入)

    1
    2
    3
    inject:  
    head:
    - <script src="https://npm.elemecdn.com/[email protected]/dist/echarts.min.js"></script>
  3. 在 \butterfly\scripts\helpers\ 目录下新建 charts.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
    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
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    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(/&amp;#/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>`
    }
  4. 在新建的 [Blogroot]\source\charts\index.md 文件中添加以下内容:
    可以自行调整参数

  • posts-chart 的 data-start="2021-01" 属性表示文章发布时间统计图仅显示 2021-01 及以后的文章数据
  • tags-chart 的 data-length="10" 属性表示仅显示排名前 10 的标签
  • categories-chart 的 data-parent="true" 属性表示 有子分类 时以旭日图显示分类,其他 无子分类 或 设置为false 或 不设置该属性 或 设置为其他非true属性 情况都以饼状图显示分类
    1
    2
    3
    4
    5
    6
    <!-- 文章发布时间统计图 -->  
    <div id="posts-chart" data-start="2021-01" style="border-radius: 8px; height: 300px; padding: 10px;"></div>
    <!-- 文章标签统计图 -->
    <div id="tags-chart" data-length="10" style="border-radius: 8px; height: 300px; padding: 10px;"></div>
    <!-- 文章分类统计图 -->
    <div id="categories-chart" data-parent="true" style="border-radius: 8px; height: 300px; padding: 10px;"></div>
  1. 可选适配明暗模式
  2. 可能遇到的问题见顶部链接内文章

文章加密

  1. 插件提供服务

    1
    npm install --save hexo-blog-encrypt
  2. 配置front-matter

    1
    2
    3
    4
    5
    6
    7
    8
    ---  
    password: password
    abstract: 有东西被加密了, 请输入密码查看.
    message: <提示信息>
    theme: xray
    wrong_pass_message: 抱歉, 这个密码看着不太对, 请再试试.
    wrong_hash_message: 抱歉, 这个文章不能被校验, 不过您还是能看看解密后的内容.
    ---

    theme预览:Theme Test - Default · Hexo (mhexo.github.io)

明暗模式切换动画

  1. 新建[BlogRoot]\themes\butterfly\layout\includes\custom\sun_moon.pug,这部分其实实质上就是一个svg文件,通过js操作它的旋转显隐,淡入淡出实现动画效果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    svg(aria-hidden='true', style='position:absolute; overflow:hidden; width:0; height:0')  
    symbol#icon-sun(viewBox='0 0 1024 1024')
    path(d='M960 512l-128 128v192h-192l-128 128-128-128H192v-192l-128-128 128-128V192h192l128-128 128 128h192v192z', fill='#FFD878', p-id='8420')
    path(d='M736 512a224 224 0 1 0-448 0 224 224 0 1 0 448 0z', fill='#FFE4A9', p-id='8421')
    path(d='M512 109.248L626.752 224H800v173.248L914.752 512 800 626.752V800h-173.248L512 914.752 397.248 800H224v-173.248L109.248 512 224 397.248V224h173.248L512 109.248M512 64l-128 128H192v192l-128 128 128 128v192h192l128 128 128-128h192v-192l128-128-128-128V192h-192l-128-128z', fill='#4D5152', p-id='8422')
    path(d='M512 320c105.888 0 192 86.112 192 192s-86.112 192-192 192-192-86.112-192-192 86.112-192 192-192m0-32a224 224 0 1 0 0 448 224 224 0 0 0 0-448z', fill='#4D5152', p-id='8423')
    symbol#icon-moon(viewBox='0 0 1024 1024')
    path(d='M611.370667 167.082667a445.013333 445.013333 0 0 1-38.4 161.834666 477.824 477.824 0 0 1-244.736 244.394667 445.141333 445.141333 0 0 1-161.109334 38.058667 85.077333 85.077333 0 0 0-65.066666 135.722666A462.08 462.08 0 1 0 747.093333 102.058667a85.077333 85.077333 0 0 0-135.722666 65.024z', fill='#FFB531', p-id='11345')
    path(d='M329.728 274.133333l35.157333-35.157333a21.333333 21.333333 0 1 0-30.165333-30.165333l-35.157333 35.157333-35.114667-35.157333a21.333333 21.333333 0 0 0-30.165333 30.165333l35.114666 35.157333-35.114666 35.157334a21.333333 21.333333 0 1 0 30.165333 30.165333l35.114667-35.157333 35.157333 35.157333a21.333333 21.333333 0 1 0 30.165333-30.165333z', fill='#030835', p-id='11346')
  2. 新建[BlogRoot]\themes\butterfly\source\css\_layout\sun_moon.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
    .Cuteen_DarkSky,  
    .Cuteen_DarkSky:before
    content ''
    position fixed
    left 0
    right 0
    top 0
    bottom 0
    z-index 88888888

    .Cuteen_DarkSky
    background linear-gradient(to top, #f8cd71 0, #5bfde9 80%)
    &:before
    transition 2s ease all
    opacity 0
    background linear-gradient(to top, #30cfd0 0, #330867 100%)

    .DarkMode
    .Cuteen_DarkSky
    &:before
    opacity 1

    .Cuteen_DarkPlanet
    z-index 99999999
    position fixed
    left -50%
    top -50%
    width 200%
    height 200%
    -webkit-animation CuteenPlanetMove 2s cubic-bezier(0.7, 0, 0, 1)
    animation CuteenPlanetMove 2s cubic-bezier(0.7, 0, 0, 1)
    transform-origin center bottom


    @-webkit-keyframes CuteenPlanetMove {
    0% {
    transform: rotate(0);
    }
    to {
    transform: rotate(360deg);
    }
    }
    @keyframes CuteenPlanetMove {
    0% {
    transform: rotate(0);
    }
    to {
    transform: rotate(360deg);
    }
    }
    .Cuteen_DarkPlanet
    #sun
    position absolute
    border-radius 100%
    left 44%
    top 30%
    height 6rem
    width 6rem
    background #ffee94
    box-shadow 0 0 40px #ffee94
    // opacity 0

    #moon
    position absolute
    border-radius 100%
    left 44%
    top 30%
    position absolute
    border-radius 100%
    height 6rem
    width 6rem
    box-shadow -1.8em 1.8em 0 0.2em #fff
    // opacity 1

    // &:after
    // position absolute
    // left 42%
    // top 30%
    // width 6rem
    // height 6rem
    // border-radius 50%
    // content ''
    // background #ffef9e
    // box-shadow 0 0 30px #ffef9e

    .search
    span
    display none

    .menus_item
    a
    text-decoration none!important
    //按钮相关,对侧栏按钮做过魔改的可以调整这里的数值
    // .icon-V
    // padding 5px
  3. 新建[BlogRoot]\themes\butterfly\source\js\sun_moon.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
    function switchNightMode() {  
    document.querySelector('body').insertAdjacentHTML('beforeend', '<div class="Cuteen_DarkSky"><div class="Cuteen_DarkPlanet"><div id="sun"></div><div id="moon"></div></div></div>'),
    setTimeout(function () {
    document.querySelector('body').classList.contains('DarkMode') ? (document.querySelector('body').classList.remove('DarkMode'), localStorage.setItem('isDark', '0'), document.getElementById('modeicon').setAttribute('xlink:href', '#icon-moon')) : (document.querySelector('body').classList.add('DarkMode'), localStorage.setItem('isDark', '1'), document.getElementById('modeicon').setAttribute('xlink:href', '#icon-sun')),
    setTimeout(function () {
    document.getElementsByClassName('Cuteen_DarkSky')[0].style.transition = 'opacity 3s';
    document.getElementsByClassName('Cuteen_DarkSky')[0].style.opacity = '0';
    setTimeout(function () {
    document.getElementsByClassName('Cuteen_DarkSky')[0].remove();
    }, 1e3);
    }, 2e3)
    })
    const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
    if (nowMode === 'light') {
    // 先设置太阳月亮透明度
    document.getElementById("sun").style.opacity = "1";
    document.getElementById("moon").style.opacity = "0";
    setTimeout(function () {
    document.getElementById("sun").style.opacity = "0";
    document.getElementById("moon").style.opacity = "1";
    }, 1000);

    activateDarkMode()
    saveToLocal.set('theme', 'dark', 2)
    // GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
    document.getElementById('modeicon').setAttribute('xlink:href', '#icon-sun')
    // 延时弹窗提醒
    setTimeout(() => {
    new Vue({
    data: function () {
    this.$notify({
    title: "关灯啦🌙",
    message: "当前已成功切换至夜间模式!",
    position: 'top-left',
    offset: 50,
    showClose: true,
    type: "success",
    duration: 5000
    });
    }
    })
    }, 2000)
    } else {
    // 先设置太阳月亮透明度
    document.getElementById("sun").style.opacity = "0";
    document.getElementById("moon").style.opacity = "1";
    setTimeout(function () {
    document.getElementById("sun").style.opacity = "1";
    document.getElementById("moon").style.opacity = "0";
    }, 1000);

    activateLightMode()
    saveToLocal.set('theme', 'light', 2)
    document.querySelector('body').classList.add('DarkMode'), document.getElementById('modeicon').setAttribute('xlink:href', '#icon-moon')
    setTimeout(() => {
    new Vue({
    data: function () {
    this.$notify({
    title: "开灯啦🌞",
    message: "当前已成功切换至白天模式!",
    position: 'top-left',
    offset: 50,
    showClose: true,
    type: "success",
    duration: 5000
    });
    }
    })
    }, 2000)
    }
    // handle some cases
    typeof utterancesTheme === 'function' && utterancesTheme()
    typeof FB === 'object' && window.loadFBComment()
    window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
    }
  4. 修改[BlogRoot]\themes\butterfly\layout\includes\head.pug,在文件末位加上一行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      //- global config  
    !=partial('includes/head/config', {}, {cache: true})

    include ./head/config_site.pug
    include ./head/noscript.pug

    !=fragment_cache('injectHeadJs', function(){return inject_head_js()})

    !=fragment_cache('injectHead', function(){return injectHtml(theme.inject.head)})
    + include ./custom/sun_moon.pug
  5. 修改[BlogRoot]\themes\butterfly\layout\includes\rightside.pug,把原本的昼夜切换按钮替换掉

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      when 'translate'  
    if translate.enable
    button#translateLink(type="button" title=_p('rightside.translate_title'))= translate.default
    when 'darkmode'
    if darkmode.enable && darkmode.button
    - button#darkmode(type="button" title=_p('rightside.night_mode_title'))
    - i.fas.fa-adjust
    + a.icon-V.hidden(onclick='switchNightMode()', title=_p('rightside.night_mode_title'))
    + svg(width='25', height='25', viewBox='0 0 1024 1024')
    + use#modeicon(xlink:href='#icon-moon')
  6. 修改[BlogRoot]\_config.butterfly.yml,引入一下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
    inject:  
    bottom:
    - <script src="/js/sun_moon.js" async></script>
    + ```

    ## 自定义加载动画
    1. 修改 `themes/butterfly/layout/includes/loading/fullpage-loading.pug`
    ```css
    #loading-box(onclick='document.getElementById("loading-box").classList.add("loaded")')
    .loading-bg
    div.loading-img
    .loading-image-dot

    script.
    const preloader = {
    endLoading: () => {
    document.body.style.overflow = 'auto';
    document.getElementById('loading-box').classList.add("loaded")
    },
    initLoading: () => {
    document.body.style.overflow = '';
    document.getElementById('loading-box').classList.remove("loaded")

    }
    }
    window.addEventListener('load',()=> { preloader.endLoading() })

    if (!{theme.pjax && theme.pjax.enable}) {
    document.addEventListener('pjax:send', () => { preloader.initLoading() })
    document.addEventListener('pjax:complete', () => { preloader.endLoading() })
    }
  7. 修改themes/butterfly/layout/includes/loading/index.pug

    1
    2
    3
    4
    5
    6
    7
    if theme.preloader.source === 1  
    include ./fullpage-loading.pug
    else if theme.preloader.source === 2
    include ./pace.pug
    else
    include ./fullpage-loading.pug
    include ./pace.pug
  8. 新建source/css/progress_bg.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
    .pace {  
    -webkit-pointer-events: none;
    pointer-events: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
    z-index: 2000;
    position: fixed;
    margin: auto;
    top: 10px;
    left: 0;
    right: 0;
    height: 8px;
    border-radius: 8px;
    width: 4rem;
    background: #eaecf2;
    border: 1px #e3e8f7;
    overflow: hidden;
    }

    .pace-inactive .pace-progress {
    opacity: 0;
    transition: 0.3s ease-in;
    }

    .pace .pace-progress {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    -o-box-sizing: border-box;
    box-sizing: border-box;
    -webkit-transform: translate3d(0, 0, 0);
    -moz-transform: translate3d(0, 0, 0);
    -ms-transform: translate3d(0, 0, 0);
    -o-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
    max-width: 200px;
    position: absolute;
    z-index: 2000;
    display: block;
    top: 0;
    right: 100%;
    height: 100%;
    width: 100%;
    background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
    animation: gradient 1.5s ease infinite;
    background-size: 200%;
    }

    .pace.pace-inactive {
    opacity: 0;
    transition: 0.3s;
    top: -8px;
    }
    @keyframes gradient {
    0% {
    background-position: 0% 50%;
    }
    50% {
    background-position: 100% 50%;
    }
    100% {
    background-position: 0% 50%;
    }
    }
  9. 修改/butterfly/source/css/_layout/loading.styl, 注意其中颜色代码--color等需自行替换为自己的色值

    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
    if hexo-config('preloader')  
    .loading-bg
    display: flex;
    width: 100%;
    height: 100%;
    position: fixed;
    background: var(--color);
    z-index: 1001;
    opacity: 1;
    transition: .3s;

    #loading-box
    .loading-img
    width: 100px;
    height: 100px;
    border-radius: 50%;
    margin: auto;
    border: 4px solid #f0f0f2;
    animation-duration: .3s;
    animation-name: loadingAction;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    .loading-image-dot
    width: 30px;
    height: 30px;
    background: #6bdf8f;
    position: absolute;
    border-radius: 50%;
    border: 6px solid #fff;
    top: 50%;
    left: 50%;
    transform: translate(18px, 24px);
    &.loaded
    .loading-bg
    opacity: 0;
    z-index: -1000;

    @keyframes loadingAction
    0% {
    opacity: 1;
    }

    100% {
    opacity: .4;
    }
  10. 在custom.css里加入

    1
    2
    3
    4
    .loading-img {  
    background: url(<头像网址>) no-repeat center center;
    background-size: cover;
    }
  11. 最后修改_config.butterfly.ymlpreloader选项, 改完以后source: 1为满屏加载无pace胶囊,source: 2pace胶囊无满屏动画, source: 3是两者都启用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # Loading Animation (加载动画)  
    preloader:
    enable: true
    # source
    # 1. fullpage-loading
    # 2. pace (progress bar)
    # else all
    source: 3
    # pace theme (see https://codebyzach.github.io/pace/)
    pace_css_url: /css/progress_bg.css
    avatar: # 自定义头像

文章栏目布局修改

  1. 修改[BlogRoot]\themes\butterfly\layout\includes\mixins\post-ui.pug,整个替换为下面的代码,这里可以引入彩色的图标,每个注释处都可以选择换上彩色图标,这里只需要替换成你自己的图标名字和调节相应的大小即可:
    本人懒得调js,就直接用fas的了

    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
    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)
    .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=title)
    .recent-post-info
    a.article-title(href=url_for(link) title=title)
    .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
    //- svg.meta_icon(style="width:16px;height:16px;position:relative;top:3px").post-ui-icon
    //- use(xlink:href='#icon-tuding')
    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
    //- svg.meta_icon(style="width:21px;height:21px;position:relative;top:6px").post-ui-icon
    //- use(xlink:href='#icon-rili')
    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
    //- svg.meta_icon(style="width:13px;height:13px;position:relative;top:2px").post-ui-icon
    //- use(xlink:href='#icon-gengxin1')
    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
    //- svg.meta_icon(style="width:12px;height:12px;position:relative;top:1px").post-ui-icon
    //- use(xlink:href='#icon-fenlei')
    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
    //- svg.meta_icon(style="width:13px;height:13px;position:relative;top:2px").post-ui-icon
    //- use(xlink:href='#icon-biaoqian')
    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
    //- svg.meta_icon(style="width:13px;height:13px;position:relative;top:2px").post-ui-icon
    //- use(xlink:href='#icon-pinglun1')
    if block
    block
    span.article-meta-label= ' ' + _p('card_post_count')

    if theme.comments.card_post_count
    case theme.comments.use[0]
    when 'Disqus'
    +countBlockInIndex
    a(href=full_url_for(link) + '#disqus_thread')
    i.fa-solid.fa-spinner.fa-spin
    when 'Disqusjs'
    +countBlockInIndex
    a(href=full_url_for(link) + '#disqusjs')
    span.disqus-comment-count(data-disqus-url=full_url_for(link))
    i.fa-solid.fa-spinner.fa-spin
    when 'Valine'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.valine-comment-count(data-xid=url_for(link))
    i.fa-solid.fa-spinner.fa-spin
    when 'Waline'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.waline-comment-count(id=url_for(link))
    i.fa-solid.fa-spinner.fa-spin
    when 'Twikoo'
    +countBlockInIndex
    a.twikoo-count(href=url_for(link) + '#post-comment')
    i.fa-solid.fa-spinner.fa-spin
    when 'Facebook Comments'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.fb-comments-count(data-href=urlNoIndex(article.permalink))
    when 'Remark42'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.remark42__counter(data-url=urlNoIndex(article.permalink))
    i.fa-solid.fa-spinner.fa-spin
    when 'Artalk'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.artalk-count(data-page-key=url_for(link))
    i.fa-solid.fa-spinner.fa-spin
    a.article-content(href=url_for(link) title=title)
    //- 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-arrow

    if theme.ad && theme.ad.index
    if (index + 1) % 3 == 0
    .recent-post-item.ads-wrap!=theme.ad.index
  2. 样式方案提供两种:

  • 样式一:电脑端宽屏采用滑动卡片,平板宽度采用双栏布局,手机宽度采用单栏卡片
  • 样式二:移除滑动卡片,按屏幕宽度依次应用三栏、双栏、单栏

新建目录\butterfly\source\css\_index_card_style\,并在下面新建对应的文件slidecard.stylmulticard.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
// slidecard
//default color:
:root
--recent-post-bgcolor: rgba(255, 255, 255, 0.9) //默认背景
--article-content-bgcolor: #49b1f5 //描述版块背景
--recent-post-arrow: #ffffff //箭头配色
--recent-post-cover-shadow: #ffffff //封面遮罩层配色,建议和默认值的颜色相对应。
--recent-post-transition: all 0.5s cubic-bezier(0.59, 0.01, 0.48, 1.17) //动画效果。不了解的不要改动
[data-theme="dark"]
--recent-post-bgcolor: rgba(35,35,35,0.5)
--article-content-bgcolor: #99999a
--recent-post-arrow: #37e2dd
--recent-post-cover-shadow: #232323
// 默认的首页卡片容器布局
.recent-posts
padding 0 15px 0 15px
height fit-content
.recent-post-item
margin-bottom 15px
width 100%
background var(--recent-post-bgcolor)
overflow hidden
border-radius 15px
.recent-post-content
display flex
background var(--recent-post-bgcolor)
position relative
.recent-post-cover
display flex
background transparent
.recent-post-info
display flex
background transparent
flex-direction column
justify-content center
align-items center
.article-title
height 50%
display: flex
text-align: center
align-items: center
justify-content: flex-end
flex-direction: column
.article-title-link
color: var(--text-highlight-color)
transition: all .2s ease-in-out
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
&:hover
color: $text-hover
.recent-post-meta
height 50%
display: flex
text-align: center
align-items: center
justify-content: flex-start
flex-direction: column
.article-meta-wrap
color #969797
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
a
color: var(--text-highlight-color)
transition: all .2s ease-in-out
color #969797
&:hover
color: $text-hover
.article-content
display flex
text-align: center
flex-direction row
align-items center
justify-content center
.article-content-text
display -webkit-box
-webkit-box-orient vertical
text-overflow: ellipsis
overflow hidden
color #fff
text-shadow 1px 2px 3px #000
&::before
content "❝"
font-size 20px
&::after
content "❞"
font-size 20px
&.ads-wrap
display: block !important
height: auto !important
// PC端滑动卡片样式
@media screen and (min-width:1069px)
.recent-posts
padding 0 15px 0 15px
.recent-post-item
.recent-post-content
position relative
height 200px
width 100%
transition var(--recent-post-transition)
&:hover
.recent-post-cover-shadow
width 10.1%
transition var(--recent-post-transition)
.recent-post-cover
width 10%
transition var(--recent-post-transition)
.article-content
width calc(30% + 80px)
transition var(--recent-post-transition)
.article-content-text
opacity 1
.recent-post-arrow
transition var(--recent-post-transition)
.recent-post-cover-shadow
z-index: 1
transition var(--recent-post-transition)
position: absolute
height 200px
width 40%
.recent-post-cover
height 200px
width 40%
transition var(--recent-post-transition)
img
height 100%
width 100%
object-fit cover

.recent-post-info
height 200px
width calc(60% - 80px)
.article-title
margin: 0px 40px
font-size 24px
.article-title-link
-webkit-line-clamp: 2;
.recent-post-meta
margin: 0px 20px
.article-meta-wrap
font-size 12px
-webkit-line-clamp: 3;
.article-content
height 200px
width 90px
background var(--article-content-bgcolor)
transition var(--recent-post-transition)
.article-content-text
-webkit-line-clamp 4
transition: var(--recent-post-transition)
opacity 0
.recent-post-arrow
transition var(--recent-post-transition)
display block
position absolute
height 20px
width 8px
background var(--recent-post-arrow)
&.both,
&.right
.recent-post-cover-shadow
left 0
background linear-gradient(to left, var(--recent-post-cover-shadow), transparent)
.recent-post-cover
order: 1
.recent-post-info
order: 2
.article-content
order: 3
clip-path polygon(0 50%, 80px 0, 100% 0, 100% 100%, 80px 100%)
.article-content-text
margin 20px 40px 20px 80px
.recent-post-arrow
order: 4
left calc(100% - 80px)
top calc(50% - 10px)
clip-path polygon(0 10px, 8px 0, 8px 20px)
&:hover
.recent-post-arrow
left calc(100% - 40px)
&.left
.recent-post-cover-shadow
right 0
background linear-gradient(to right, var(--recent-post-cover-shadow), transparent)
.recent-post-cover
order: 4
.recent-post-info
order: 3
.article-content
order: 2
clip-path polygon(100% 50%,calc(100% - 80px) 100%,0 100%,0 0,calc(100% - 80px) 0)
.article-content-text
margin 20px 80px 20px 40px
.recent-post-arrow
order: 1
left 72px
top calc(50% - 10px)
clip-path polygon(0 0, 8px 10px, 0 20px)
&:hover
.recent-post-arrow
left 32px
// 双栏布局卡片自适应适配
@media screen and (min-width:572px) and (max-width:1068px)
.recent-posts
padding 0 15px 0 15px
display flex
flex-direction row
flex-wrap wrap
.recent-post-item
border-radius 15px
overflow hidden
width 47%
margin 0px 3% 20px 0px
nav#pagination
width: 100%
// 手机端单栏布局自适应适配
@media screen and (max-width:572px)
.recent-posts
padding 0 15px 0 15px
.recent-post-item
border-radius 15px
overflow hidden

// 手机端及双栏卡片样式
@media screen and (max-width:1068px)
.recent-posts
.recent-post-item
.recent-post-content
flex-direction column
flex-wrap nowrap
align-items center
max-height 350px
height: auto
width 100%
.recent-post-cover
width 100%
height 200px
clip-path polygon(0 130px,0 0,100% 0,100% 130px,50% 100%)
img
height 200px
width 100%
object-fit cover
.recent-post-info
height 150px
width 100%
padding 0px 25px 5px 25px
.article-title
margin: 0px 40px
font-size 18px
.article-title-link
-webkit-line-clamp: 2;
.recent-post-meta
margin: 0px 20px
.article-meta-wrap
font-size 12px
-webkit-line-clamp: 3;
.article-content
position absolute
height 200px
width 100%
background rgba(25,25,25,0.5)
clip-path polygon(0 130px,0 0,100% 0,100% 130px,50% 100%)
.article-content-text
-webkit-line-clamp 3
font-size 16px
margin 0px 25px 30px 25px
.recent-post-arrow
display block
background var(--article-content-bgcolor)
position absolute
height 10px
width 20px
clip-path polygon(0 0,100% 0,50% 100%)
top 20px
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
// multicard
:root
--theme-color:rgb(57, 197, 187)
--text-bg-hover:rgba(57, 197, 187, 0.7)

.recent-posts
padding 0 5px 0 5px
height fit-content
.recent-post-item
margin-bottom 15px
overflow hidden
border-radius 15px
.recent-post-content
display flex
position relative
.recent-post-cover
display flex
background transparent
.recent-post-info
display flex
background transparent
flex-direction column
justify-content center
align-items center
.article-title
height 50%
display: flex
text-align: center
align-items: center
justify-content: flex-end
flex-direction: column
.article-title-link
color: var(--text-highlight-color)
transition: all .2s ease-in-out
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
&:hover
color: var(--theme-color)
.recent-post-meta
height 50%
display: flex
text-align: center
align-items: center
justify-content: flex-start
flex-direction: column
.article-meta-wrap
color #969797
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
a
color: var(--text-highlight-color)
transition: all .2s ease-in-out
color #969797
&:hover
color: var(--theme-color)
.article-content
display flex
text-align: center
flex-direction row
align-items center
justify-content center
.article-content-text
display -webkit-box
-webkit-box-orient vertical
text-overflow: ellipsis
overflow hidden
color #fff
text-shadow 1px 2px 3px #000
transition transform 0.6s;
&:hover
transform: scale(1.1);
&.ads-wrap
display: block !important
height: auto !important
nav#pagination
width: 100%
// 卡片单元布局样式
.recent-posts
padding 0 5px 0 5px
display flex
flex-direction row
flex-wrap wrap
.recent-post-item
border-radius 15px
overflow hidden
.recent-post-content
flex-direction column
flex-wrap nowrap
align-items center
max-height 350px
height: auto
width 100%
.recent-post-cover
width 100%
height 200px
clip-path polygon(0 130px,0 0,100% 0,100% 130px,50% 100%)
img
height 200px
width 100%
object-fit cover
.recent-post-info
height 145px
width 100%
padding 0px 25px 5px 25px
.article-title
margin: 0px 40px
font-size 19px
.article-title-link
-webkit-line-clamp: 2;
.recent-post-meta
margin: 0px 20px
.article-meta-wrap
font-size 13px
-webkit-line-clamp: 3;
.article-content
position absolute
height 200px
width 100%
background rgba(25,25,25,0.4)
clip-path polygon(0 130px,0 0,100% 0,100% 130px,50% 100%)
.article-content-text
-webkit-line-clamp 3
font-size 16px
margin 0px 25px 30px 25px
&::before
content "「"
font-size 20px
&::after
content "」"
font-size 20px
.recent-post-arrow
display block
background var(--text-bg-hover)
position absolute
height 10px
width 20px
clip-path polygon(0 0,100% 0,50% 100%)
// 三栏布局滑动卡片样式
@media screen and (min-width:1069px)
.recent-posts
.recent-post-item
width 32.3%
margin 0px 1% 20px 0px
.recent-post-content
.recent-post-info
.article-title
margin: 0px 5px
.article-title-link
-webkit-line-clamp: 1;
.recent-post-meta
margin: 0px 5px
.article-meta-wrap
-webkit-line-clamp: 2;
// 双栏布局卡片自适应适配
@media screen and (min-width:572px) and (max-width:1068px)
.recent-posts
.recent-post-item
width 47%
margin 0px 3% 20px 0px
// 单栏布局卡片自适应适配
@media screen and (max-width:572px)
.recent-posts
.recent-post-item
width 100%
  1. 修改[BlogRoot]\themes\butterfly\source\css\_page\homepage.styl,将整文件内容替换为以下代码:

    1
    2
    3
    4
    if hexo-config('index_card_style') == 'slidecard'  
    @import './_index_card_style/slidecard'
    else if hexo-config('index_card_style') == 'multicard'
    @import './_index_card_style/multicard'
  2. 然后在主题配置文件[BlogRoot]\_config.butterfly.yml里新增配置项,这样我们就可以通过配置项自由切换使用哪款了:

    1
    2
    3
    # 主页卡片样式  
    # Docs: https://akilar.top/posts/d6b69c49/
    index_card_style: multicard # slidecard | multicard.styl
  3. 考虑到不管是样式一还是样式二都存在一个布局突变的情况。为了不至于让首页的文章出现空缺,建议将首页生成的文章数量控制为1,2,3的公倍数。修改站点配置文件[BlogRoot]\_config.yml。找到以下配置项进行调整,注意这是站点配置文件本就有的配置项,不是新增配置项。建议是调整为12篇。如果你的侧边栏魔改内容特别多,那么建议改成18、24、30。务必确保文章卡片栏比侧栏完全展开要长,这样展示效果最好

    1
    2
    3
    4
    5
    6
    7
    8
    # Home page setting  
    # path: Root path for your blogs index page. (default = '')
    # per_page: Posts displayed per page. (0 = disable pagination)
    # order_by: Posts order. (Order by date descending by default)
    index_generator:
    path: ''
    per_page: 12
    order_by: -date
  4. 查看主题配置文件[BlogRoot]\_config.butterfly.yml,找到配置项开启描述栏,建议选择2模式,这样同时可以自定义描述和封面

    1
    2
    3
    4
    5
    6
    7
    8
    # Display the article introduction on homepage  
    # 1: description
    # 2: both (if the description exists, it will show description, or show the auto_excerpt)
    # 3: auto_excerpt (default)
    # false: do not show the article introduction
    index_post_content:
    method: 2
    length: 500 # if you set method to 2 or 3, the length need to config

信息卡片背景图

custom.css中加入以下样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 个人信息卡片背景图 */  
[data-theme="light"] #aside-content > .card-widget.card-info {
background-image: url(https://sourcebucket.s3.ladydaily.com/img/snowflower.webp);
background-repeat: no-repeat;
background-attachment: inherit;
background-size: 100%;
}
[data-theme="dark"] #aside-content > .card-widget.card-info {
background-image: url(https://sourcebucket.s3.ladydaily.com/img/aurora.webp);
background-repeat: no-repeat;
background-attachment: inherit;
background-size: 100%;
}
  • 一个是白天模式,一个是夜晚模式,也可以直接只要一个,删除判断
  • 图片最最好用渐变的,我比较懒,直接用的透明的

头像呼吸灯

custom.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
/* 头像呼吸灯 */  
[data-theme="light"] .avatar-img {
animation: huxi_light 4s ease-in-out infinite;
}
[data-theme="dark"] .avatar-img {
animation: huxi_dark 4s ease-in-out infinite;
}
@keyframes huxi_light {
0% {
box-shadow: 0px 0px 1px 1px #e9f5fa;
}
50% {
box-shadow: 0px 0px 5px 5px #e9f5fa;
}
100% {
box-shadow: 0px 0px 1px 1px #e9f5fa;
}
}
@keyframes huxi_dark {
0% {
box-shadow: 0px 0px 1px 1px #39c5bb;
}
50% {
box-shadow: 0px 0px 5px 5px #39c5bb;
}
100% {
box-shadow: 0px 0px 1px 1px #39c5bb;
}
}
  • 代码不难看懂,就是关键帧之间通过某个插值方法连续变样式而已
  • 本人删除了白天和黑夜的判断,并增加了一些关键帧

网站变化标题

  1. 新建文件[BlogRoot]\source\js\title.js,写入以下内容:
    可以使用emoji,win+.即可开始输入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //动态标题  
    var OriginTitile = document.title;
    var titleTime;
    document.addEventListener('visibilitychange', function () {
    if (document.hidden) {
    //离开当前页面时标签显示内容
    document.title = 'ATRI沉睡中';
    clearTimeout(titleTime);
    } else {
    //返回当前页面时标签显示内容
    document.title = '你回来啦';
    //两秒后变回正常标题
    titleTime = setTimeout(function () {
    document.title = OriginTitile;
    }, 2000);
    }
    });
  2. 在主题配置文件_config.butterfly.yml引入该文件:
    1
    2
    3
    inject:   
    bottom:
    - <script async src="/js/title.js"></script>