











前置教程:
前往以下网站,不保证安全性哦喵~Hexo Butterfly博客魔改的一点点基础Ariasakaの小窝
最早这个留言弹幕是我直接找Dorakika抄的,见:
前往以下网站,不保证安全性哦喵~添加一个留言弹幕页面Dorakika
后来发现这个留言弹幕有一些问题:
所以这次我对这个弹幕进行了一些小的修改(其实是因为看到洪哥改的然后自己改了js),本来是适配waline的但是考虑到两个评论系统的数据结构基本相同所以都支持
ps:最近高产是因为这些东西都是以前的一下发出来而且受到糖果屋群友的鼓动导致
下面放出教程
本教程仅适用于Twikoo和Waline评论系统,其它系统请自行修改
首先修改一些pug:
修改 [blogRoot]/themes/butterfly/layout/includes/third-party/comments/index.pug
#post-comment
.comment-head
...
.comment-wrap
each name in theme.comments.use
div
case name
when 'Disqus'
#disqus_thread
...
+ .comment-barrage如果想要侧边栏控制显隐的话(当然有右键菜单的随便加不加),还要修改 [blogRoot]/themes/butterfly/layout/includes/rightside.pug:
....
when 'hideAside'
if aside.enable && aside.button && page.aside !== false
button#hide-aside-btn(type="button" title=_p('rightside.aside'))
i.fas.fa-arrows-alt-h
when 'toc'
if showToc
button#mobile-toc-button.close(type="button" title=_p("rightside.toc"))
i.fas.fa-list-ul
when 'chat'
if chat_btn
button#chat_btn(type="button" title=_p("rightside.chat"))
i.fas.fa-sms
when 'comment'
if commentsJsLoad
a#to_comment(href="#post-comment" title=_p("rightside.scroll_to_comment"))
i.fas.fa-comments
+ a#switch_commentBarrage(href="javascript:switchCommentBarrage();" title="开关弹幕")
+ i.iconfont.icon-danmu
#rightside
....然后你需要获取一个Token。
{% tabs %}
在开发人员工具-应用程序-本地存储-你的网址-twikoo-access-token里面即可看到,然后复制下面的值,待会用到。
在f12网络界面找到几个以web?env=cloudbase-baas-xxx开头的请求:
然后一个一个看,如果请求方式是options的话,就不管,找到请求方式是post的几个请求中的其中一个:
然后在负载里面复制那一行accesstoken的右半边双引号包起来的部分,比如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoie1wibG9naW5UeXBlXCI6XCJBTk9OWU1PVVNcIixcImVudk5hbWVcIjpcImNsb3VkYmFzZS1iYWFzLTRnZWd4NDAwZjdhZDc0OTFcIixcInV1aWRcIjpcImUyOWVkZDgwYzNhNzQzOTM5OGE1YmQ1NWZmMmJiZDA0XCJ9IiwiaWF0IjoxNjYzMzIzMTAwLCJleHAiOjE2NjMzMjY3MDB9.YR_NwdIzRZv6pNzEqQqK0ngz9dtDCRlSKmrZnNMwkcM;1145141919
现在你就获取到它了。
{% endtabs %}
twikoo和waline的js不一样,所以请根据自己的情况修改
{% tabs %}
新建 [blogRoot]/themes/butterfly/source/js/commentBarrage.js,然后按照注释修改配置:
const commentBarrageConfig = {
//浅色模式和深色模式颜色,务必保持一致长度,前面是背景颜色,后面是字体,随机选择,默认这个颜色还好
lightColors:[
['var(--lyx-white-acrylic2)','var(--lyx-black)'],
],
darkColors:[
['var(--lyx-black-acrylic2)','var(--lyx-white)'],
],
//同时最多显示弹幕数
maxBarrage: 1,
//弹幕显示间隔时间,单位ms
barrageTime: 3000,
//twikoo部署地址(Vercel、私有部署),腾讯云的为环境ID
twikooUrl: "https://tkapi.yaria.top",
//token获取见前文
accessToken: "{YOUR_TOKEN}",
pageUrl: window.location.pathname,
barrageTimer: [],
barrageList: [],
barrageIndex: 0,
// 没有设置过头像时返回的默认头像,见cravatar文档 https://cravatar.cn/developers/api,可以不改以免出错
noAvatarType: "retro",
dom: document.querySelector('.comment-barrage'),
//是否默认显示留言弹幕
displayBarrage: true,
//头像cdn,默认cravatar
avatarCDN: "cravatar.cn",
}
function isInViewPortOfOne (el) {
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
const offsetTop = el.offsetTop
const scrollTop = document.documentElement.scrollTop
const top = offsetTop - scrollTop
return top <= viewPortHeight
}
document.onscroll = function() {
if(commentBarrageConfig.displayBarrage){
if(isInViewPortOfOne(document.getElementById("post-comment"))){
document.getElementsByClassName("comment-barrage")[0].setAttribute("style",`display:none;`)
}
else{
document.getElementsByClassName("comment-barrage")[0].setAttribute("style","")
}
}
}
function initCommentBarrage(){
var data = JSON.stringify({
"event": "COMMENT_GET",
"commentBarrageConfig.accessToken": commentBarrageConfig.accessToken,
"url": commentBarrageConfig.pageUrl
});
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
commentBarrageConfig.barrageList = commentLinkFilter(JSON.parse(this.responseText).data);
commentBarrageConfig.dom.innerHTML = '';
}
});
xhr.open("POST", commentBarrageConfig.twikooUrl);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(data);
setInterval(()=>{
if(commentBarrageConfig.barrageList.length){
popCommentBarrage(commentBarrageConfig.barrageList[commentBarrageConfig.barrageIndex]);
commentBarrageConfig.barrageIndex += 1;
commentBarrageConfig.barrageIndex %= commentBarrageConfig.barrageList.length;
}
if(commentBarrageConfig.barrageTimer.length > (commentBarrageConfig.barrageList.length > commentBarrageConfig.maxBarrage?commentBarrageConfig.maxBarrage:commentBarrageConfig.barrageList.length)){
removeCommentBarrage(commentBarrageConfig.barrageTimer.shift())
}
},commentBarrageConfig.barrageTime)
}
function commentLinkFilter(data){
data.sort((a,b)=>{
return a.created - b.created;
})
let newData = [];
data.forEach(item=>{
newData.push(...getCommentReplies(item));
});
return newData;
}
function getCommentReplies(item){
if(item.replies){
let replies = [item];
item.replies.forEach(item=>{
replies.push(...getCommentReplies(item));
})
return replies;
}else{
return [];
}
}
function popCommentBarrage(data){
let barrage = document.createElement('div');
let width = commentBarrageConfig.dom.clientWidth;
let height = commentBarrageConfig.dom.clientHeight;
barrage.className = 'comment-barrage-item'
let ran = Math.floor(Math.random()*commentBarrageConfig.lightColors.length)
document.getElementById("barragesColor").innerHTML=`[data-theme='light'] .comment-barrage-item { background-color:${commentBarrageConfig.lightColors[ran][0]};color:${commentBarrageConfig.lightColors[ran][1]}}[data-theme='dark'] .comment-barrage-item{ background-color:${commentBarrageConfig.darkColors[ran][0]};color:${commentBarrageConfig.darkColors[ran][1]}}`;
barrage.innerHTML = `
<div class="barrageHead">
<img class="barrageAvatar" src="https://${commentBarrageConfig.avatarCDN}/avatar/${data.mailMd5}?d=${commentBarrageConfig.noAvatarType}"/>
<div class="barrageNick">${data.nick}</div>
<a href="javascript:switchCommentBarrage()" style="font-size:20px">×</a>
</div>
<div class="barrageContent">${data.comment}</div>
`
commentBarrageConfig.barrageTimer.push(barrage);
commentBarrageConfig.dom.append(barrage);
}
function removeCommentBarrage(barrage){
barrage.className = 'comment-barrage-item out';
if(commentBarrageConfig.maxBarrage!=1){
setTimeout(()=>{
commentBarrageConfig.dom.removeChild(barrage);
},1000)
}else{
commentBarrageConfig.dom.removeChild(barrage);
}
}
switchCommentBarrage = function () {
localStorage.setItem("isBarrageToggle",Number(!Number(localStorage.getItem("isBarrageToggle"))))
if(!isInViewPortOfOne(document.getElementById("post-comment"))){
commentBarrageConfig.displayBarrage=!(commentBarrageConfig.displayBarrage);
let commentBarrage = document.querySelector('.comment-barrage');
if (commentBarrage) {
$(commentBarrage).fadeToggle()
}
}
}
$(".comment-barrage").hover(function(){
clearInterval(timer);
},function () {
timer=setInterval(()=>{
if(commentBarrageConfig.barrageList.length){
popCommentBarrage(commentBarrageConfig.barrageList[commentBarrageConfig.barrageIndex]);
commentBarrageConfig.barrageIndex += 1;
commentBarrageConfig.barrageIndex %= commentBarrageConfig.barrageList.length;
}
if(commentBarrageConfig.barrageTimer.length > (commentBarrageConfig.barrageList.length > commentBarrageConfig.maxBarrage?commentBarrageConfig.maxBarrage:commentBarrageConfig.barrageList.length)){
removeCommentBarrage(commentBarrageConfig.barrageTimer.shift())
}
},commentBarrageConfig.barrageTime)
})
if(localStorage.getItem("isBarrageToggle")==undefined){
localStorage.setItem("isBarrageToggle","0");
}else if(localStorage.getItem("isBarrageToggle")=="1"){
localStorage.setItem("isBarrageToggle","0");
switchCommentBarrage()
}
initCommentBarrage()在主题配置文件中引入该文件和jquery以及一个css容器:
inject:
head:
- <style id="barragesColor"></style>
- ...
bottom:
- <script type="text/javascript" src="https://gcore.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script>
- <script type="text/javascript" src="/js/commentBarrage.js"></script>新建 [blogRoot]/themes/butterfly/source/js/commentBarrage.js,然后按照注释修改配置:
const commentBarrageConfig = {
//浅色模式和深色模式颜色,务必保持一致长度,前面是背景颜色,后面是字体,随机选择,默认这个颜色还好
lightColors:[
['var(--lyx-white-acrylic2)','var(--lyx-black)'],
],
darkColors:[
['var(--lyx-black-acrylic2)','var(--lyx-white)'],
],
//同时最多显示弹幕数
maxBarrage: 1,
//弹幕显示间隔时间,单位ms
barrageTime: 3000,
// 你的Leancloud APPID
lcAppId: "{YOUR_APPID}",
// 你的Leancloud APPKEY
lcAppKey: "{YOUR_APPKEY}",
// 你的Leancloud 域名
lcUrl:"https://your-url.leancloud.cn",
pageUrl: window.location.pathname,
barrageTimer: [],
barrageList: [],
barrageIndex: 0,
// 没有设置过头像时返回的默认头像,见cravatar文档 https://cravatar.cn/developers/api,可以不改以免出错
noAvatarType: "retro",
dom: document.querySelector('.comment-barrage'),
//是否默认显示留言弹幕
displayBarrage: true,
//头像cdn,默认cravatar
avatarCDN: "cravatar.cn"
}
function GetUrlRelativePath() {
var url = document.location.toString();
var arrUrl = url.split("//");
var start = arrUrl[1].indexOf("/");
var relUrl = arrUrl[1].substring(start);
if (relUrl.indexOf("?") != -1) {
relUrl = relUrl.split("?")[0];
}
return relUrl;
}
function isInViewPortOfOne (el) {
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
const offsetTop = el.offsetTop
const scrollTop = document.documentElement.scrollTop
const top = offsetTop - scrollTop
return top <= viewPortHeight
}
document.onscroll = function() {
if(commentBarrageConfig.displayBarrage){
if(isInViewPortOfOne(document.getElementById("post-comment"))){
document.getElementsByClassName("comment-barrage")[0].setAttribute("style",`display:none;`)
}
else{
document.getElementsByClassName("comment-barrage")[0].setAttribute("style","")
}
}
}
function initCommentBarrage(){
const { Query, User } = AV;
AV.init({
appId: commentBarrageConfig.lcAppId,
appKey: commentBarrageConfig.lcAppKey,
serverURL: commentBarrageConfig.lcUrl
});
const query = new AV.Query('Comment');
query.equalTo('url', GetUrlRelativePath());
query.find().then((comments) => {
console.log(comments)
for(var i=0;i<comments.length;i++){
comments[i]["attributes"]["mailMd5"]=md5(comments[i]["attributes"]["mail"])
commentBarrageConfig.barrageList.push(comments[i]["attributes"]);
}
});
setInterval(()=>{
if(commentBarrageConfig.barrageList.length){
popCommentBarrage(commentBarrageConfig.barrageList[commentBarrageConfig.barrageIndex]);
commentBarrageConfig.barrageIndex += 1;
commentBarrageConfig.barrageIndex %= commentBarrageConfig.barrageList.length;
}
if(commentBarrageConfig.barrageTimer.length > (commentBarrageConfig.barrageList.length > commentBarrageConfig.maxBarrage?commentBarrageConfig.maxBarrage:commentBarrageConfig.barrageList.length)){
removeCommentBarrage(commentBarrageConfig.barrageTimer.shift())
}
},commentBarrageConfig.barrageTime)
}
function commentLinkFilter(data){
data.sort((a,b)=>{
return a.created - b.created;
})
let newData = [];
data.forEach(item=>{
newData.push(...getCommentReplies(item));
});
return newData;
}
function getCommentReplies(item){
if(item.replies){
let replies = [item];
item.replies.forEach(item=>{
replies.push(...getCommentReplies(item));
})
return replies;
}else{
return [];
}
}
function popCommentBarrage(data){
let barrage = document.createElement('div');
let width = commentBarrageConfig.dom.clientWidth;
let height = commentBarrageConfig.dom.clientHeight;
barrage.className = 'comment-barrage-item'
let ran = Math.floor(Math.random()*commentBarrageConfig.lightColors.length)
document.getElementById("barragesColor").innerHTML=`[data-theme='light'] .comment-barrage-item { background-color:${commentBarrageConfig.lightColors[ran][0]};color:${commentBarrageConfig.lightColors[ran][1]}}[data-theme='dark'] .comment-barrage-item{ background-color:${commentBarrageConfig.darkColors[ran][0]};color:${commentBarrageConfig.darkColors[ran][1]}}`;
barrage.innerHTML = `
<div class="barrageHead">
<img class="barrageAvatar" src="https://${commentBarrageConfig.avatarCDN}/avatar/${data.mailMd5}?d=${commentBarrageConfig.noAvatarType}"/>
<div class="barrageNick">${data.nick}</div>
<a href="javascript:switchCommentBarrage()" style="font-size:20px">×</a>
</div>
<div class="barrageContent">${data.comment}</div>
`
commentBarrageConfig.barrageTimer.push(barrage);
commentBarrageConfig.dom.append(barrage);
}
function removeCommentBarrage(barrage){
barrage.className = 'comment-barrage-item out';
if(commentBarrageConfig.maxBarrage!=1){
setTimeout(()=>{
commentBarrageConfig.dom.removeChild(barrage);
},1000)
}else{
commentBarrageConfig.dom.removeChild(barrage);
}
}
switchCommentBarrage = function () {
if(!isInViewPortOfOne(document.getElementById("post-comment"))){
commentBarrageConfig.displayBarrage=!(commentBarrageConfig.displayBarrage);
let commentBarrage = document.querySelector('.comment-barrage');
if (commentBarrage) {
$(commentBarrage).fadeToggle()
}
}
}
$(".comment-barrage").hover(function(){
clearInterval(timer);
},function () {
timer=setInterval(()=>{
if(commentBarrageConfig.barrageList.length){
popCommentBarrage(commentBarrageConfig.barrageList[commentBarrageConfig.barrageIndex]);
commentBarrageConfig.barrageIndex += 1;
commentBarrageConfig.barrageIndex %= commentBarrageConfig.barrageList.length;
}
if(commentBarrageConfig.barrageTimer.length > (commentBarrageConfig.barrageList.length > commentBarrageConfig.maxBarrage?commentBarrageConfig.maxBarrage:commentBarrageConfig.barrageList.length)){
removeCommentBarrage(commentBarrageConfig.barrageTimer.shift())
}
},commentBarrageConfig.barrageTime)
})
initCommentBarrage()在主题配置文件中引入该文件和jquery、md5、leancloud api以及一个css容器:
inject:
head:
- <style id="barragesColor"></style>
- ...
bottom:
- <script type="text/javascript" src="https://gcore.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.min.js"></script>
- <script src="//gcore.jsdelivr.net/npm/leancloud-storage@4.13.1/dist/av-min.js"></script>
- <script type="text/javascript" src="/js/commentBarrage.js"></script>{% endtabs %}
然后编写css部分,由于默认配置有我的标准色,所以会一并放到css里面(只有部分,完整版自己扒),新建 [blogRoot]/themes/butterfly/source/css/commentBarrage.css
.comment-barrage {
position: fixed;
bottom: 0;
right: 55px;
padding: 0 0 30px 10px;
z-index: 100;
display: flex;
flex-direction: column;
justify-content: end;
align-items: flex-end;
}
@media screen and (max-width: 768px) {
.comment-barrage {
display: none!important;
}
}
.comment-barrage-item {
min-width: 150px;
max-width: 200px;
width: fit-content;
min-height: 80px;
max-height: 144px;
margin: 4px 0;
padding: 8px;
background: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(20px) saturate(180%);
border-radius: 8px;
color: #fff;
animation: barrageIn 0.3s cubic-bezier(.25, .01, .5, 1.5);
transition: 1s;
display: flex;
flex-direction: column;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.comment-barrage-item.out {
opacity: 0;
}
@keyframes barrageIn {
0% {
transform: scale(0.1);
}
,
100% {
transform: scale(1.0);
}
}
.comment-barrage-item .barrageHead {
height: 30px;
padding: 0;
line-height: 30px;
font-size: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
display: flex;
justify-content: space-between;
align-items: center;
}
.comment-barrage-item .barrageAvatar {
width: 16px;
height: 16px;
margin: 0;
border-radius: 50%;
}
.comment-barrage-item .barrageContent {
font-size: 14px;
height: calc(100% - 30px);
overflow: scroll;
}
.comment-barrage-item .barrageContent::-webkit-scrollbar {
height: 0;
width: 4px;
}
.comment-barrage-item .barrageContent::-webkit-scrollbar-button {
display: none;
}
:root{
--lyx-black:black;
--lyx-white:white;
--lyx-white-acrylic2:#fffa;
--lyx-black-acrylic2: #000a;
}2022-9-11更新:
调整了弹幕的min-width,如果感觉弹幕宽度太窄可以修改: ```diff ... .comment-barrage-item{
在主题配置文件中引入:
inject:
head:
- <link rel="stylesheet" href="/css/commentBarrage.css">
- ...
bottom:
- ...现在你就拥有了一个更好的留言弹幕了!
{% checkbox 支持Gitalk %}
Butterfly主题的留言弹幕界面增强版(支持Twikoo、Waline、Valine)
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。