配置Umami到about页

2024-10-02:添加CF Work 部署api方式,感谢@spark 提供的worker脚本。至此白嫖已成艺术

2024-08-16:更新文章 修复Hello Word部分的动画效果和countup加载动画效果

前言

昨天,51LA遭遇了DDoS攻击,导致昨天的数据全部丢失。对于一个数据一般般但还每天关注数据的人来说,这无疑接受不了。主要听说51LA投毒

部署方式

在寻找替代方案时,偶然发现了张洪大佬的Umami。Umami是一个开源的网站分析工具,界面简洁,功能全面。所以下面讲述本文的主角-Umami

Docker部署

需要服务器支持,速度较快

Vercel部署

无需服务器支持,速度一般

结束了?

我是非常支持vercel的所以就选择放在Vercel了(关键是免费)

按照教程,也是成功将Umami部署到了Vercel上。

Umami的界面确实简洁美观,数据是我本地测试的

修改页面

前提条件,你需要进行部署API,下面方式可以任选一个适合你的

方法一

需要服务器支持,速度较快

方法二

@spark 佬提供的方法在cf worker部署(无需服务器支持)

开始修改

如果你都搞定了,那么修改关于页面的正文开始了

themes/anzhiyu/layout/includes/head.pug 添加

1
2
3
4
5
6
//- Umami
if theme.Umami
if theme.Umami.umami_url
script(async defer src=`${theme.Umami.umami_url_js}` data-website-id=`${theme.Umami.umami_id}` data-host-url=`${theme.Umami.umami_url}`)
else
script(async defer src=`${theme.Umami.umami_url_js}` data-website-id=`${theme.Umami.umami_id}`)

然后修改 themes/anzhiyu/source/css/_page/about.styl

1
2
3
大致在1255
- if (hexo-config('LA.enable')) {
+ if (hexo-config('LA.enable') || hexo-config('Umami.enable')) {

接着修改 themes/anzhiyu/layout/includes/page/about.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
//-  应该是91行
.author-content
- if theme.LA.enable
+ if theme.LA.enable || theme.Umami.enable
- let cover = item.statistic.cover
.about-statistic.author-content-item(style=`background: url(${cover}) top / cover no-repeat;`)
.card-content
.author-content-item-tips 数据
span.author-content-item-title 访问统计
#statistic
- .post-tips
- | 统计信息来自
- a(href='https://invite.51.la/1NzKqTeb?target=V6', target='_blank', rel='noopener nofollow') 51la网站统计
+ if theme.LA.enable
+ .post-tips
+ | 统计信息来自
+ a(href='https://www.51.la/', target='_blank', rel='noopener nofollow') 51LA统计
+ else if theme.Umami.enable
+ .post-tips
+ | 统计信息来自
+ a(href='https://um.ruom.top', target='_blank', rel='noopener nofollow') Umami统计
.banner-button-group
- let link = item.statistic.link
- let text = item.statistic.text

继续修改 直接搜 - const ck = theme.LA.ck 把下面的全部替换

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
// 复制即是正常缩进(两个字符) 需要删除本行
//- Umami 统计 和 51LA 统计
if theme.Umami && theme.Umami.enable
script(defer).
(function() {
const umamiApiUrl = "#{url_for(theme.Umami.umami_api)}";
fetch(umamiApiUrl)
.then(res => res.json())
.then(data => {
let title = {
"today_uv": "今日人数",
"today_pv": "今日访问",
"yesterday_uv": "昨日人数",
"yesterday_pv": "昨日访问",
"last_month_pv": "本月访问",
"last_year_pv": "本年访问"
};
let s = document.getElementById("statistic");
for (let key in data) {
if (data.hasOwnProperty(key) && title[key]) {
s.innerHTML += `<div><span>${title[key]}</span><span id="${key}">${data[key]}</span></div>`;
}
}
initCountUp(data, title);
})
.catch(error => console.error('Error:', error));
})();
else
script(defer).
function initAboutPage() {
fetch("https://v6-widget.51.la/v6/#{ck}/quote.js")
.then(res => res.text())
.then(data => {
let title = ["最近活跃", "今日人数", "今日访问", "昨日人数", "昨日访问", "本月访问", "总访问量"];
let num = data.match(/(<\/span><span>).*?(\/span><\/p>)/g);

num = num.map(el => {
let val = el.replace(/(<\/span><span>)/g, "");
let str = val.replace(/(<\/span><\/p>)/g, "");
return str;
});

let statisticEl = document.getElementById("statistic");

// 自定义不显示哪个或者显示哪个,如下为不显示 最近活跃访客 和 总访问量
let statistic = [];
for (let i = 0; i < num.length; i++) {
if (!statisticEl) return;
if (i == 0) continue;
statisticEl.innerHTML +=
"<div><span>" + title[i] + "</span><span id=" + title[i] + ">" + num[i] + "</span></div>";
queueMicrotask(() => {
statistic.push(
new CountUp(title[i], 0, num[i], 0, 2, {
useEasing: true,
useGrouping: true,
separator: ",",
decimal: ".",
prefix: "",
suffix: "",
})
);
});
}

let statisticElement = document.querySelector(".about-statistic.author-content-item");
function statisticUP() {
if (!statisticElement) return;

const callback = (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
for (let i = 0; i < num.length; i++) {
if (i == 0) continue;
queueMicrotask(() => {
statistic[i - 1].start();
});
}
observer.disconnect(); // 停止观察元素,因为不再需要触发此回调
}
});
};

const options = {
root: null,
rootMargin: "0px",
threshold: 0
};
const observer = new IntersectionObserver(callback, options);
observer.observe(statisticElement);
}

statisticUP();
initCountUp({}, {});
});

initAnimation();
}
if (typeof gsap === "object") {
initAboutPage()
} else {
getScript("!{url_for(theme.asset.gsap_js)}").then(initAboutPage);
}

//- 初始化 countup.js
script(defer).
function initCountUp(data, title) {
const elements = [];

for (let key in data) {
if (data.hasOwnProperty(key) && title[key]) {
const element = document.getElementById(key);
if (element) {
elements.push({ id: key, value: data[key], element: element });
}
}
}

const selfInfoContentYearElement = document.getElementById("selfInfo-content-year");
if (selfInfoContentYearElement) {
elements.push({ id: "selfInfo-content-year", value: #{selfInfoContentYear}, element: selfInfoContentYearElement });
}

const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const target = elements.find(el => el.element === entry.target);
if (target) {
const countUp = new CountUp(target.id, 0, target.value, 0, 2, {
useEasing: true,
useGrouping: target.id === "selfInfo-content-year" ? false : true,
separator: ",",
decimal: ".",
prefix: "",
suffix: "",
});
countUp.start();
observer.unobserve(entry.target);
}
}
});
}, { threshold: 0 });

elements.forEach(el => observer.observe(el.element));
}

//- 独立鼠标跟随动画
script(defer).
function initAnimation() {
var pursuitInterval = null;
pursuitInterval = setInterval(function () {
const show = document.querySelector("span[data-show]");
const next = show.nextElementSibling || document.querySelector(".first-tips");
const up = document.querySelector("span[data-up]");

if (up) {
up.removeAttribute("data-up");
}

show.removeAttribute("data-show");
show.setAttribute("data-up", "");

next.setAttribute("data-show", "");
}, 2000);

document.addEventListener("pjax:send", function () {
pursuitInterval && clearInterval(pursuitInterval);
});

var helloAboutEl = document.querySelector(".hello-about");
helloAboutEl.addEventListener("mousemove", evt => {
const mouseX = evt.offsetX;
const mouseY = evt.offsetY;
gsap.set(".cursor", {
x: mouseX,
y: mouseY,
});

gsap.to(".shape", {
x: mouseX,
y: mouseY,
stagger: -0.1,
});
});
}
if (typeof gsap === "object") {
initAnimation()
} else {
getScript("!{url_for(theme.asset.gsap_js)}").then(initAnimation);
}

最后在主题的config.yml配置项内添加

1
2
3
4
5
6
7
# Umami
Umami:
enable: true # 开关
umami_url_js: https://um.ruom.top/script.js # 填写 umami js地址 可以使用第三方CDN加速但需要配置下面的 umami_url
umami_id: c19add88-59e1-4fa1-a406-09e64d2845f3 # 填写 umami 统计 ID
umami_api: https://umam-api.jlinmr.workers.dev/ # 填写 umami API 地址
umami_url: # https://um.ruom.top 填写 umami 服务器地址 使用 CDN 加速 Umami 静态资源后需配置此项

结束

数据不备份,魔改两行泪,操作本文需要一定基础。文章内外站链接不能保证其稳定性和连通性。