服务器更新但edgeone缓存没刷新解决方案

进入edgeone控制台点击清除缓存,选择站点,内容类型选择目录或者url,清除方法如果直接删除,就没有备份,如果是标记过期,当站点出现问题就可以回溯到上一个版本使用。输入内容为https://blog.dreamshelter.cn/然后确定清除就行了

live2d

对于live2d,ai告诉我一个比原版更聪明的看板娘,安装方法是:

方案二:利用 Butterfly 的 inject 功能(最优雅)

Butterfly 专门提供了一个 inject 配置项,允许你把代码插入到页面的特定位置,而不用去翻找复杂的 HTML 文件。

  1. 打开 _config.butterfly.yml
  2. 找到 inject: 部分,在 bottom: 下面添加那行脚本:
1
2
3
4
5
inject:
head:
# 可以在这里加 CSS
bottom:
- <script src="https://fastly.jsdelivr.net/gh/stevenjoezhang/live2d-widget@latest/autoload.js"></script>

看板娘

看板娘的这个我应该怎么变成右下角固定

如果你使用的是这个 autoload.js 脚本,它默认通常会出现在左下角。要将其固定到右下角,最直接的方法是通过 CSS 覆盖

由于这个脚本在运行时会自动往页面注入一个 ID 为 waifu(或者类似的容器)的 HTML 元素,我们可以直接在 Hexo 中添加样式来强制改变它的位置。

方案一:通过 Butterfly 的 Inject 功能(最推荐)

不需要改动 JS 脚本,直接在 _config.butterfly.yml 中添加一行 CSS 注入即可:

  1. 打开 _config.butterfly.yml
  2. 找到 inject -> head 部分。
  3. 添加以下代码:

YAML

1
2
3
4
5
inject:
head:
- <style>#waifu { left: auto !important; right: 0 !important; }</style>
bottom:
- <script src="https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0/dist/autoload.js"></script>

代码解释:

  • left: auto !important;:取消默认的左侧定位。
  • right: 0 !important;:将其固定到右侧。你可以把 0 改成 20px 让它离边缘有一点距离。

方案二:如果你使用的是本地文件

如果你已经把 waifu.css 下载到了本地(例如放在 source/live2d-widget/waifu.css):

  1. 打开该 waifu.css 文件。

  2. 搜索 #waifu

  3. 将其中的 left: 0; 修改为:

    CSS

    1
    2
    3
    4
    5
    #waifu {
    right: 20px; /* 距离右边 20 像素 */
    bottom: 0;
    /* 确保没有 left 属性,如果有,请删掉或改为 left: auto; */
    }

方案三:针对不同插件容器名的通用补救

如果你发现上面的 #waifu 没起作用,说明你这个版本的插件可能用了不同的 ID。你可以按以下步骤查找:

  1. 在浏览器打开你的博客,按 F12 打开开发者工具。
  2. 点击左上角的“小箭头”图标,去页面上点击那个看板娘。
  3. 查看右侧面板中该元素的 id 是什么(可能是 landlordlive2d-widget)。
  4. 将方案一代码中的 #waifu 替换为那个 ID 即可。

⚠️ 可能存在的冲突

由于你在右下角可能还有:

  1. Butterfly 的“回到顶部”按钮
  2. APlayer 播放器(如果你也把它设在了右边)。

建议: 如果右下角太挤了,你可以微调位置,比如: right: 50px !important; bottom: 20px !important; 这样看板娘会往左边和上方挪一点,避免挡住功能按钮。

改完后记得执行 hexo cleanhexo g。看板娘现在成功“瞬移”到右边了吗?

pwa

安装完后右上角会有个安装按钮,可以离线看博客,然后有缓存可以加快访问速度,右上角会有个通知博客更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
npm install --global gulp-cli #全局安装gulp命令集
npm install workbox-build gulp --save#安装workbox和gulp扬件

#压缩html插件
npm install gulp-htmlclean --save-dev
npm install --save gulp-htmlmin
#压缩css播件
npm install gulp-clean-css --save-dev
#压缩js播件
#使用babel压缩js,与terser二选一
npm install --save-dev gulpuglify
npm install --save-dev gulp-babel @babel/core @babel/preset-env
#使用terser压缩js,与babel二选一(推荐)
npm install gulp-terser --save-dev
npm install --save-dev gulp-babel @babel/core @babel/preset-env
# 压缩图片播件(没啥用)
npm install --save-dev gulp-imagcmin
#压缩字体播件(font-min仅支持压缩ttf格式的字体包)
npm install gulp-fontmin --save-dev

关于font-min的补充说明,在本文中,是通过读取所有编译好的html文件(./public/“.html)中的字符,然后匹配原有字体包内./public/fonts/.ttf字体样式,输出压缩后的字体包到 /public/fontsdest/目录。所以最终引用字体的相对路径应读是/fontsdes/.ttf。而本地测试时,如果没有运行gulp,自然也就不会输出压缩字体包到public目录,也就看不到字体样式。

julp-terser只会直接压缩js代码,所以不存在因为语法变动导致的错误。事实上,当我们使用jsdelivr的CDN服务时,只需要在css或者js的后级前添加加.min,例如example.js->example.min.js,JsDelivr就会自动使用terser帮我们压缩好代码,

在package.json添加

1
"type":"module",

创建gulpfile.js,在hexo的根目录创建这个文件

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
import gulp from 'gulp';
import cleanCSS from 'gulp-clean-css';
import htmlmin from 'gulp-htmlmin';
import htmlclean from 'gulp-htmlclean';
// import imagemin from 'gulp-imagemin';
import workbox from 'workbox-build';
import fontmin from 'gulp-fontmin';

//若使用babel压缩js,则取消下方注释,并注释terser的代码
// var uglify = require('gulp-uglify');
// var babel = require('gulp-babel');

//若使用terser压缩js
import terser from 'gulp-terser';

//pwa
gulp.task('generate-service-worker', ()=> {
return workbox.injectManifest({
swSrc: './sw-template.js',
swDest: './public/sw.js',
globDirectory: './public',
globPatterns: [
// 缓存所有以下类型的文件,极端不推荐
// "**/*.{html,css,js,json,woff2,xml}'
// 推荐只缓存404,主页和主要样式和脚本,
'404.html',
'index.html',
'js/main.js',
'css/index.css',
],
modifyURLPrefix: {
'': './',
},
});
});

//minify js babel
// 若使用babel压缩js,取消下方注释,并注释terser的代码
// gulp.task('compress',()=>
// gulp.src(['./public/**/*.js','!./public/*e*/*.min.js')
// .pipe(babel({
// 888888963
// presets: ['@babel/preset-env']
// }))
// .pipe(uglify().on('error', function(e){
// console.log(e);
// }))
// .pipe(gulp.dest('./public'))
// );

// minify js - gulp-tester
// 若使用terser压编js
gulp.task('compress', ()=>
gulp
.src([
'./public/**/*.js',
'!./public/**/*.min.js',
'!./public/js/custom/galmenu.js',
'!./public/js/custom/gitcalendar.js',
])
.pipe(terser())
.pipe(gulp.dest('./public'))
);

//css
gulp.task('minify-css', () => {
return gulp
.src('./public/**/*.css')
.pipe(
cleanCSS({
compatibility: 'ie11',
})
)
.pipe(gulp.dest('./public'));
});

//压缩字体
function minifyFont(text, cb) {
gulp
.src('./public/fonts/*.ttf')//原字体所在目录
.pipe(
fontmin({
text: text,
})
)
.pipe(gulp.dest('./public/fonts/'))//压缩后的输出目录
.on('end', cb);
}

gulp.task('mini-font', cb => {
var buffers = [];
gulp
.src(['./public/**/*.html'])//HTML文件所在目录请根据自身情况修改
.on('data', function (file){
// 将读取到的 HTML 文件内容存入数组
buffers.push(file.contents);
})
.on('end', function () {
// 1. 将所有 HTML 的 Buffer 合并,并转为字符串
var text = Buffer.concat(buffers).toString('utf-8');

// 2. 这里的逻辑是安知鱼/大部分教程的思路:
// 将全站文字进行去重处理,减少 fontmin 的负担
var uniqueText = "";
for (var i = 0; i < text.length; i++) {
if (uniqueText.indexOf(text[i]) === -1) {
uniqueText += text[i];
}
}
// 3. 调用上面定义的压缩函数,传入提取到的“字符全集”
minifyFont(uniqueText, cb);
});
});

// 压缩 public 目录内 html
gulp.task('minify-html', () => {
return gulp
.src('./public/**/*.html')
.pipe(htmlclean())
.pipe(
htmlmin({
removeComments:true,//清除 HTML 註释
collapseWhitespace:true,//压缩HTML
collapseBooleanAttributes: true,//省略布尔属性的值 <input checked="true"/> ==> <input />
removeEmptyAttributes:true,//删除所有空格作属性值<input id=""/>==> <input />
removeScriptTypeAttributes: true,//删除 <script>的 type="text/javascript"
removeStyleLinkTypeAttributes: true, //删除 <style>和<Link> 的 ype="text/css"
minifyJS: true,//压缩页面 JS
minifyCSS: true,//压缩页面CS5
minifyURLs: true,
})
)
.pipe(gulp.dest('./public'));
});

// 压缩 public/uploads 目录内图片
// gulp.task('minify-images', async() => {
// gulp
// .src('./public/img/**/*.*')
// .pipe(
// imagemin({
// optimizationLevel: 5,//类型:Number 预设: 3 取值範围:0-7(优化等级)
// progressive: true,//类型:Boolean 预设: false无失真压缩jpg图片
// interlaced: false,//类型:Boolean 预设: false 隔行扫描gif进行渲染
// multipass: false,//类型:Boolean 预设: false 多次优化svg直到完全优化
// })
// )
// .pipe(gulp.dest('./public/img'));
// });

//执行 gulp 命令时执行的任务
gulp.task(
'default',
gulp.series(
'generate-service-worker',
gulp.parallel('compress', 'minify-html', 'minify-css', 'mini-font'))
);

创建sw-template.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
56
57
58
59
60
61
62
63
64
const workboxVersion = '5.1.3';

// 引入 Workbox
importScripts(`https://storage.googleapis.com/workbox-cdn/releases/${workboxVersion}/workbox-sw.js`);

// 设置缓存名称前缀
workbox.core.setCacheNameDetails({
prefix:'shelter',
});

// 强制等待激活与更新
workbox.core.skipWaiting();
workbox.core.clientsClaim();

// 注册成功后要立即缓存的资源列表
// 具体缓存列表在gulpfile.js中配置,见下文
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST,{
directoryIndex: null,
});

// 清空过期缓存
workbox.precaching.cleanupOutdatedCaches();

// 2. 字体缓存 (修复了你代码中的正则错误)
workbox.routing.registerRoute(
/\.(?:eot|ttf|woff|woff2)$/,
new workbox.strategies.CacheFirst({
cacheName: 'fonts',
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 1000,
maxAgeSeconds: 60 * 60 * 24 * 30, // 缓存 30 天
}),
new workbox.cacheableResponse.CacheableResponsePlugin({
statuses: [0, 200],
}),
],
})
);

// 3. 图片缓存 (安知鱼博客通常包含大量图片)
workbox.routing.registerRoute(
/\.(?:png|jpg|jpeg|svg|gif|webp)$/,
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'images',
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 500,
maxAgeSeconds: 60 * 60 * 24 * 30,
}),
],
})
);

// 4. 谷歌字体缓存
workbox.routing.registerRoute(
/^https:\/\/fonts\.googleapis\.com/,
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'google-fonts-stylesheets',
})
);

// 5. 谷歌统计 (如果你使用了 Google Analytics)
workbox.googleAnalytics.initialize();

在[Blogroot]\themes\butterfly\layout\includes\third-party\目录下新建pwanotice.pug文件,打开 [Blogroot]\themes\butterfly\layout\includes\third-party\pwanotice.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
#app-refresh(style='position: fixed; top: -2.2rem; left: 0; right: 0; z-index: 99999; padding: 0 1rem; font-size: 15px; height: 2.2rem; transition: all 0.3s ease;')
.app-refresh-wrap(style='display: flex; color: #fff; height: 100%; align-items: center; justify-content: center;')
label 🚀 有新内容发布啦!
a(href='javascript:void(0)' onclick='location.reload()')
span(style='color: #fff; text-decoration: underline; cursor: pointer; margin-left: 8px;') 点击刷新
script.
if ('serviceWorker' in navigator) {
// 如果当前页面受 Service Worker 控制,监听控制权变化
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.addEventListener('controllerchange', function() {
showNotification();
});
}

// 页面加载完成后注册 sw.js
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').catch(err => {
console.error('PWA 注册失败:', err);
});
});
}

function showNotification() {
// 优先使用主题自带的 Snackbar 弹窗
if (window.GLOBAL_CONFIG && GLOBAL_CONFIG.Snackbar) {
var snackbarBg = document.documentElement.getAttribute('data-theme') === 'light' ?
GLOBAL_CONFIG.Snackbar.bgLight :
GLOBAL_CONFIG.Snackbar.bgDark;

var snackbarPos = GLOBAL_CONFIG.Snackbar.position;

Snackbar.show({
text: '🚀 有新文章发布啦!',
backgroundColor: snackbarBg,
duration: 10000, // 持续 10 秒
pos: snackbarPos,
actionText: '点击刷新',
actionTextColor: '#fff',
onActionClick: function(e) {
location.reload();
},
});
} else {
// 如果没有 Snackbar,则显示顶部的 app-refresh 栏
var showBg = document.documentElement.getAttribute('data-theme') === 'light' ? '#49b1f5' : '#1f1f1f';
var appRefresh = document.getElementById('app-refresh');
if (appRefresh) {
appRefresh.style.cssText = 'top: 0; background: ' + showBg + ';';
}
}
}

修改[Blogroot]\themes\butterfly\layout\includes\additional-js.pug
,在文件底部添加以下内容,注意缩进。 butterfly_v3.6.0取消了缓存配置,转为完全默认,需要将{cache:theme.fragment_cache} 改为{cache: true},现在使用的是高版本,使用最后两行就行,注意pug是严格缩进模式,第一行一定是一个空格,第二行两个

1
2
3
4
5
if theme.pjax.enable
!=partial('includes/third-party/pjax',{},{cache:theme.fragment_cache})
!=partial('includes/third-party/baidu_push', {},{cache:theme.fragment_cache})
if theme.pwa.enable
!= partial('includes/third-party/pwanotice', {} , {cache: true})

将你的图标包移入相应的目录,例如我是(img/siteicon/,所以放到[Blogroot]/source/img/siteicon/目录下。没有的话新建,site icon就是站点图标,可以去阿里的iconfont网站找资源

新建文件名为 manifest.json 并将其放到 [Blogroot]/source 目录下,此时还不能直接用,需要添加一些内容,以下是我的 manifest.json 配置内容,权且作为参考,其中的 theme_color 建议用取色器取设计的图标的主色调,同时务必配置 start_url和 name的配置项,这关系到你之后能否看到浏览器的应用安装按钮。

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
{
"name":"Shelter的博客",
"short_name": "Shelter",
"theme_color": "#49b1f5",
"background _color": "#49b1f5",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [{
"src":"/img/siteicon/16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "/img/siteicon/32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/img/siteicon/64.png",
"sizes": "64x64",
"type": "image/png"
},
{
"src":"/img/siteicon/128.png",
"sizes":"128x128",
"type": "image/png"
},
{
"src":"/img/siteicon/144.png",
"sizes":"144x144",
"type":"image/png"
},
{
"src":"/img/siteicon/512.png",
"sizes":"512x512",
"type": "image/png"
}
],
"splash_pages": null
}

打开主题配置文件 [Blogroot]/_config.butterfly.yml,找到 PWA 配置项。添加图标路径。这里的 theme_color,建议改成你图标的主色调,包括 manifest.json 中的 theme_color 也是如此。

1
2
3
4
5
6
7
8
pwa:
enable: true
manifest: /manifest.json
theme_color: "#49b1f5",
apple_touch_icon: /img/siteicon/128.png
favicon_32_32: /img/siteicon/32/png
favicon_16_16: /img/siteicon/16.png
mask_icon: /img/siteicon/128.png

一图流

新建文件[Blogroot]/source/css/custom.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
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/* font-face {
font-family: Candyhome;
src:url(https://cdn.cbd.int/anzhiyu-blog@latest/fonts/Candyhome.ttf);
font-display: swap;
font-weight: lighter;
} */
/* 谷歌字体 */
@import url(https://fonts.googleapis.com/css2?family=ZCOOL+KuaiLe&display=swap);
@font-face {
font-family: ZhuZiAYuanJWD;
src: url('https://npm.elemecdn.com/anzhiyu-theme-static@1.0.0/fonts/ZhuZiAWan.woff2') format('woff2');
font-display: swap;
font-weight: lighter;
}


/* 这里应该是站点最上面那个字体 */
div#menus {
font-family:'ZhuZiAYuanJWD';
}
h1#site-title {
font-family: ZhuZiAYuanJWD;
font-size: 3em !important;
}
a.article-title,
a.blog-slider__title,
a.categoryBar-list-link,
h1.post-title {
font-family: ZhuZiAYuanJWD;
}

.iconfont {
font-family:'iconfont' !important;
font-size: 3em;
/*可以定义图标大小 */
font-style: normal;
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
}

/*时间轴生肖icon */
svg.icon {
/* 这里定义svg.icon,避免和Butterfly自带的note标签冲突 */
width: 1em;
height: 1em;
/* width和height定义图标的默认宽度和高度*/
vertical-align:-0.15em;
fill: currentColor;
overflow: hidden;
}
.icon-zhongbiao::before {
color: #f7c768;
}

/* bilibli番剧插件 */
.bangumi-active {
background:#dbecfe !important;
border-radius: 10px !important;
}
a.bangumi-tab:hover {
text-decoration: none !important;
}
.bangumi-button:hover {
background:#dbecfe !important;
border-radius: 10px !important;
}
a.bangumi-button.bangumi-nextpage:hover {
text-decoration: none !important;
}
.bangumi-button {
padding: 5px 10px !important;
}
a.bangumi-tab {
padding: 5px 10px !important;
}
svg.icon.faa-tada {
font-size: 1.1em;
}

/* 解决artitalk的图标问题 */
#uploadSource > svg {
width: 1.19em;
height: 1.5em;
}

/*top-img黑色透明玻璃效果移除,不建议加,除非你执着于完全一图流或者背景图对比色明显 */
#page-header:not(.not-top-img):before {
background-color: transparent !important;
}

/* 首页文章卡片 */
#recent-posts > .recent-post-item {
background:rgba(255,255,255,0.9);
}

/* 首页侧栏卡片 */
#aside-content .card-widget {
background:rgba(255,255,255,0.9);
}

/* 文章页面正文背景 **/
div#post {
background:rgba(255,255,255,0.9);
}

/*分页页面 */
div#page {
background:rgba(255,255,255,0.9);
}

/* 归档页面 */
div#archive {
background:■rgba(255,255,255,0.9);
}

/* 标签页面 */
div#tag {
background:rgba(255,255,255,0.9);
}

/*分类页面 */
div#category {
background:rgba(255,255,255,0.9);
}

/*夜间模式伪类遮罩层透明*/
[data-theme='dark']#recent-posts > .recent-post-item {
background: #121212;
}

[data-theme='dark'] .card-widget {
background: #121212 !important;
}

[data-theme='dark'] div#post {
background: #121212 !important;
}

[data-theme='dark'] div#tag {
background: #121212 !important;
}

[data-theme='dark'] div#archive {
background: #121212 !important;
}

[data-theme='dark'] div#page {
background: #121212 !important;
}

[data-theme='dark'] div#category {
background: #121212 !important;
}

[data-theme='dark'] div#category {
background: transparent !important;
}

/* 页脚透明 */
#footer {
background: transparent !important;
}

/* 头图透明 */
#page-header {
background: transparent !important;
}

#rightside > div > button {
border-radius: 5px;
}

/* 滚动条的一些设置 */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}

::-webkit-scrollbar-thumb {
background-color: #49b1f5;
border-radius: 2em;
}

::-webkit-scrollbar-corner {
background-color: transparent;
}

::-moz-selection {
color: ■#fff;
background-color: #49b1f5;
}

/* 音乐播放器 */

/* .aplayer .aplayer-lrc {
display: none !important;
}*/

.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
left: -66px !important;
transition: all 0.3s;
/* 默认情况下缩进左侧66px,只留一点箭头部分 */
}

.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
left: 0 !important;
transition: all 0.3s;
/* 鼠标悬停是左侧缩进归零,完全显示按钮 */
}

.aplayer.aplayer-fixed {
z-index: 999999 !important;
}

/* 评论框
.vwrap {
box-shadow:2px2px5px #bbb;
background:口rgba(255,255,255,0.3);
border-radius: 8px;
padding: 30px;
margin:30px 0px 30px px;
}

/* 设置评论框 */

.vcard {
box-shadow: 2px 2px 5px #bbb;
background: rgba(255,255,255,0.3);
border-radius: 8px;
padding: 30px;
margin: 30px 0px 0px 0px;
}

/* 鼠标图标 */
body {
cursor: url('/img/x1.cur'), auto;
}
a,
[type='button']:not(:disabled),
[type='reset']:not(:disabled),
[type='submit']:not(:disabled),
button:not(:disabled) {
cursor: url('/img/x2.cur'), auto !important;
}
/* md网站下划线 */
#article-container a:hover {
text-decoration: none !important;
}

#article-container #hpp_talk p img {
display: inline;
}

/* 404页面 */
#error-wrap {
position: absolute;
top: 40%;
right: 0;
left: 0;
margin: 0 auto;
padding:0 1rem;
max-width: 1000px;
transform: translate(0, -50%);
}

#error-wrap .error-content {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin:0 1rem;
height: 18rem;
border-radius:8px;
background: var(--card-bg);
box-shadow: var(--card-box-shadow);
transition: all 0.3s;
}
#error-wrap .error-content .error-img {
box-flex: 1;
flex: 1;
height: 100%;
border-top-left-radius: 8px;
border-bottom-left-radius:8px;
background-color: 口#49b1f5;
background-position:center;
background-size: cover;
}

#error-wrap .error-content .error-info {
box-flex: 1;
flex: 1;
padding: 0.5rem;
text-align: center;
font-size: 14px;
font-family: Titillium Web, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft JhengHei', 'Microsoft YaHei',
sans-serif;
}

#error-wrap .error-content .error-info .error_title {
margin-top:-4rem;
font-size: 9em;
}
#error-wrap .error-content .error-info .error_subtitle {
margin-top:-3.5rem;
word-break: break-word;
font-size: 1.6em;
}
#error-wrap .error-content .error-info a {
display: inline-block;
margin-top: 0.5rem;
padding: 0.3rem 1.5rem;
background: var(--btn-bg);
color: var(--btn-color);
}

#body-wrap.error .aside-list {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
bottom: 0px;
position: absolute;
padding: 1rem;
width: 100%;
overflow: scroll;
}
#body-wrap.error .aside-list .aside-list-group {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
max-width:1200px;
margin: 0 auto;
}
#body-wrap.error .aside-list .aside-list-item {
padding: 0.5rem;
}
#body-wrap.error .aside-list .aside-list-item img {
width: 100%;
object-fit: cover;
border-radius:12px;
}

#body-wrap.error .aside-list .aside-list-item .thumbnail {
overflow: hidden;
width: 230px;
height: 143px;
background: var(--heo-card-bg);
display: flex;
}
#body-wrap.error .aside-list .aside-list-item .content .title {
-webkit-line-clamp:2;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
line-height: 1.5;
justify-content: center;
align-items: flex-end;
align-content:center;
padding-top: 0.5rem;
color: white;
}
#body-wrap.error .aside-list .aside-list-item .content time {
display: none;
}
/* 代码框主题 */
#article-container figure.highlight {
border-radius:10px;
}

/* 1. 外层容器:设置【不透明】边框 */
#aside-content .card-clock {
background: transparent !important; /* 必须透明,否则会遮挡背景图 */
border: 2px solid #ffffff !important; /* 这里是你的不透明边框,颜色可改 */
border-radius: 12px !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
overflow: hidden; /* 确保内部半透明层不超出边框 */
}

/* 2. 内层背景:保持【半透明】和毛玻璃效果 */
#aside-content .card-clock .card-background {
background-color: rgba(255, 255, 255, 0.4) !important;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
margin: 0 !important; /* 填满外层边框内部 */
border-radius: 0 !important; /* 边框由外层容器提供,内层无需重复圆角 */
box-shadow: inset 0 0 6px rgba(255, 255, 255, 0.2) !important;
}

/* 3. 夜间模式适配 */
[data-theme='dark'] #aside-content .card-clock {
border: 2px solid #333333 !important; /* 暗色模式下的不透明边框 */
}

[data-theme='dark'] #aside-content .card-clock .card-background {
background-color: rgba(0, 0, 0, 0.3) !important;
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.2) !important;
}

/* 4. 字体颜色修正 */
#aside-content .card-clock .clock-row {
color: var(--font-color) !important;
text-shadow: 0 0 5px rgba(255, 255, 255, 0.5);
}

不需要一图流就删除下面代码

1
2
3
4
5
6
7
8
9
/* 页脚透明 */
#footer {
background: transparent !important;
}

/* 头图透明 */
#page-header {
background: transparent !important;
}

然后在butterfly配置文件里找到inject添加下面代码

1
2
3
4
5
inject:
head:
# - <link rel="stylesheet" href="/xxx.css">
# 自定义css
- <link rel="stylesheet" href="/css/custom.css" media="defer" onload="this.media='all'">

然后在这个配置文件里找background添加路径

1
background: /img/background.png

如果本地部署没问题但是服务器有问题,就在config里设置url,设置成自己的github.io

image-20260203005249715

字数统计

安装插件:在你的博客根目录,打开cmd命令窗口执行npm install hexo-wordcount --save

开启配置:修改主题配置文件_config.butterfly.yml中的wordcount

1
2
3
4
5
wordcount:
enable: true
post_wordcount: true
min2read: true
total_wordcount: true

留言板

下载好插件然后在_config.butterfly.yml粘贴这些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# envelope_comment
# see https://akilar.top/posts/e2d3c450/
envelope_comment:
enable: true #控制开关
custom_pic:
cover: /img/envelope-cover.png #信笺头部图片
line: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png #信笺底部图片
beforeimg: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png # 信封前半部分
afterimg: https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png # 信封后半部分
message: #信笺正文,多行文本,写法如下
- 有什么想问的?
- 有什么想说的?
- 有什么想吐槽的?
- 哪怕是有什么想吃的,都可以告诉我哦~
bottom: 自动书记人偶竭诚为您服务! #仅支持单行文本
height: 950px #1050px,信封划出的高度
path: #【可选】comments 的路径名称。默认为 comments,生成的页面为 comments/index.html
front_matter: #【可选】comments页面的 front_matter 配置
title: 留言板
comments: true

github action自动部署

在github的设置里选择Developer Settings,然后点击Personal access tokens,选Tokens (classic),Generate new token(classic)新建一个,他会给个token,这个要复制下来,然后再新建个私人仓库,名字随意,放上博客源码

在博客文件里的.github新建一个workflows文件夹,新建一个autodeploy.yml文件,里面输入以下配置

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
name: 自动部署
# 当有改动推送到master分支时,启动Action
on:
push:
branches:
- main
#2020年10月后github新建仓库默认分支改为main,注意更改
release:
types:
- published
jobs :
deploy:
runs-on: ubuntu-latest
steps:
- name: 检查分支
uses: actions/checkout@v2
with:
ref: main

- name: 安装 Node
uses: actions/setup-node@v1
with:
node-version: '12.x'

- name: 安装 Hexo
run: |
export TZ='Asia/Shanghai'
npm install hexo-cli -g

- name: 缓存 Hexo
uses: actions/cache@v1
id: cache
with:
path: node modules
key: ${{runner.0S}}-${{hashFiles('**/package-lock.json')}}

- name: 安装依赖
if: steps.cache.outputs.cache-hit != 'true'
run: |
npm install gulp-cli -g #全局安装gulp
npm install --save

- name: 生成静态文件
run: |
hexo clean
hexo bangumi -u #bilibili番剧更新
hexo generate
gulp

- name: 部署到Github
uses: JamesIves/github-pages-deploy-action@v4
with:
token: # token换上自己的
repository-name: Shelter67/Shelter67.github.io

folder: public
commit-message: '${{ github,event.head_commit.message }} $(date +"%Z %Y-%m-%d %A %H:%M:%$") Updated By Github Actions'

然后在.gitignore里输入以下内容

1
2
3
4
5
6
7
8
9
10
11
.DS_Store
Thumbs.db
db.json
*.log
node_modules/
public/
.deploy*/
.deploy_git*/
.idea
themes/butterfly/.git
_multiconfig.yml

然后在butterfly文件夹里有个.git文件夹(隐藏的),可以给删掉

然后在博客根目录输入以下指令

1
2
3
git init
git remote add origin git@github-shelter67:Shelter67/hexo-SourceCode-Repo.git #这里后面的链接是新创建的源码仓库的ssh
git checkout -b main#切换到main分支

然后再运行git提交指令

1
2
3
git add .
git commit -m "github action update"
git push origin main

查看提交情况要进入到源码仓库,然后点击action,可以看到一个自动部署的东西,点进去点deploy就可以看到正在执行的hexo缓存

电子钟

下面安知鱼这些已过期

先卸载店长的侧边栏电子钟

1
npm uninstall hexo-butterfly-clock

然后安装安知鱼的

1
npm install hexo-butterfly-clock-anzhiyu --save

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# electric clock
# see https://anzhiy.cn/posts/fc18.html
electric_clock:
enable: true #开关
priority: 5 #过滤器优先权
enable_page: all #应用页面
exclude:
#-/posts/
#- /about/
layout: # 挂载容器类型
type: class
name: sticky_layout
index: 0
loading: https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu@1.0.5/lib/loading.gif #加载动画自定义
clock_css: https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu@1.0.5/lib/clock.min.css
clock_js: https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu@1.0.5/lib/clock.min.js
ip_api: https://widget.qweather.net/simple/static/js/he-simple-common.js?v=2.0
qweather_key: # 和风天气key
gaud_map_key: #高德地图web服务key

和风天气key:https://id.qweather.com/#/login

进入后点击开发服务控制台

高德地图:https://lbs.amap.com/,进入控制台,点击我的应用,然后创建,名称随意,类型选其他,点击添加key,名称随意,服务平台选web服务,然后提交

新版电子钟

近期,和风天气官方彻底关闭了旧版简易天气接口:

1
https://widget.qweather.net/simple/static/js/he-simple-common.js?v=2.0

为了解决时钟组件完全不可用的问题,将旧方案彻底移除,并迁移了新的插件:

hexo-butterfly-clock-remake,整体视觉和功能与原版一致,同时使用了和风天气最新接口,兼容性与稳定性更好

地址:https://github.com/hoochanlon/hexo-butterfly-clock-remake

一、卸载旧插件

如果你使用过旧版时钟方案,务必先卸载它们

1
2
3
4
npm uninstall hexo-butterfly-clock
npm uninstall hexo-butterfly-clock-anzhiyu
npm uninstall hexo-butterfly-clock-anzhiyu-yang
npm uninstall hexo-butterfly-clock-remake

二、安装新版插件

1
npm install hexo-butterfly-clock-veeink

注意:
该插件依赖 和风天气开发者 API。你需要前往注册并获取自己的:

  • API Host(需手动在前面加 https://注意这里,作者并没有提到,但不加会报错
  • API Key

三、配置使用(站点配置或主题配置均可)

_config.yml_config.butterfly.yml 中加入以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# electric_clock
# see: https://github.com/hoochanlon/hexo-butterfly-clock-remake
electric_clock:
enable: true
priority: 5
enable_page: all
exclude:
# - /posts/
# - /about/
layout:
type: class
name: aside-content
# insert_before: user-countdown
insert_after: card-announcement
loading: https://cdn.cbd.int/hexo-butterfly-clock-veeink@1.0.7/lib/loading.gif
clock_css: https://cdn.cbd.int/hexo-butterfly-clock-veeink@1.0.7/lib/clock-min.css
clock_js: /js/clock-min.js

qweather_api_host: {YOUR API HOST} # 注意要写成 https://xxx.qweatherapi.com
qweather_key: {YOUR KEY}

# default_city: "资兴"

为解决pjax问题,切换页面就会导致电子钟不再加载或重复加载报错不显示问题,需要下载js样式

https://cdn.cbd.int/hexo-butterfly-clock-veeink@1.0.7/lib/clock-min.js

然后打开js文件,找到

1
const DEBUG_MODE=!1;

改成

1
var DEBUG_MODE=!1;

四、关键参数说明

image-20260207173945077

关于博客上传服务器

可以强制执行这个触发钩子

1
sudo git --work-tree=/www/wwwroot/blog.dreamshelter.cn --git-dir=/home/git/repos/xxx.git checkout -f

音乐播放

新加了页面自动播放功能,但是要开启pjax,不然会换个页面就重新播放

1
2
pjax:
enable: true

以下是代码

1
2
3
4
5
6
bottom:
# - <script src="xxxx"></script>
# aplayer音乐
- '<div class="aplayer no-destroy" data-id="17745883873" data-server="netease" data-type="playlist" data-order="list" data-fixed="true" data-preload="auto" data-autoplay="true" data-mutex="true"></div>'
# 2. 初始音量脚本
- '<script>function initAPlayerVolume(){setTimeout(function(){var a=document.querySelector(".aplayer");if(a&&a.aplayer){a.aplayer.volume(0.1);console.log("音量已设为10%")}},1500)}window.addEventListener("load",initAPlayerVolume);document.addEventListener("pjax:complete",initAPlayerVolume);</script>'

页脚计时器和github徽标

安装指令:

1
npm install hexo-butterfly-footer-beautify --save

问题: 很多 Butterfly 的新版本或魔改版本,页脚的容器 ID 可能叫 footer 而不是 footer-wrap验证方法: 1. 在浏览器打开你的博客。 2. 按 F12,点击左上角的“小箭头”图标,点一下你的页脚。 3. 查看右侧代码,看看包围页脚内容的最外层 <div>id 到底是什么。如果叫 footer,你必须把配置里的 name 改成 footer

1
2
3
layout:
type: id
name: footer-wrap

而我这里的是footer-other,在 HTML 中,idclass 是不一样的。

  • ID 选择器在代码中写做 id="footer",配置里直接写名字。
  • Class 选择器在代码中写做 class="footer-other"

就要改成

1
2
3
4
layout:
type: class # 改为 class
name: footer-other # 对应你看到的那个标签
index: 0

最终配置:

1
2
3
4
5
6
7
8
9
10
~~~

# github贡献图

参考文章:https://www.cnblogs.com/an-shiguang/p/18269053

## 安装依赖:

~~~bash
npm i @barry-flynn/hexo-github-calendar --save

添加配置:

在 Hexo 项目根目录的 _config.yml 文件最后面添加如下配置(注意是在 Hexo 的配置文件中添加,而不是主题的配置文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# hexo-github-canlendar
# 贡献度热力图插件 https://github.com/Barry-Flynn/hexo-github-calendar
githubcalendar:
enable: true # 是否启用本插件
enable_page: / # 要生效的页面,如 / 首页,/about/ 介绍页等
user: shiguang-coding # GitHub 用户名
layout:
type: id
name: recent-posts
index: 0
githubcalendar_html: '<div class="recent-post-item" style="width:100%;height:auto;padding:10px;"><div id="github_loading" style="width:10%;height:100%;margin:0 auto;display: block"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50" xml:space="preserve"><path fill="#d0d0d0" d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z" transform="rotate(275.098 25 25)"><animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.6s" repeatCount="indefinite"></animateTransform></path></svg></div><div id="github_container"></div></div>'
pc_minheight: 280px
mobile_minheight: 0px
# 贡献统计的梯度色卡值,可自行调整
color: "['#ebedf0', '#a2f7af', '#6ce480', '#54ad63', '#469252', '#31753c', '#1f5f2a', '#13531f', '#084111', '#032b09', '#000000']"
# 推荐填写你自建的API接口,公用api随时可能会失效
# api: https://github-calendar-api.meta-code.top/api
api: https://githubcalendarapi.shiguang666.eu.org/api
# 推荐下载后使用本地文件
# calendar_js: https://cdn.jsdelivr.net/gh/barry-flynn/hexo-github-calendar/hexo_githubcalendar.js # 在线文件,容易加载失败
calendar_js: /js/hexo_githubcalendar.js # 本地文件,请下载到主题文件夹的source目录下
plus_style: ""

下载 hexo_githubcalendar.js 到主题文件夹 source目录,例如 themes\butterfly\source\js

然后执行三连命令即可

1
hexo clean && hexo g && hexo s

自建API

由于我以及其他博主提供的公共API随时可能失效,所以推荐自建API接口,使用Vercel部署完全免费喔。

新用户可以直接用Github登录,建议重新用google账号或者其他国外账号重新注册个Github账号,因为我之前使用自己的Github账号登录时好像被拒绝了,可能是绑定了国内的邮箱,有地域限制之类的。

先将 python_github_calendar_api By Barry-Flynn Fork 到自己的Github

登录 Vercel Add New.. => Project

搜索刚才导入的项目,当然如果仓库不是很多的话可以直接看到最近创建的仓库,点击 import导入

然后点击Deploy 进行部署

等待部署成功

然后可以绑定自己的域名,否则服务可能会因为DNS污染导致被墙无法正常使用

可直接点击 Add Domain

或者到 Settings => Domains 添加域名

例如此处我要添加的域名为https://vercelapi.dreamshelter.cn,当然这里我在腾讯与服务器先创建了这个二级域名,添加后提示需要添加一条CNAME记录指向`cname.vercel-dns.com`,进入到腾讯云dnspod管理界面,添加vercel给你的cname那些,ttl600,然后等待部署成功

如果依然提示 INTERNAL_SERVER_ERROR 500错误,错误码 FUNCTION_INVOCATION_FAILED则说明当前

Node的版本不支持。将默认Node版本从20.x 改为 18.x

这是自己优化后的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
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
function GithubCalendar(git_githubapiurl, git_color, git_user) {
if (document.getElementById('github_container')) {
var github_canlendar = (git_user, git_githubapiurl, git_color) => {
var git_fixed = 'fixed';
var git_px = 'px';
var git_month = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
var git_monthchange = [];
var git_oneyearbeforeday = '';
var git_thisday = '';
var git_amonthago = '';
var git_aweekago = '';
var git_weekdatacore = 0;
var git_datacore = 0;
var git_total = 0;
var git_datadate = '';
var git_git_data = [];
var git_positionplusdata = []; // 提示框坐标数组
var git_firstweek = [];
var git_lastweek = [];
var git_beforeweek = [];
var git_thisweekdatacore = 0;
var git_mounthbeforeday = 0;
var git_mounthfirstindex = 0;
var git_crispedges = 'crispedges';
var git_thisdayindex = 0;
var git_amonthagoindex = 0;
var git_amonthagoweek = [];
var git_firstdate = [];
var git_first2date = [];
var git_montharrbefore = [];
var git_monthindex = 0;

var retinaCanvas = (canvas, context, ratio) => {
if (ratio > 1) {
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
canvas.width = canvasWidth * ratio;
canvas.height = canvasHeight * ratio;
canvas.style.width = '100%';
canvas.style.height = canvasHeight + 'px';
context.scale(ratio, ratio);
}
}

var responsiveChart = () => {
if (document.getElementById("gitcanvas")) {
var git_tooltip_container = document.getElementById('git_tooltip_container');
var ratio = window.devicePixelRatio || 1
var git_x = '';
var git_y = '';
var git_span1 = '';
var git_span2 = '';
var github_calendar_c = document.getElementById("gitcanvas");
github_calendar_c.style.width = '100%';
github_calendar_c.style.height = '';
var github_calendar_ctx = github_calendar_c.getContext("2d");

var box = document.getElementById("gitcalendarcanvasbox");
width = github_calendar_c.width = box.offsetWidth;
// 稍微增加高度比例,为日期留出垂直空间
height = github_calendar_c.height = 10 * 0.96 * github_calendar_c.width / git_data.length;

retinaCanvas(github_calendar_c, github_calendar_ctx, ratio)
var linemaxwitdh = height / 10;
var lineminwitdh = 0.8 * linemaxwitdh;

// 【优化1】调整起始位置,Y轴预留更多空间给月份
var startY = 0.05 * width;
var setposition = { x: 0.02 * width, y: startY };

// 【优化2】核心:每次重绘前必须清空旧坐标,否则鼠标检测会乱跳
git_positionplusdata = [];

for (var week in git_data) {
weekdata = git_data[week];
for (var day in weekdata) {
var dataitem = { date: weekdata[day].date, count: weekdata[day].count, x: setposition.x, y: setposition.y };
git_positionplusdata.push(dataitem);

github_calendar_ctx.fillStyle = git_thiscolor(git_color, weekdata[day].count);
// 绘制格子
github_calendar_ctx.fillRect(setposition.x, setposition.y, lineminwitdh, lineminwitdh);
setposition.y = setposition.y + linemaxwitdh
}
setposition.y = startY;
setposition.x = setposition.x + linemaxwitdh;
}

// 【优化3】月份与周几文字的精准定位
if (document.body.clientWidth > 700) {
github_calendar_ctx.font = "500 12px -apple-system, sans-serif";
github_calendar_ctx.fillStyle = '#999';
github_calendar_ctx.textBaseline = "top";

// 周几
github_calendar_ctx.fillText("日", 0, startY + 0 * linemaxwitdh);
github_calendar_ctx.fillText("二", 0, startY + 2 * linemaxwitdh);
github_calendar_ctx.fillText("四", 0, startY + 4 * linemaxwitdh);
github_calendar_ctx.fillText("六", 0, startY + 6 * linemaxwitdh);

// 月份文字位置上移,避免被格子挡住
var monthindexlist = github_calendar_c.width / 24;
for (var index in git_monthchange) {
github_calendar_ctx.fillText(git_monthchange[index], monthindexlist, 0.1 * linemaxwitdh);
monthindexlist = monthindexlist + github_calendar_c.width / 12.5;
}
}

// 【优化4】鼠标交互检测逻辑
var getMousePos = (canvas, event) => {
var rect = canvas.getBoundingClientRect();
// 修正 Canvas 缩放后的坐标转换
var x = (event.clientX - rect.left) * (canvas.width / rect.width) / ratio;
var y = (event.clientY - rect.top) * (canvas.height / rect.height) / ratio;

for (var item of git_positionplusdata) {
if (x > item.x && x < item.x + lineminwitdh && y > item.y && y < item.y + lineminwitdh) {
git_span1 = item.date;
git_span2 = item.count;
git_x = event.clientX - 100;
git_y = event.clientY - 70;
var html = tooltip_html(git_x, git_y, git_span1, git_span2);
append_div_gitcalendar(git_tooltip_container, html);
return; // 找到后立即退出循环
}
}
}

github_calendar_c.onmousemove = function (event) {
git_tooltip_container.innerHTML = "";
getMousePos(github_calendar_c, event);
};
}
}

// --- 核心逻辑函数保持插件原始结构 ---
var addlastmonth = () => {
if (git_thisdayindex === 0) {
[52, 51, 50, 49, 48].forEach(idx => thisweekcore(idx));
git_thisweekdatacore += git_firstdate[6].count;
git_amonthago = git_firstdate[6].date;
} else {
[52, 51, 50, 49].forEach(idx => thisweekcore(idx));
thisweek2core();
git_amonthago = git_first2date[git_thisdayindex - 1].date;
}
}

var thisweek2core = () => {
for (var i = git_thisdayindex - 1; i < git_first2date.length; i++) {
git_thisweekdatacore += git_first2date[i].count
}
}

var thisweekcore = (index) => {
if(git_data[index]) {
for (var item of git_data[index]) {
git_thisweekdatacore += item.count
}
}
}

var addlastweek = () => {
for (var item of git_lastweek) {
git_weekdatacore += item.count
}
}

var addbeforeweek = () => {
for (var i = git_thisdayindex; i < git_beforeweek.length; i++) {
git_weekdatacore += git_beforeweek[i].count
}
}

var addweek = (data) => {
if (git_thisdayindex === 6) {
git_aweekago = git_lastweek[0].date;
addlastweek()
} else {
var lastweek_data = data.contributions[51];
git_aweekago = lastweek_data[git_thisdayindex + 1].date;
addlastweek();
addbeforeweek();
}
}

fetch(git_githubapiurl).then(res => res.json()).then(data => {
if (document.getElementById('github_loading')) {
document.getElementById('github_loading').remove()
};
git_data = data.contributions;
git_total = data.total;
git_first2date = git_data[48];
git_firstdate = git_data[47];
git_firstweek = data.contributions[0];
git_lastweek = data.contributions[52];
git_beforeweek = data.contributions[51];
git_thisdayindex = git_lastweek.length - 1;
git_thisday = git_lastweek[git_thisdayindex].date;
git_oneyearbeforeday = git_firstweek[0].date;
git_monthindex = parseInt(git_thisday.substring(5, 7));

var tempMonth = [...git_month];
git_montharrbefore = tempMonth.splice(git_monthindex % 12, 12);
git_monthchange = git_montharrbefore.concat(tempMonth).slice(0, 12);

addweek(data);
addlastmonth();
var html = github_main_box(git_monthchange, git_data, git_user, git_color, git_total, git_thisweekdatacore, git_weekdatacore, git_oneyearbeforeday, git_thisday, git_aweekago, git_amonthago);
append_div_gitcalendar(github_container, html);
responsiveChart()
}).catch(e => console.error(e));

window.onresize = () => responsiveChart();

var git_thiscolor = (color, x) => {
if (x === 0) return color[0];
if (x < 2) return color[1];
if (x < 20) return color[Math.min(parseInt(x / 2), 9)];
return color[9];
};

var tooltip_html = (x, y, span1, span2) => {
return `<div class="gitmessage" style="top:${y}px;left:${x}px;position:fixed;z-index:10000">
<div class="angle-wrapper"><span>${span1}&nbsp;</span><span>${span2} 次上传</span></div>
</div>`;
};

var github_canvas_box = () => {
return '<div id="gitcalendarcanvasbox"><canvas id="gitcanvas"></canvas></div>';
};

var github_info_box = (user, color) => {
return `<div id="git_tooltip_container"></div>
<div class="contrib-footer clearfix mt-1 mx-3 px-3 pb-1">
<div class="float-left text-gray">数据来源 <a href="https://github.com/${user}" target="blank">@${user}</a></div>
<div class="contrib-legend text-gray">Less <ul class="legend">
${[0,2,4,6,8].map(i => `<li style="background-color:${color[i]}"></li>`).join('')}
</ul> More </div>
</div>`;
};

var github_main_box = (monthchange, git_data, user, color, total, thisweek, week, start, end, aweek, amonth) => {
var style = github_main_style();
return `<div class="position-relative">
<div class="border py-2 graph-before-activity-overview">
<div class="js-gitcalendar-graph mx-md-2 mx-3 d-flex flex-column pt-1 graph-canvas gitcalendar-graph text-center">
${github_canvas_box()}
</div>
${github_info_box(user, color)}
</div>
</div>
<div class="contrib-stats-box">
<div class="contrib-column table-column"><span class="text-muted">过去一年提交</span><span class="contrib-number">${total}</span><span class="text-muted">${start} - ${end}</span></div>
<div class="contrib-column table-column"><span class="text-muted">最近一月提交</span><span class="contrib-number">${thisweek}</span><span class="text-muted">${amonth} - ${end}</span></div>
<div class="contrib-column table-column"><span class="text-muted">最近一周提交</span><span class="contrib-number">${week}</span><span class="text-muted">${aweek} - ${end}</span></div>
</div>${style}`;
};

var github_main_style = () => {
return `<style>
#github_container{text-align:center;width:100%;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;}
.contrib-legend{float:right;font-size:11px;}
.legend{display:inline-block;list-style:none;margin:0 5px;padding:0;}
.legend li{display:inline-block;width:10px;height:10px;border-radius:2px;margin:0 1px;}
.contrib-stats-box{display:flex;width:100%;padding-top:10px;border-top:1px solid #eee;}
.contrib-column{flex:1;padding:15px 10px;text-align:center;position:relative;}
.contrib-column::before{content:"";position:absolute;top:20%;left:0;height:60%;width:1px;background:linear-gradient(to bottom, transparent, #d1d5da, transparent);}
.contrib-column:first-child::before{display:none;}
.contrib-number{font-weight:600;font-size:22px;display:block;color:#24292e;margin:2px 0;}
.text-muted{font-size:11px;color:#6a737d;display:block;}
.angle-wrapper{background:rgba(255,255,255,0.98);box-shadow:0 4px 12px rgba(0,0,0,0.15);border:1px solid #e1e4e8;border-radius:6px;padding:8px 12px;color:#24292e;font-size:12px;}
</style>`;
};
};

var append_div_gitcalendar = (parent, text) => {
var temp = document.createElement('div');
temp.innerHTML = text;
while (temp.firstChild) parent.appendChild(temp.firstChild);
};

github_canlendar(git_user, git_githubapiurl, git_color);
}
}

Twikoo评论

链接:https://twikoo.js.org/backend.html#%E7%A7%81%E6%9C%89%E9%83%A8%E7%BD%B2

  1. 服务端下载安装 Node.js

  2. 安装 Twikoo server: npm i -g tkserver

  3. 根据需要配置环境变量,所有的环境变量都是可选的

  4. 启动 Twikoo server: tkserver

  5. 访问 http://服务端IP:8080 测试服务是否启动成功

  6. 配置前置代理实现 HTTPS 访问(可以用 Nginx、负载均衡或 Cloudflare 等)
    我这里直接在宝塔面板的html域名管理里设置配置文件,在最后一个}之前添加以下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # TWIKOO 反向代理配置
    location /twikooapi/ {
    proxy_pass http://127.0.0.1:8080/; # 注意末尾有斜杠,8080换成你的twikoo端口
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header REMOTE-HOST $remote_addr;
    add_header X-Cache $upstream_cache_status;
    }
  7. 到博客配置文件中配置 envId 为 'https://blog.dreamshelter.cn/twikooapi/'

我之前弄了个pm2管理twikoo,持续化管理,但是一直没能启动,本地查看的时候却没事,不知道这个pm2不配置会不会影响,这里懒得写上了

对于贡献图互相挤压问题

贡献图会被挤在左半边,评论区挤在右半边,解决方式是在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
54
55
56
57
58
59
60
61
/* 只有当页面中存在 #github_container 时(即贡献图页面),才应用以下逻辑 */
body:has(#github_container) #content-inner.layout {
display: flex !important;
flex-direction: column !important;
}

/* 仅在贡献图页面修复贡献图容器 */
body:has(#github_container) #content-inner > .recent-post-item {
width: 100% !important;
background: var(--card-bg) !important;
border-radius: 12px;
padding: 15px !important;
margin-bottom: 20px !important;
box-shadow: var(--card-hover-shadow);
flex: none !important;
}

/* 仅在贡献图页面修复评论区背景和宽度 */
body:has(#github_container) #page {
width: 100% !important;
background: var(--card-bg) !important; /* 解决透明问题 */
border-radius: 12px;
padding: 20px 40px !important;
box-shadow: var(--card-hover-shadow);
margin: 0 !important;
flex: none !important;
}

/* --- 针对 GitHub 贡献图页面的精准细节修复 --- */
body:has(#github_container) {

/* 1. 修复 Canvas 拉伸:强制等比例缩放 */
#gitcanvas {
/* 撤销原本固定的 170px 高度,改为自动计算以保持比例 */
height: auto !important;
/* 防止在超大屏上过度拉伸导致模糊 */
max-width: 100% !important;
display: block !important;
margin: 0 auto !important;
/* 提升渲染清晰度 */
image-rendering: -webkit-optimize-contrast;
}

/* 2. 增加容器的滚动条:防止手机端被挤压变形 */
#gitcalendarcanvasbox {
width: 100% !important;
overflow-x: auto !important; /* 屏幕太窄时允许横滑,不强制挤压 */
-webkit-overflow-scrolling: touch;
}

/* 3. 修复底部统计文字(防止文字也跟着变扁) */
.contrib-column.table-column {
flex: 1 !important;
white-space: nowrap !important;
}

/* 4. 优化 Loading 动画位置 */
#github_loading {
margin: 50px auto !important;
}
}