魔改前必看(太细节的就不写在教程中了,我也不会🤣🤣🤣)

  1. 博客魔改有风险,如果博客魔改出问题了又没有备份,可通过此项目查看基础源码进行回滚jerryc127/hexo-theme-butterflyccknbc-actions/blog-butterfly
  2. 这部分魔改基本上都是大佬们造好的轮子,我按照大佬们的轮子结合自己的喜好进行魔改的。
  3. 鉴于每个人的根目录名称都不一样,本帖博客根目录一律以[BlogRoot]指代。
  4. 本帖涉及魔改源码的内容,会使用diff代码块标识,复制时请不要忘记删除前面的+、-符号。
  5. 因为.pug.styl以及.yml等对缩进要求较为严格,请尽量不要使用记事本等无法提供语法高亮的文本编辑器进行修改。
  6. 本帖基于Butterfly主题进行魔改方案编写,因此请读者优先掌握Butterfly主题官方文档的内容后再来进行魔改。
  7. 魔改会过程常常引入自定义的css与js文件,方法见Hexo博客添加自定义css和js文件(太懒了不想自己写)

博客搭建与魔改系列教程导航🚥🚥🚥

  1. 博客搭建基础教程(一)
  2. 博客搭建基础教程(二)
  3. 博客搭建基础教程(三)
  4. 博客魔改教程总结(一)
  5. 博客魔改教程总结(二)
  6. 博客魔改教程总结(三) ⇦当前位置🪂

右边按钮阅读进度

点击查看教程

主题自带功能

  1. [BlogRoot]\_config.butterfly.yml中修改如下

    1
    2
    # show scroll percent in scroll-to-top button
    rightside_scroll_percent: true

    设置为true之后就会在上箭头显示阅读百分比。

  2. 这样设置之后只有数字显示,如果想要添加上百分号需要在[BlogRoot]\themes\butterfly\source\js\main.js修改一下代码,代码位置大概在350行左右

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      /**
    * rightside scroll percent
    */
    const rightsideScrollPercent = currentTop => {
    const scrollPercent = btf.getScrollPercent(currentTop, document.body)
    const goUpElement = document.getElementById('go-up')

    if (scrollPercent < 95) {
    goUpElement.classList.add('show-percent')
    - goUpElement.querySelector('.scroll-percent').textContent = scrollPercent
    + goUpElement.querySelector('.scroll-percent').textContent = `${scrollPercent}%`
    } else {
    goUpElement.classList.remove('show-percent')
    }
    }
  3. 重启项目即可看到效果

    1
    hexo cl; hexo s

自定义魔改(Leonus)

详见:返回顶部显示网页阅读进度

主题中自带的显示百分比显示数值没有那么丝滑,可以通过自定义实现显示百分比,通过该方法实现显示阅读百分比,务必关闭第一种方法的配置,否则会出现冲突。

实现原理其实很简单,我们只需要使用 被顶部卷去的高度 / (页面总高度 - 可视高度) ,既可算出百分比。
之所以减去可视高度,是因为当我们在滑到最底部的时候,可以看出 页面高度 = 被卷去的高度 + 可视高度

  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>
  5. 重启项目即可看到效果

    1
    hexo cl; hexo s

评论表情包放大(Leonus)

点击查看教程

详见:评论表情包放大功能,超实用

其实实现的原理很简单,就是创建一个盒子,将表情包的内容放在盒子里面,然后控制盒子位置和显示隐藏即可。

  1. 新建文件[BlogRoot]\source\js\emoji.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
    // 如果当前页有评论就执行函数
    if (document.getElementById('post-comment')) owoBig();
    // 表情放大
    function owoBig() {
    let flag = 1, // 设置节流阀
    owo_time = '', // 设置计时器
    m = 3; // 设置放大倍数
    // 创建盒子
    let div = document.createElement('div'),
    body = document.querySelector('body');
    // 设置ID
    div.id = 'owo-big';
    // 插入盒子
    body.appendChild(div)

    // 构造observer
    let observer = new MutationObserver(mutations => {

    for (let i = 0; i < mutations.length; i++) {
    let dom = mutations[i].addedNodes,
    owo_body = '';
    if (dom.length == 2 && dom[1].className == 'OwO-body') owo_body = dom[1];
    // 如果需要在评论内容中启用此功能请解除下面的注释
    // else if (dom.length == 1 && dom[0].className == 'tk-comment') owo_body = dom[0];
    else continue;

    // 禁用右键(手机端长按会出现右键菜单,为了体验给禁用掉)
    if (document.body.clientWidth <= 768) owo_body.addEventListener('contextmenu', e => e.preventDefault());
    // 鼠标移入
    owo_body.onmouseover = (e) => {
    if (flag && e.target.tagName == 'IMG') {
    flag = 0;
    // 移入300毫秒后显示盒子
    owo_time = setTimeout(() => {
    let height = e.path[0].clientHeight * m, // 盒子高
    width = e.path[0].clientWidth * m, // 盒子宽
    left = (e.x - e.offsetX) - (width - e.path[0].clientWidth) / 2, // 盒子与屏幕左边距离
    top = e.y - e.offsetY; // 盒子与屏幕顶部距离

    if ((left + width) > body.clientWidth) left -= ((left + width) - body.clientWidth + 10); // 右边缘检测,防止超出屏幕
    if (left < 0) left = 10; // 左边缘检测,防止超出屏幕
    // 设置盒子样式
    div.style.cssText = `display:flex; height:${height}px; width:${width}px; left:${left}px; top:${top}px;`;
    // 在盒子中插入图片
    div.innerHTML = `<img src="${e.target.src}">`
    }, 300);
    }
    };
    // 鼠标移出隐藏盒子
    owo_body.onmouseout = () => { div.style.display = 'none', flag = 1, clearTimeout(owo_time); }
    }

    })
    observer.observe(document.getElementById('post-comment'), { subtree: true, childList: true }) // 监听的 元素 和 配置项
    }
  2. 新建文件[BlogRoot]\source\js\emoji.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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #owo-big {
    position: fixed;
    align-items: center;
    background-color: rgb(255, 255, 255);
    border: 1px #aaa solid;
    border-radius: 10px;
    z-index: 9999;
    display: none;
    transform: translate(0, -105%);
    overflow: hidden;
    animation: owoIn 0.3s cubic-bezier(0.42, 0, 0.3, 1.11);
    }

    [data-theme=dark] #owo-big {
    background-color: #4a4a4a
    }

    #owo-big img {
    width: 100%;
    }

    /* 动画效果代码由 Heo:https://blog.zhheo.com/ 提供 */
    @keyframes owoIn {
    0% {
    transform: translate(0, -95%);
    opacity: 0;
    }
    100% {
    transform: translate(0, -105%);
    opacity: 1;
    }
    }
  3. 引入cssjs文件,和之前教程添加方法相同在这里就不再赘述了。

  4. 重启项目即可看到效果

    1
    hexo cl; hexo s

评论输入提醒(Leonus)

点击查看教程

详见:给你的评论添加一个输入提醒吧

实现很简单,纯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
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
/* 设置文字内容 :nth-child(1)的作用是选择第几个 */
.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(1):before {
content: '输入QQ号会自动获取昵称和头像🐧';
}

.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(2):before {
content: '收到回复将会发送到您的邮箱📧';
}

.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(3):before {
content: '可以通过昵称访问您的网站🔗';
}

/* 当用户点击输入框时显示 */
.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::before,
.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::after {
display: block;
}

/* 主内容区 */
.el-input.el-input--small.el-input-group.el-input-group--prepend::before {
/* 先隐藏起来 */
display: none;
/* 绝对定位 */
position: absolute;
/* 向上移动60像素 */
top: -60px;
/* 文字强制不换行,防止left:50%导致的文字换行 */
white-space: nowrap;
/* 圆角 */
border-radius: 10px;
/* 距离左边50% */
left: 50%;
/* 然后再向左边挪动自身的一半,即可实现居中 */
transform: translate(-50%);
/* 填充 */
padding: 14px 18px;
background: #444;
color: #fff;
}

/* 小角标 */
.el-input.el-input--small.el-input-group.el-input-group--prepend::after {
display: none;
content: '';
position: absolute;
/* 内容大小(宽高)为0且边框大小不为0的情况下,每一条边(4个边)都是一个三角形,组成一个正方形。
我们先将所有边框透明,再给其中的一条边添加颜色就可以实现小三角图标 */
border: 12px solid transparent;
border-top-color: #444;
left: 50%;
transform: translate(-50%, -48px);
}

快速添加友链

点击查看教程

参考教程:怕冷爱上雪:友情链接快速添加

  1. 新建文件[BlogRoot]\source\js\kslink.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
    var leonus = {
    linkCom: e => {
    var t = document.querySelector(".el-textarea__inner");
    "bf" == e ? (t.value = "```yml\n", t.value += "- name: \n link: \n avatar: \n descr: \n siteshot: ", t.value += "\n```", t.setSelectionRange(15, 15)) : (t.value = "站点名称:\n站点地址:\n头像链接:\n站点描述:\n站点截图:", t.setSelectionRange(5, 5)), t.focus()
    },
    owoBig: () => {
    if (!document.getElementById("post-comment") || document.body.clientWidth < 768) return;
    let e = 1,
    t = "",
    o = document.createElement("div"),
    n = document.querySelector("body");
    o.id = "owo-big", n.appendChild(o), new MutationObserver((l => {
    for (let a = 0; a < l.length; a++) {
    let i = l[a].addedNodes,
    s = "";
    if (2 == i.length && "OwO-body" == i[1].className) s = i[1];
    else {
    if (1 != i.length || "tk-comment" != i[0].className) continue;
    s = i[0]
    }
    s.onmouseover = l => {
    e && ("OwO-body" == s.className && "IMG" == l.target.tagName || "tk-owo-emotion" == l.target.className) && (e = 0, t = setTimeout((() => {
    let e = 3 * l.path[0].clientHeight,
    t = 3 * l.path[0].clientWidth,
    a = l.x - l.offsetX - (t - l.path[0].clientWidth) / 2,
    i = l.y - l.offsetY;
    a + t > n.clientWidth && (a -= a + t - n.clientWidth + 10), a < 0 && (a = 10), o.style.cssText = `display:flex; height:${e}px; width:${t}px; left:${a}px; top:${i}px;`, o.innerHTML = `<img src="${l.target.src}">`
    }), 300))
    }, s.onmouseout = () => {
    o.style.display = "none", e = 1, clearTimeout(t)
    }
    }
    })).observe(document.getElementById("post-comment"), {
    subtree: !0,
    childList: !0
    })
    },
    };
  2. 新建文件[BlogRoot]\source\css\kslink.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
    /* 添加友链按钮 */
    /* 快速填写格式 */
    .addBtn {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    }
    .addBtn button {
    transition: .2s;
    display: flex;
    margin: 5px auto;
    color: var(--global-bg);
    padding: 15px;
    border-radius: 40px;
    background: var(--search-result-title);
    align-items: center;
    }

    button {
    padding: 0;
    outline: 0;
    border: none;
    background: 0 0;
    cursor: pointer;
    touch-action: manipulation;
    }
    .fa-solid, .fas {
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    }
    .addBtn i {
    font-size: 1.3rem;
    margin-right: 10px;
    }
    .addBtn button:hover {
    background: var(--theme-color);
    color: #fff;
    }
  3. 为了节省DOM规模,仅在你的友链页面(例如[BlogRoot]\source\link\index.md)对应的md文件最后,引入以上两个文件以及定义按钮元素:

    1
    2
    3
    <div class="addBtn"><button onclick="leonus.linkCom()"><i class="fa-solid fa-circle-plus"></i>快速申请 (默认样式)</button> <button onclick="leonus.linkCom(&quot;bf&quot;)"><i class="fa-solid fa-circle-plus"></i>快速申请 (Butterfly)</button></div>
    <link rel="stylesheet" href="/css/kslink.css">
    <script src="/js/kslink.js"></script>
  4. 重启项目即可看到效果

    1
    hexo cl; hexo s

Vue+Element样式弹窗

点击查看教程
  1. 在主题配置文件[BlogRoot]\_config.butterfly.yml中 引入VueElement相关依赖:

    1
    2
    3
    4
    5
    6
    inject:
    head:
    + - <link rel="stylesheet" href="https://cdn1.tianli0.top/npm/element-ui@2.15.6/packages/theme-chalk/lib/index.css"> # 引入组件库(f12)
    bottom:
    + - <script async src="https://cdn1.tianli0.top/npm/vue@2.6.14/dist/vue.min.js"></script> # 引入VUE(f12)
    + - <script async src="https://cdn1.tianli0.top/npm/element-ui@2.15.6/lib/index.js"></script> # 引入ElementUI(f12)
  2. 在你想要弹出弹窗的js代码中加入如下代码即可触发弹窗:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    new Vue({
    data: function () {
    this.$notify({
    title: "你已被发现😜",
    message: "小伙子,扒源记住要遵循GPL协议!",
    position: 'top-left',
    offset: 50,
    showClose: true,
    type: "warning",
    duration: 5000
    });
    }
    })
    • notify:弹窗类型,可以替换为message(信息提示)和confirm(二次确认提示)

    • title:弹窗标题,可以改为自定义标题

    • message:弹窗信息,可以改为自定义内容

    • position:弹出位置,bottom、top和left、right两两组合

    • offset:偏移量,简单可以理解为与边界的距离

    • showClose:是否显示关闭按钮

    • type:提示类型,可选success/warning/info/error等

    • duration:停留时间,弹出停留至消失的时间,单位ms

      详见:Vue中常用的提示信息

  3. 通过监听的方式监听动作触发相应的提示

    • [BlogRoot]\js文件夹下创建通知js脚本notification

    • 添加防抖全局计时器,这样可以给触发一些合理的时间间隔

      1
      2
      3
      4
      5
      6
      7
      // 防抖全局计时器
      let TT = null; //time用来控制事件的触发
      // 防抖函数:fn->逻辑 time->防抖时间
      function debounce(fn, time) {
      if (TT !== null) clearTimeout(TT);
      TT = setTimeout(fn, time);
      }
    • 添加复制唱功的提示信息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // 常用的提示信息 https://blog.csdn.net/weixin_53791978/article/details/124169067
      // 复制提醒
      document.addEventListener("copy", function () {
      debounce(function () {
      new Vue({
      data: function () {
      this.$notify({
      title: "哎嘿!复制成功🍬",
      message: "若要转载最好保留原文链接哦,给你一个大大的赞!",
      position: 'top-left',
      offset: 50,
      showClose: true,
      type: "success",
      duration: 5000
      });
      }
      })
      }, 300);
      })
    • 当打开控制台的时候给出提示

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      document.onkeydown = function (e) {
      if (123 == e.keyCode || (e.ctrlKey && e.shiftKey && (74 === e.keyCode || 73 === e.keyCode || 67 === e.keyCode)) || (e.ctrlKey && 85 === e.keyCode)) {
      debounce(function () {
      new Vue({
      data: function () {
      this.$notify({
      title: "你已被发现😜",
      message: "小伙子,扒源记住要遵循GPL协议!",
      position: 'top-left',
      offset: 50,
      showClose: true,
      type: "warning",
      duration: 5000
      });
      }
      })
      }, 300);
      }
      };

      我在这里的示例就这两种,其他想要实现的弹窗提示或者二次确认弹窗都可以通过上述方法查找资料实现。

  1. 重启项目即可看到效果

    1
    hexo cl; hexo s