本文最后更新于 2024-08-10,文章内容可能已经过时。

前言

随着 halo-theme-hao 主题 1.4.9 版本的正式更新,删减了好多的功能,比如弹幕、鱼塘、豆瓣页面模版等,因此基于最新的 1.4.9 进行了一些个性化的修改,保留了最新版本特性的同时,加入了之前版本的一些功能,接着来看看我具体的改造过程吧!

一、加入弹幕功能

1、在halo-theme-hao/template/comments.html 文件的第260行,也就是 <hr>标签上方加入如下代码:

<th:block th:if="${ theme.config.comments.commentsEnable &&
                        #strings.contains('Twikoo,Artalk,Waline',theme.config.comments.use) &&
                        theme.config.envelope_comment.enable_danmu }">
                    <div id="danmuBtn">

                    </div>
                    <div id="danmu">
                    </div>


                    <style>
                        .default-style a {
                            color: #eee;
                        }

                        :root {
                            --hao-purple: #4976f5;
                        }

                        #article-container a:not(.headerlink, .fancybox) {
                            font-weight: 700;
                            color: #4c4948;
                            padding: 0 3px;
                            border-bottom: 2px var(--hao-purple) solid;
                        }

                        #article-container a:not(.headerlink, .fancybox):hover {
                            color: #fff;
                            border-radius: 5px;
                            text-decoration: none;
                            background-color: var(--hao-purple);
                        }

                        #danmu {
                            width: 100%;
                            height: calc(100% - 60px);
                            position: fixed;
                            left: 0;
                            top: 60px;
                            z-index: 1;
                            pointer-events: none;
                        }

                        .hidedanmu {
                            opacity: 0;
                        }

                        .hidedanmu * {
                            pointer-events: none !important;
                        }

                        div#danmuBtn {
                            display: flex;
                            justify-content: center;
                            margin: 20px;
                        }

                        div#danmuBtn button {
                            background: var(--hao-purple);
                            color: white;
                            padding: 8px 20px;
                            margin: 0 20px;
                            border-radius: 100px;
                        }

                        .default-style {
                            pointer-events: all;
                            cursor: pointer;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            font-size: 16px;
                            border-radius: 100px;
                            background-color: rgba(0, 0, 0, 0.5);
                            padding: 6px 16px 6px 6px;
                            color: #eee;
                        }

                        .default-style:hover {
                            background-color: rgba(0, 0, 0, 0.7);
                            transition: .3s
                        }

                        .default-style img {
                            pointer-events: none;
                            height: 30px;
                            width: 30px;
                            margin: 0 5px 0 0 !important;
                            border-radius: 50%;
                        }

                        .default-style p {
                            line-height: 1;
                            pointer-events: none;
                            margin: 0 !important;
                            max-width: 300px;
                            white-space: nowrap;
                            overflow: hidden;
                            text-overflow: ellipsis;
                        }
                    </style></th:block>

2、在目录 halo-theme-hao/templates/assets/libs/twikoo/下加入文件easy-Danmaku.min.js。文件自行在 1.4.8 版本里边寻找复制粘贴。

3、在 halo-theme-hao/templates/modules/layouts/layout.html文件的第 157 行后,也就是代码 <th:block th:replace="~{modules/common/51-la}"/> 后加入如下代码:

<!-- 官方评论插件js.  -->
<script th:if="${pluginFinder.available('PluginCommentWidget')}"
            src="/plugins/PluginCommentWidget/assets/static/comment-widget.iife.js"></script>
<script th:if="${theme.config.envelope_comment.enable_danmu}"
            th:src="${assets_link + '/libs/twikoo/easy-Danmaku.min.js'}" id="Danmaku"></script>

4、在 themes/theme-hao/templates/assets/js/halo.js 文件中加入如下方法,如果有则无需添加:

  danmu: () => {
        const e = new EasyDanmakuMin({
            el: "#danmu",
            line: 10,
            speed: 20,
            hover: !0,
            loop: !0
        });
        let t = saveToLocal.get("danmu");
        if (t)
            e.batchSend(t, !0);
        else {
            let n = [];
            if (GLOBAL_CONFIG.source.comments.use == 'Twikoo') {
                fetch(GLOBAL_CONFIG.source.twikoo.twikooUrl, {
                    method: "POST",
                    body: JSON.stringify({
                        event: "GET_RECENT_COMMENTS",
                        accessToken: GLOBAL_CONFIG.source.twikoo.accessToken,
                        includeReply: !1,
                        pageSize: 5
                    }),
                    headers: {
                        "Content-Type": "application/json"
                    }
                }).then((e => e.json())).then((({
                    data: t
                }) => {
                    t.forEach((e => {
                            null == e.avatar && (e.avatar =
                                    "https://cravatar.cn/avatar/d615d5793929e8c7d70eab5f00f7f5f1?d=mp"
                                ),
                                n.push({
                                    avatar: e.avatar,
                                    content: e.nick + ":" + btf.changeContent(e
                                        .comment),
                                    href: e.url + '#' + e.id

                                })
                        })),
                        e.batchSend(n, !0),
                        saveToLocal.set("danmu", n, .02)
                }))
            }
            if (GLOBAL_CONFIG.source.comments.use == 'Artalk') {
                const statheaderList = {
                    method: 'GET',
                    headers: {
                        'Origin': window.location.origin
                    }
  
                }
                const queryParams = new URLSearchParams({
                    'site_name': GLOBAL_CONFIG.source.artalk.siteName,
                    'limit': '100'
                });

                fetch(GLOBAL_CONFIG.source.artalk.artalkUrl + 'api/v2/stats/latest_comments?' + queryParams
                        .toString(),
                        statheaderList)
                    .then((e => e.json())).then((({
                        data: t
                    }) => {
                        t.forEach((e => {
                                n.push({
                                    avatar: 'https://cravatar.cn/avatar/' + e
                                        .email_encrypted + '?d=mp&s=240',
                                    content: e.nick + ":" + btf.changeContent(e
                                        .content_marked),
                                    href: e.page_url + '#atk-comment-' + e.id

                                })
                            })),
                            e.batchSend(n, !0),
                            saveToLocal.set("danmu", n, .02)
                    }))
            }
            if (GLOBAL_CONFIG.source.comments.use == 'Waline') {
                const loadWaline = () => {
                    Waline.RecentComments({
                        serverURL: GLOBAL_CONFIG.source.waline.serverURL,
                        count: 50
                    }).then(({
                        comments
                    }) => {
                        const walineArray = comments.map(e => {
                            return {
                                'content': e.nick + ":" + btf.changeContent(e.comment),
                                'avatar': e.avatar,
                                'href': e.url + '#' + e.objectId,
                            }
                        })
                        e.batchSend(walineArray, !0),
                            saveToLocal.set("danmu", walineArray, .02)
                    })
                }
                if (typeof Waline === 'object') loadWaline()
                else getScript(GLOBAL_CONFIG.source.waline.js).then(loadWaline)
            }

        }
        document.getElementById("danmuBtn").innerHTML =
            "<button class=\"hideBtn\" onclick=\"document.getElementById('danmu').classList.remove('hidedanmu')\">显示弹幕</button> <button class=\"hideBtn\" onclick=\"document.getElementById('danmu').classList.add('hidedanmu')\">隐藏弹幕</button>"
    },

5、在主题的配置项留言中加入设置功能

在根目录下的 settings.yaml 文件的留言配置选项中加入如何设置项,注意层级关系!!!

        - $formkit: radio
          label: 弹幕
          name: enable_danmu
          value: true
          options:
            - label: 启用
              value: true
            - label: 禁用
              value: false

至此,弹幕功能加入到你的博客系统了!由于缓存的问题,可能需要自行清理一下才有效果。

二、加入鱼塘和豆瓣页面模版

  1. 在主题包的根目录下找到 theme.yaml 文件,添加如下的yaml格式代码
- name: 豆瓣页面模版
  description: 豆瓣页面
  screenshot:
  file: douban.html
- name: 鱼塘页面模版
  description: 友链鱼塘
  screenshot:
  file: fcircle.html
  1. 加入相关文件
    在template下加入 douban.html、fcircle.html 两个文件。这两个文件可以在之前的1.4.8版本中可以找到。
    在增加鱼塘页面模版的时候,由于 1.4.9 版本也删除鱼塘相关的 js/css文件,因此需要在 halo-theme-hao/templates/assets/libs/moments/ 目录下新增以下三个文件:
  • app.min.js
  • bundle.js
  • heoMainColor.js
    这三个文件的下载方式和上述一样,找到以前版本的主题复制过来即可!
  1. 加入主题设置yaml参数
    在根目录下的 settings.yaml 下的友链配置组后加入如下代码:
    - group: fcircle
      label: 友链鱼塘
      formSchema:
        - $formkit: text
          name: buttonTitle
          label: 按钮标题
          value: 部署项目
        - $formkit: url
          name: buttonUrl
          validation: url
          value: https://githubfast.com/Rock-Candy-Tea/hexo-circle-of-friends
          label: 按钮跳转连链接
          placeholder: 请输入跳转 url
        - $formkit: url
          name: apiurl
          value: "https://moments.kunkunyu.com/"
          label: token地址
          placeholder: 请输入token地址
        - $formkit: radio
          name: fcircleRandomFriendsEnable
          label: 友链鱼塘页面(钓鱼)
          value: true
          options:
            - label: 打开
              value: true
            - label: 关闭
              value: false
        - $formkit: radio
          name: linksRandomFriendsEnable
          label: 友链页面(钓鱼)
          value: true
          options:
            - label: 打开
              value: true
            - label: 关闭
              value: false
    - group: douban
      label: 豆瓣
      formSchema:
        - $formkit: attachment
          name: backgroundImg
          label: 背景图
          value: https://liuzhihang.com/upload/moments.png
          placeholder: 请输入图片地址
        - $formkit: text
          name: smallTitle
          label: 小标题
          value: 电影
        - $formkit: text
          name: bigTitle
          label: 大标题
          value: 静下来慢慢感受着,流淌的故事。
        - $formkit: text
          name: detail
          label: 描述
          value: 跟 hao 用心体会电影的魅力
        - $formkit: text
          name: buttonTitle
          label: 按钮标题
          value: 感受更多
        - $formkit: url
          name: buttonUrl
          validation: url
          value: https://www.douban.com/search?cat=1002&q=%E6%9C%80%E6%96%B0%E7%94%B5%E5%BD%B1
          label: 按钮跳转连链接
          placeholder: 请输入跳转 url

三、加入友链页面的钓鱼功能

必须保证友链鱼塘在主题的配置项中!!!

找到 theme-hao/templates/macro/content-links.html 文件,在该文件的第47行代码后,即

 <!--互动友链-->
        <th:block th:replace="~{macro/links-canvas :: links-canvas(${groups})}" />

后加入如下代码:

<th:block th:if="${not #lists.isEmpty(groups) && theme.config.fcircle.linksRandomFriendsEnable}">
            <div class="title-h2-a">
                <div class="title-h2-a-left">
                    <h2 style="padding-top:0;margin:.6rem 0 .6rem">🎣 钓鱼</h2>
                    <a class="random-post-start" href="javascript:fetchRandomPost();">
                        <i class="haofont hao-icon-arrow-rotate-right"></i>
                    </a>
                </div>
                <div th:if="${not #strings.isEmpty(theme.config.link.fcircleUrl)}" class="title-h2-a-right">
                    <a class="random-post-all" th:href="${theme.config.link.fcircleUrl}" data-pjax-state="">查看鱼塘</a>
                </div>
            </div>
            <div id="random-post"></div>
            <script type="text/javascript">
                var fdataUser = {
                    apiurl: "[(${ theme.config.fcircle.apiurl })]",
                    defaultFish: 500,
                    hungryFish: 500,
                }
            </script>
            <script th:src="${assets_link + '/libs/moments/random-friends-post.js'}"></script>
</th:block>

目前的链接安全管理插件无法主题模版页面检测到链接,因此如果鱼塘和友链页面的随机文章上想加入链接跳转安全检测功能,可以在文件 halo-theme-hao/templates/assets/libs/moments/random-friends-post.js的代码 var link = json.link; 替换为 var link = "/jumpGo?goUrl=" + encodeURI(json.link);

四、侧边栏加入今日诗词小组件

  1. 创建组件
    在目录 theme-hao/templates/modules/widgets/aside 下新增文件 card-poem.html,内容如下:
<!-- 今日诗词 -->
<div>
  <div id="card-poem" class="card-widget">
      <div id="poem_sentence"></div>
      <div id="poem_info">
          <div id="poem_dynasty"></div>
          <div id="poem_author"></div>
      </div>
  </div>
  <script data-pjax type="text/javascript" src="https://cdn.jsdelivr.net/npm/js-heo@1.0.11/poem/jinrishici.js"></script>
  <script type="text/javascript" data-pjax>
      document.addEventListener("pjax:complete", function () {
          jinrishici.load(function(result) {
            var sentence = document.querySelector("#poem_sentence")
            if(sentence){
              var author = document.querySelector("#poem_author")
              var dynasty = document.querySelector("#poem_dynasty")
              var sentenceText = result.data.content
              sentenceText = sentenceText.substr(0, sentenceText.length - 1);
              sentence.innerHTML = sentenceText
              dynasty.innerHTML = result.data.origin.dynasty
              author.innerHTML = result.data.origin.author + '《' + result.data.origin.title + '》'
            }
          });
      })
      document.addEventListener("DOMContentLoaded", function () {
           jinrishici.load(function(result) {
              var sentence = document.querySelector("#poem_sentence")
              if(sentence){
                 var author = document.querySelector("#poem_author")
                 var dynasty = document.querySelector("#poem_dynasty")
                 var sentenceText = result.data.content
                 sentenceText = sentenceText.substr(0, sentenceText.length - 1);
                 sentence.innerHTML = sentenceText
                 dynasty.innerHTML = result.data.origin.dynasty
                 author.innerHTML = result.data.origin.author + '《' + result.data.origin.title + '》'
              }
            });
      })
  </script>
  <link rel="stylesheet" th:href="${assets_link + '/libs/moments/heoMainColor.css'}">
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/js-heo@1.0.11/poem/poem.css">
  <style>
      #poem_sentence{
            font-weight: bolder;
        }
      #poem_dynasty, #poem_author{
        font-size: 14px;
        color: #909399;
      }
      #poem_sentence{
        color: #499EFF;
      }
  </style>
</div>

  1. 引入组件

在主题的根目录下的 settings.yaml 文件中加入组件的名称,不同版本根据设置格式自行加入,对于1.4.9版本重构后的侧边栏组件可按照如下示例加入:

 - $formkit: repeater
              name: indexWidget
              id: indexWidget
              label: 首页
              children:
                - $formkit: select
                  name: widget
                  id: widget
                  key: widget
                  label: "小工具"
                  options:
                    - value: "profile"
                      label: 个人卡片
                    - value: "card-poem"
                      label: 今日诗词

之后在你的主题侧边栏设置中,便可以勾选此选项,增加一个今日诗词组件了!

五、侧边栏小板报魔改

我们先来看一下整体效果吧!

xiaoheiban.png

前置条件,需要申请腾讯位置服务的 key,申请地址

1、修改 head.html 文件

在主题的 templates/modules/head.html 路径中修改如下部分,主要是在 welocme.js 文件中加入版本号,防止缓存导致的更新问题。

  <!-- 小板报 -->
    <th:block th:if="${not #strings.isEmpty(theme.config.sidebar.welcome.key)}">
        <script defer th:if="${#strings.contains(theme.config.sidebar.widgetss.indexWidgets,'welcome') ||
            #strings.contains(theme.config.sidebar.widgetss.postWidgets,'welcome') ||
            #strings.contains(theme.config.sidebar.widgetss.tagWidgets,'welcome') ||
            #strings.contains(theme.config.sidebar.widgetss.categoryWidgets,'welcome')}"
                th:src="${assets_link + '/libs/welcome/welcome.js' + theme_version}"></script>
    </th:block>

2、修改配置文件

在主题的根目录下找到 settings.yaml 文件,主要是修改了腾讯位置 key 的描述信息,其他的经度坐标和维度坐标不变。

- $formkit: group
  name: welcome
  label: 小板报
  value:
     key:
     locationLng:
     locationLat:
   children:
      - $formkit: text
        name: key
        label: 腾讯地图服务key
        value: 
        help: 获取地址https://lbs.qq.com/dev/console/application/mine
      - $formkit: text
        name: locationLng
        label: 经度坐标
        value: "113.215456"
        help: 可访问 https://lbs.qq.com/getPoint 拾取坐标,示例:113.215456
        placeholder: 请输入经度坐标
      - $formkit: text
        name: locationLat
        label: 纬度坐标
        help: 示例:22.582401
        value: "22.582401"
        placeholder: 请输入纬度坐标

3、确认全局变量配置

找到此路径下的 theme-hao/templates/modules/variables/site-config.html site-config 文件,确保一下变量存在:

 welcome: {
                key: "[(${theme.config.sidebar.welcome.key})]",
                locationLng: [[${theme.config.sidebar.welcome.locationLng}]],
                locationLat: [[${theme.config.sidebar.welcome.locationLat}]]
            },

如不存在,请在该文件的 GLOBAL_CONFIG 的 对象中增加该变量!

4、修改 welcome.js 文件

找到 templates/assets/libs/welcome/welcome.js 文件,修改最终插入到组件的内容逻辑,这里是通过 Ipinfo 请求获取到了位置信息,然后计算访客和你填入的经纬度之间的距离,并根据地名、时间动态拼接一些提示语。直接粘贴一下代码将你的 welcome.js 文件替换。

let ipLocation = {};
$.ajax({
    type: 'get',
    url: 'https://apis.map.qq.com/ws/location/v1/ip?key=' + GLOBAL_CONFIG.source.welcome.key + '&output=jsonp',
    dataType: 'jsonp',
    success: function (res) {
        if(res.status == 0 && res.message == "Success"){
            ipLocation = {
                city: res.result.ad_info.city,
                country: res.result.ad_info.nation,
                region: res.result.ad_info.province,
                district: res.result.ad_info.district,
                ip: res.result.ip,
                location: res.result.location
            }
        }else{
            ipLocation = {
                city: "未知",
                country: "未知",
                region: "未知",
                district: "未知",
                ip: "未知",
                location: {
                    lng:GLOBAL_CONFIG.source.welcome.locationLng,
                    lat:GLOBAL_CONFIG.source.welcome.locationLat,
                }
            }
        }
        showWelcome();
    },
    error: function(res){
        document.getElementById("welcome-info").innerText = "请求出错,请检查token用量是否耗尽";
    }
})

// 如果使用了pjax在加上下面这行代码
document.addEventListener('pjax:complete', ()=>{
     showWelcome();
});


function getDistance(e1, n1, e2, n2) {
    const R = 6371
    const { sin, cos, asin, PI, hypot } = Math
    let getPoint = (e, n) => {
        e *= PI / 180
        n *= PI / 180
        return { x: cos(n) * cos(e), y: cos(n) * sin(e), z: sin(n) }
    }

    let a = getPoint(e1, n1)
    let b = getPoint(e2, n2)
    let c = hypot(a.x - b.x, a.y - b.y, a.z - b.z)
    let r = asin(c / 2) * 2 * R
    return Math.round(r);
}

function showWelcome() {
   
    if (ipLocation.city) {
        let latLoc = ipLocation.location
        let dist = getDistance(GLOBAL_CONFIG.source.welcome.locationLng, GLOBAL_CONFIG.source.welcome.locationLat, latLoc.lng, latLoc.lat);
        let pos = ipLocation.country;
        let ip;
        let posdesc;
        //根据国家、省份、城市信息自定义欢迎语
        switch (ipLocation.country) {
            case "日本":
                posdesc = "よろしく,一起去看樱花吗";
                break;
            case "美国":
                posdesc = "Let us live in peace!";
                break;
            case "英国":
                posdesc = "想同你一起夜乘伦敦眼";
                break;
            case "俄罗斯":
                posdesc = "干了这瓶伏特加!";
                break;
            case "法国":
                posdesc = "C'est La Vie";
                break;
            case "德国":
                posdesc = "Die Zeit verging im Fluge.";
                break;
            case "澳大利亚":
                posdesc = "一起去大堡礁吧!";
                break;
            case "加拿大":
                posdesc = "拾起一片枫叶赠予你";
                break;
            case "中国":
                pos = `${ipLocation.region} ${ipLocation.city} ${ipLocation.district}`;
                ip = ipLocation.ip;
                switch (ipLocation.region.replace("省", "").replace("市", "")) {
                    case "北京":
                        posdesc = "北——京——欢迎你~~~";
                        break;
                    case "天津":
                        posdesc = "讲段相声吧";
                        break;
                    case "河北":
                        posdesc = "山势巍巍成壁垒,天下雄关铁马金戈由此向,无限江山";
                        break;
                    case "山西":
                        posdesc = "展开坐具长三尺,已占山河五百余";
                        break;
                    case "内蒙古":
                        posdesc = "天苍苍,野茫茫,风吹草低见牛羊";
                        break;
                    case "辽宁":
                        posdesc = "我想吃烤鸡架!";
                        break;
                    case "吉林":
                        posdesc = "状元阁就是东北烧烤之王";
                        break;
                    case "黑龙江":
                        posdesc = "很喜欢哈尔滨大剧院";
                        break;
                    case "上海":
                        posdesc = "众所周知,中国只有两个城市";
                        break;
                    case "江苏":
                        switch (ipLocation.city.replace("市", "")) {
                            case "南京":
                                posdesc = "这是我挺想去的城市啦";
                                break;
                            case "苏州":
                                posdesc = "上有天堂,下有苏杭";
                                break;
                            default:
                                posdesc = "散装是必须要散装的";
                                break;
                        }
                        break;
                    case "浙江":
                        posdesc = "东风渐绿西湖柳,雁已还人未南归";
                        break;
                    case "河南":
                        switch (ipLocation.city.replace("市", "")) {
                            case "郑州":
                                posdesc = "豫州之域,天地之中";
                                break;
                            case "南阳":
                                posdesc = "臣本布衣,躬耕于南阳此南阳非彼南阳!";
                                break;
                            case "驻马店":
                                posdesc = "峰峰有奇石,石石挟仙气嵖岈山的花很美哦!";
                                break;
                            case "开封":
                                posdesc = "刚正不阿包青天";
                                break;
                            case "洛阳":
                                posdesc = "洛阳牡丹甲天下";
                                break;
                            default:
                                posdesc = "可否带我品尝河南烩面啦?";
                                break;
                        }
                        break;
                    case "安徽":
                        posdesc = "蚌埠住了,芜湖起飞";
                        break;
                    case "福建":
                        posdesc = "井邑白云间,岩城远带山";
                        break;
                    case "江西":
                        posdesc = "落霞与孤鹜齐飞,秋水共长天一色";
                        break;
                    case "山东":
                        posdesc = "遥望齐州九点烟,一泓海水杯中泻";
                        break;
                    case "湖北":
                        switch (ipLocation.city.replace("市", "")) {
                            case "黄冈":
                                posdesc = "红安将军县!辈出将才!";
                                break;
                            default:
                                posdesc = "来碗热干面~";
                                break;
                        }
                        break;
                    case "湖南":
                        posdesc = "74751,长沙斯塔克";
                        break;
                    case "广东":
                        switch (ipLocation.city.replace("市", "")) {
                            case "广州":
                                posdesc = "看小蛮腰,喝早茶了嘛~";
                                break;
                            case "深圳":
                                posdesc = "今天你逛商场了嘛~";
                                break;
                            case "阳江":
                                posdesc = "阳春合水!博主家乡~ 欢迎来玩~";
                                break;
                            default:
                                posdesc = "来两斤福建人~";
                                break;
                        }
                        break;
                    case "广西":
                        posdesc = "桂林山水甲天下";
                        break;
                    case "海南":
                        posdesc = "朝观日出逐白浪,夕看云起收霞光";
                        break;
                    case "四川":
                        posdesc = "康康川妹子";
                        break;
                    case "贵州":
                        posdesc = "茅台,学生,再塞200";
                        break;
                    case "云南":
                        posdesc = "玉龙飞舞云缠绕,万仞冰川直耸天";
                        break;
                    case "西藏":
                        posdesc = "躺在茫茫草原上,仰望蓝天";
                        break;
                    case "陕西":
                        posdesc = "来份臊子面加馍";
                        break;
                    case "甘肃":
                        switch (ipLocation.city.replace("市", "")) {
                            case "兰州":
                                posdesc = "来一碗兰州牛肉面🍝";
                                break;
                            case "武威":
                                posdesc = "羌笛何须怨杨柳,春风不度玉门关";
                                break;
                            case "天水":
                                posdesc = "大河之水天上来,奔流到海不复回";
                                break;
                            default:
                                posdesc = "来甘肃旅游吧~";
                                break;
                        }
                        break;
                    case "青海":
                        posdesc = "牛肉干和老酸奶都好好吃";
                        break;
                    case "宁夏":
                        posdesc = "大漠孤烟直,长河落日圆";
                        break;
                    case "新疆":
                        posdesc = "驼铃古道丝绸路,胡马犹闻唐汉风";
                        break;
                    case "台湾":
                        posdesc = "我在这头,大陆在那头";
                        break;
                    case "香港":
                        posdesc = "永定贼有残留地鬼嚎,迎击光非岁玉";
                        break;
                    case "澳门":
                        posdesc = "性感荷官,在线发牌";
                        break;
                    default:
                        posdesc = "带我去你的城市逛逛吧!";
                        break;
                }
                break;
            default:
                posdesc = "带我去你的国家逛逛吧";
                break;
        }

        //根据本地时间切换欢迎语
        let timeChange;
        let date = new Date();
        if (date.getHours() >= 5 && date.getHours() < 11) timeChange = "<span>🌤️ 早上好,一日之计在于晨</span>";
        else if (date.getHours() >= 11 && date.getHours() < 13) timeChange = "<span>☀️ 中午好,记得午休喔~</span>";
        else if (date.getHours() >= 13 && date.getHours() < 17) timeChange = "<span>🕞 下午好,饮茶先啦!</span>";
        else if (date.getHours() >= 17 && date.getHours() < 19) timeChange = "<span>🚶‍♂️ 即将下班,记得按时吃饭~</span>";
        else if (date.getHours() >= 19 && date.getHours() < 24) timeChange = "<span>🌙 晚上好,夜生活嗨起来!</span>";
        else timeChange = "夜深了,早点休息,少熬夜";

        // 新增ipv6显示为指定内容
        if ((ip + "").indexOf(":") != -1) {
            ip = "<br>好复杂,咱看不懂~(ipv6)";
        }
        try {
            //自定义文本和需要放的位置
            document.getElementById("welcome-info").innerHTML =
                `欢迎来自 <b><span style="color: var(--hao-ip-color);font-size: var(--hao-gl-size)">${pos}</span></b> 的朋友🧑‍🤝‍🧑<br>${posdesc}🍂<br>当前位置距博主约 <b><span style="color: var(--hao-ip-color)">${dist}</span></b> 公里!<br>您的IP地址为:<b><span>${ip}</span></b><br>${timeChange} <br>`;
        } catch (err) {
            console.log("Pjax无法获取元素");
            console.log("如果[侧边栏]设置中没有给本页添加 welcome 小部件,请忽略报错");
        }
    } else {
        try {
            //自定义文本和需要放的位置
            document.getElementById("welcome-info").innerHTML =
                `${ipLocation.message}`;
        } catch (err) {
            console.log("Pjax无法获取元素")
            console.log("如果[侧边栏]设置中没有给本页添加 welcome 小部件,请忽略报错");
        }

    }

}

至此,已经完成了小板报的修改。

六、明暗模式切换动画

关于动画切换魔改的效果有点问题,主要是渐变背景不起作用,因为想法都是从buterfully中迁移过来的,也没研究出什么结果的,但是最后的动画效果是有的,你可以在本站试一试。如果有能力的朋友自己可以研究一下,烦请不吝赐教。

1、增加动画文件

找到目录 /templates/modules/widgets/,在该目录下下新增 lightDarkAnimation.html 文件,本质上该文件是一个 svg 文件,其内容如下:

<th:block>
  <svg aria-hidden='true' style='position:absolute; overflow:hidden; width:0; height:0'>
    <symbol id="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>
      <path d="M736 512a224 224 0 1 0-448 0 224 224 0 1 0 448 0z" fill="#FFE4A9" p-id="8421"></path>
      <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>
      <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"></path>
    </symbol>
    <symbol id="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>
      <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"></path>
    </symbol>
  </svg>
</th:block>

2、新增 css 文件

/templates/assets/zhheo/ 下新增文件 switchDark.css 。内容如下:


/*明暗模式切换动画*/
.Cuteen_DarkSky, .Cuteen_DarkSky:before{
  content: '';
  position fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: 8;
}

.Cuteen_DarkSky{
  background-color: linear-gradient(#feb8b0, #fef9db);
}
.Cuteen_DarkSky:before{
    transition: 2s ease all;
    opacity: 0;
    background-image: linear-gradient(#4c3f6d, #6c62bb, #93b1ed);
}

.DarkMode .Cuteen_DarkSky:before{
      opacity: 1;
}

.Cuteen_DarkPlanet{
  z-index: 9;
  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:after{
    position: absolute;
    left: 35%;
    top: 40%;
    width: 9.375rem;
    height: 9.375rem;
    border-radius: 50%;
    content: '';
    background: linear-gradient(#fefefe, #fffbe8);
}

3、修改明暗模式切换 js 逻辑

找到 templates/assets/zhheo/blogex.js 文件,修改 switchDarkMode 方法。修改内容如下:

//深色模式切换
    switchDarkMode = () => {
        document.querySelector('body').insertAdjacentHTML('beforeend', 
            '<div class="Cuteen_DarkSky"><div class="Cuteen_DarkPlanet"></div></div>');
        setTimeout(function() {
             document.querySelector('body').classList.contains('DarkMode') ? (document.querySelector('body').classList.remove('DarkMode')): (localStorage.setItem('isDark', '0'));
             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);
        });
        "dark" === document.documentElement.getAttribute("data-theme") ? (activateLightMode(),
            saveToLocal.set("theme", "light", 2),
        void 0 !== GLOBAL_CONFIG.Snackbar && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day, false, 2000),
            $(".menu-darkmode-text").text("深色模式")) : (activateDarkMode(),
            saveToLocal.set("theme", "dark", 2),
        void 0 !== GLOBAL_CONFIG.Snackbar && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night, false, 2000),
            $(".menu-darkmode-text").text("浅色模式")),
            handleCases()
        heo.darkModeStatus();
        //代码块
        if (GLOBAL_CONFIG.prism.enable) {
            halo.dataCodeTheme();
        }
    }

4、引入 文件

找到 templates/modules/layouts/layout.html 文件,在head标签中引入css文件

 <!-- 引入明暗模式切换动画css样式 -->
    <link rel="stylesheet" th:href="${assets_link + '/zhheo/switchDark.css' + theme_version}" />

在此处(<div id="web_bg"></div>)引入以下内容

<!-- 网站背景 -->
<div id="web_bg"></div>
<!-- 引入明暗模式切换动画 -->
<th:block th:replace="~{modules/widgets/lightDarkAnimation}" />

至此,已经完成所有的设置,明暗动画可以正常切换了,渐变背景问题目前无法找到解决方案,可能与主题的某些 CSS 有冲突,希望大佬们可以指正。

七、给侧栏标签组件增加加载更多按钮

侧栏标签组件如果标签过多会导致内容篇幅过长,影响布局美观性。所以给标签侧边栏限制高度,增加一个加载更多按钮,这样在有查看全部标签需求的时候可以自行加载查看。整体效果如下:

card-side-tagcloud.png

具体的修改步骤如下:

1、增加按钮

找到 templates/modules/widgets/aside/contain/tags-contain.html 文件,粘贴如下内容覆盖原文件。

里边主要修改部分是:加入了加载更多按钮,以及增加一个方法 open_all_tags 。

<!-- 标签 -->
<th:block
          th:with="tags = ${tagFinder.listAll()}, tagQuantity = ${#conversions.convert(theme.config.sidebar.tagQuantity, 'java.lang.Integer')}">

    <div class="item-headline"></div>
    <div class="card-tag-cloud" id="aside-card-tags-cloudwb">
        <a class="tag-item" style="font-size:1em" th:each="tag,iterStat : ${tags}"
           th:href="@{${tag.status.permalink}}"
           th:if="${tagQuantity >= 0 && iterStat.index < tagQuantity || tagQuantity < 0}"
           th:title="${tag.spec.displayName}">
            <!-- 角标 -->
            [[${tag.spec.displayName}]]<sup th:text="${tag.status.visiblePostCount}"></sup>
        </a>
    </div>
    <a id="more-tags-btn" href="javascript:void(0);" rel="external nofollow" onclick="open_all_tags()"data-pjax-state="">加载更多</a>
    <script th:if="${theme.config.other.tagRandomColorEnable}">
        for (const tag of document.getElementsByClassName('tag-item')) {
            let randomColor ="#"+((1<<24)*Math.random()|0).toString(16);
            tag.style.color = randomColor;
        }
        function open_all_tags(){
            let cardCloud = document.querySelector("#aside-card-tags-cloudwb");
            cardCloud.style.height = "auto";
            document.querySelector("#more-tags-btn").style.display = "none";
        }
    </script>

</th:block>

2、增加 css 样式

找到 /templates/assets/zhheo/zhheoblog.css 文件,加入如下样式:

/* 标签云卡片 .card-widget .card-tags  */
.card-widget .card-tag-cloud {
    height: 300px;
    overflow: hidden;
}
a#more-tags-btn {
    width: 100%;
    text-align: center;
    background: var(--heo-secondbg);
    color: var(--heo-fontcolor);
    border-radius: 8px;
    display: flex;
    justify-content: center;
    font-size: 14px;
    user-select: none;
    padding: 4px 0;
    margin-top: 5px;
    border: var(--style-border-always);
    box-shadow: var(--heo-shadow-border);
}
a#more-tags-btn:hover{
    background-color: transparent;
    text-decoration: none;
    transition: all .3s ease-out 0s;
    overflow-wrap: break-word;
    -webkit-user-drag: none;
    background: var(--heo-theme);
}

八、字体以及鼠标样式修改

<!-- 字体 -->
<link rel="stylesheet" href="https://cdn.staticfile.net/lxgw-wenkai-webfont/1.7.0/style.css" />
<!-- 自定义的字体样式,可自行引入 -->
<link rel="stylesheet" href="/upload/font/result.css" />
<style>

/* 字体样式修改 */
body, #post-content, .code-toolbar{
    font-family: "LXGW WenKai",PingFang SC,Hiragino Sans GB,Droid Sans Fallback,Microsoft YaHei,sans-serif !important;
  font-weight: 545;
} 
/*鼠标样式修改*/
body {
     cursor: url(https://blog.wenjing.xin/upload/icon/mouse-new.png), default;
} 
</style>

全局注入代码

希望我的修改可以多大家有所帮助,后边会根据自己需求的变化会分享一些更加实用的魔改教程!