前言

本文教程主要针对 Hexo Butterfly 主题博客中的 Twikoo 评论,按本文逻辑或许可以获取其他类型的评论,本文不作探讨。

看了其他小伙伴的 评论热评教程,觉得有点复杂,于是自己想了一个方法来实现这个功能。

  • 沿用其他模块中用到的 swiper 轮播
  • 使用本地缓存保存 隐藏或显示 热评,刷新或切换页面时仍 隐藏或显示 热评
  • 增加评论的城市和日期
  • 页面提交评论后会立即更新热评数据
  • 点击 热评 可跳转至该评论

此功能需关闭评论的懒加载 _config.butterfly.yml => comments.lazyload: false,否则需等页面活动至评论区才会加载。

1
2
3
4
5
6
7
8
9
10
11
comments:
# Up to two comments system, the first will be shown as default
# Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42
use: # Valine,Disqus
- Twikoo
text: true # Display the comment name next to the button
# lazyload: The comment system will be load when comment element enters the browser's viewport.
# If you set it to true, the comment count will be invalid
lazyload: false
count: true # Display comment count in post's top_img
card_post_count: false # Display comment count in Home Page

修改 twikoo.pug

打开 Twikoo 评论文件 \themes\butterfly\layout\includes\third-party\comments\twikoo.pug,添加以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
- const { envId, region, option } = theme.twikoo
- const { use, lazyload, count } = theme.comments

script.
(()=>{
const init = () => {
twikoo.init(Object.assign({
el: '#twikoo-wrap',
envId: '!{envId}',
region: '!{region}',
onCommentLoaded: function () {
btf.loadLightbox(document.querySelectorAll('#twikoo .tk-content img:not(.tk-owo-emotion)'))
+ setTimeout(function(){
+ let comment = document.querySelectorAll('.tk-comments-container .tk-comment')
+ if (comment.length > 0) {
+ let tk_nick = document.querySelectorAll('.tk-comments-container .tk-comment .tk-nick')
+ let tk_avatar = document.querySelectorAll('.tk-comments-container .tk-comment .tk-avatar-img')
+ let tk_time = document.querySelectorAll('.tk-comments-container .tk-comment .tk-time')
+ let tk_city = document.querySelectorAll('.tk-comments-container .tk-comment .tk-extras .tk-extra:first-child span:last-child')
+ let tk_content = document.querySelectorAll('.tk-comments-container .tk-comment .tk-content>span:last-child')
+ let html = '<div class="swiper-wrapper">'
+ for (let i = 0; i < comment.length; i++) {
+ let content = tk_content[i].innerHTML
+ content = content.replace(/\n/g,'') // replace \n
+ content = content.replace(/<blockquote>.*?<\/blockquote>/gi, '') // replace blockquote
+ //- content = content.replace(/<a[^>]+?data-caption="image".*?<img.*?src="(.*?)"?[^\>]+><\/a>/ig, '[!{_p("aside.card_newest_comments.image")}]') // replace image link
+ //- content = content.replace(/<img.*?src="(.*?)"?[^\>]+>/ig, '[!{_p("aside.card_newest_comments.image")}]') // replace image link
+ //- content = content.replace(/<a[^>]+?href=["']?([^"']+)["']?[^>]*>([^<]+)<\/a>/gi, '[!{_p("aside.card_newest_comments.link")}]') // replace url
+ content = content.replace(/<pre.*?<\/pre>/gi, '[!{_p("aside.card_newest_comments.code")}]') // replace code
+ //- content = content.replace(/<[^>]+>/g, "") // remove html tag
+ html += `
+ <div class="swiper-slide">
+ <div class="comment-barrage-item">
+ <div class="barrage-info">
+ <a class="barrage-title" title="跳转至该评论" href="#${comment[i].getAttribute('id')}">热评</a>
+ <a href="${tk_nick[i].href ? tk_nick[i].href + '" target="_blank" rel="noopener noreferrer"': 'javascript:void(0);"'}><img class="barrage-avatar" src="${tk_avatar[i].src}"></a>
+ <span class="barrage-nick">${tk_nick[i].innerText}</span>
+ <span class="barrage-city">${tk_city[i].innerText}</span>
+ <span class="barrage-time">${tk_time[i].innerText}</span>
+ <a class="barrage-close" onclick="switchCommentBarrage()" title="隐藏热评"><i class="fa-solid fa-xmark"></i></a>
+ </div>
+ <div class="barrage-content">${content}</div>
+ </div>
+ </div>`
+ }
+ html += '</div>'
+ let barrageContainer = document.getElementById('comment-barrage') || document.createElement('div')
+ barrageContainer.id = 'comment-barrage'
+ barrageContainer.innerHTML = html
+ barrageContainer.style.zIndex = window.localStorage.getItem('commentBarrageDisplay') === 'false' ? '-1001' : '1'
+ document.getElementById('post-comment').appendChild(barrageContainer)
+ var barrageSwiper = new Swiper('#comment-barrage', {
+ direction: 'vertical',
+ loop: true,
+ mousewheel: true,
+ autoplay: {
+ delay: 3000,
+ disableOnInteraction: true,
+ }
+ })
+ barrageContainer.onmouseenter = function () {
+ barrageSwiper.autoplay.stop()
+ };
+ barrageContainer.onmouseleave = function () {
+ barrageSwiper.autoplay.start()
+ };
+ }
+ }, 1000)
}
}, !{JSON.stringify(option)}))
}

const getCount = () => {
const countELement = document.getElementById('twikoo-count')
if(!countELement) return
twikoo.getCommentsCount({
envId: '!{envId}',
region: '!{region}',
urls: [window.location.pathname],
includeReply: false
}).then(function (res) {
countELement.innerText = res[0].count
}).catch(function (err) {
console.error(err);
});
}

const runFn = () => {
init()
!{count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : ''}
}

const loadTwikoo = () => {
if (typeof twikoo === 'object') {
setTimeout(runFn,0)
return
}
getScript('!{url_for(theme.asset.twikoo)}').then(runFn)
}

if ('!{use[0]}' === 'Twikoo' || !!{lazyload}) {
if (!{lazyload}) btf.loadComment(document.getElementById('twikoo-wrap'), loadTwikoo)
else loadTwikoo()
} else {
window.loadOtherComment = () => {
loadTwikoo()
}
}
})()

增加自定义样式

增加自定义样式,下面 css 部分变量需自行修改,按 F12 自取。

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
/* 热评 */
#comment-barrage {
display: block;
position: fixed;
z-index: 1;
font-size: 90%;
bottom: 1rem;
right: 2rem;
width: 306px;
height: 150px;
overflow: hidden;
}

@media (max-width: 738px) {
#comment-barrage {
z-index: -1001 !important;
}
}

#comment-barrage .comment-barrage-item {
opacity: 0;
width: 300px;
height: fit-content;
max-height: 144px;
padding: 10px;
position: absolute;
margin: 0 3px;
bottom: 5px;
background: var(--card-bg);
border: var(--card-border);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
-webkit-transition: all .3s ease-in-out;
-moz-transition: all .3s ease-in-out;
-o-transition: all .3s ease-in-out;
-ms-transition: all .3s ease-in-out;
transition: all .3s ease-in-out;
}

#comment-barrage .swiper-slide-active .comment-barrage-item {
opacity: 1;
}

#comment-barrage .comment-barrage-item:hover {
border-color: var(--main) !important;
box-shadow: var(--main-shadow) !important;
}

.barrage-info {
overflow: hidden;
font-size: 90%;
height: 35px;
border-bottom: var(--card-border-dashed);
}

.barrage-info span {
margin: 0 .2em;
vertical-align: middle;
}

.barrage-title {
font-weight: bold;
padding: .3em 0.5em;
background: var(--font-color);
color: var(--card-bg);
margin-right: .5em;
border-radius: .5em;
vertical-align: middle;
}

.barrage-title:hover {
color: var(--second);
background: var(--main);
}

.barrage-avatar {
height: 2em;
width: 2em;
border-radius: 50%;
vertical-align: middle;
}

.barrage-nick {
font-weight: bold;
}

.barrage-close {
position: absolute;
top: 0;
right: 10px;
}

.barrage-content {
padding-top: 5px;
max-height: 95px;
height: fit-content;
overflow-y: scroll;
text-align: justify;
word-break: break-all;
}

.barrage-content p {
margin: 0;
}

.barrage-content img {
width: 3em;
}

增加 隐藏/显示 热评 js

1
2
3
4
5
6
7
// 切换热评
function switchCommentBarrage () {
let flag = window.localStorage.getItem('commentBarrageDisplay') // undefined || false
document.getElementById('comment-barrage').style.zIndex = flag === 'false' ? '1' : '-1001'
// 本地缓存一天,刷新或切换页面时仍 隐藏或显示 热评。
window.localStorage.setItem('commentBarrageDisplay', flag === 'false' ? 'undefined' : 'false', 86400000)
}

引入 swiper 轮播功能

修改 _config.butterfly.yml 文件

1
2
3
4
5
6
7
# Inject
# Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag)
# 插入代码到头部 </head> 之前 和 底部 </body> 之前
inject:
head:
- <link rel="stylesheet" href="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/Swiper/8.0.6/swiper-bundle.min.css"> # 轮播
- <script data-pjax src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/Swiper/8.0.6/swiper-bundle.min.js"></script> # 轮播

Hexo 三连

执行 Hexo 三连

1
hexo clean && hexo g && hexo s