碎碎念

4月08日,店长久违的更新了方舟系列的友链卡片,作为店长铁粉,当然要第一时间买店长的火柴啦,于是我将店长所有的友链卡片稍微整合了一下,结合安知鱼的教程,最终实现了“五世同堂”友链页面,大家可以在下面链接卡片或者友链页面查看教程,五种友链可以随便混合搭配,大家按照自己的butterfly主题风格稍作修改即可,并且针对鱼的友链美化后无法适配友链朋友圈,这里提供了一个相对可行解决方案,我也试验过,可以正常自行部署以及vercel无服务器部署,于是在此分享给大家!

功能对比

-教程-本站教程店长教程安知鱼教程原生友链页功能解释
卡片类型𝟱𝟱𝟮𝟭友链卡片的不同外观效果数目
随机顺序相同组别中友链卡片顺序随机打乱
卡片组合友链页面不同组别设置不同效果卡片
适配友圈友链朋友圈无法抓取的解决方案
适配夜间针对于byer卡片对于夜间模式的简单适配

写在最前

本教程涉及到魔改,需要修改主题源文件,需要一定的技术水平和操作风险。在本次教程中,我也会详细讲解具体做法和相关修改内容的原因,以便于能够让更多人学会魔改。在进行任何修改之前,请务必备份好所有原始文件,以防意外发生。备份的方法可以是将整个项目压缩成一个压缩文件,确保您在出问题后可以随时通过解压方式回滚到之前的状态。谨慎操作,祝您顺利魔改成功!如果有任何疑问或需要帮助,请随时评论区或者邮件联系我。

由于风格原因,原用于演示的友链页面中的ark及byer改为butterfly及另外两种特效,前两者具体样式请看截图。

卡片魔改

注意先去备份所有源文件,防止改错了回不去的尴尬局面(别跟我一样呜呜呜)

修改模板文件

打开:"[root]\themes\butterfly\layout\includes\page\flink.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
#article-container
if top_img === false
h1.page-title= page.title
.flink
if site.data.link
each i in site.data.link
if i.class_name
h2!= i.class_name
if i.class_desc
.flink-desc!=i.class_desc
if i.flink_style === 'butterfly'
.butterfly-flink-list
- let randomList = i.link_list.slice()
if i.random
- randomList.sort(() => Math.random() - 0.5)
each item in randomList
.flink-list-item
a(href=url_for(item.link) title=item.name target="_blank")
.flink-item-icon
if theme.lazyload.enable
img.nolazyload(data-lazy-src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
else
img.nolazyload(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
.flink-item-info
.flink-item-name= item.name
.flink-item-desc(title=item.descr)= item.descr
else if i.flink_style === 'flexcard'
.flexcard-flink-list
- let randomList = i.link_list.slice()
if i.random
- randomList.sort(() => Math.random() - 0.5)
each item in randomList
a.flink-list-card(href=url_for(item.link) target='_blank' data-title=item.descr)
.wrapper.cover
- var siteshot = item.siteshot ? url_for(item.siteshot) : 'https://s0.wp.com/mshots/v1/' + item.link + '?w=400&h=300'
if theme.lazyload.enable
img.cover.fadeIn.nolazyload(data-lazy-src=siteshot onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
img.cover.fadeIn.nolazyload(data-lazy-src=avatar onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
else
img.cover.fadeIn.nolazyload(src=siteshot onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
img.cover.fadeIn.nolazyload(data-lazy-src=avatar onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
.info
if theme.lazyload.enable
img.flink-avatar.nolazyload(data-lazy-src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt='' )
else
img.nolazyload(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt='' )
span.flink-sitename= item.name
else if i.flink_style === 'volantis'
.volantis-flink-list
- let randomList = i.link_list.slice()
if i.random
- randomList.sort(() => Math.random() - 0.5)
each item in randomList
a.site-card(target='_blank' rel='noopener' href=url_for(item.link))
.img
- var siteshot = item.siteshot ? url_for(item.siteshot) : 'https://s0.wp.com/mshots/v1/' + item.link + '?w=400&h=300'
img.nolazyload.no-lightbox(src=siteshot onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
.info
img.nolazyload.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt='' )
span.title= item.name
span.desc(title=item.descr)= item.descr
else if i.flink_style === 'byer'
.byer-flink-list
- let randomList = i.link_list.slice()
if i.random
- randomList.sort(() => Math.random() - 0.5)
each item in randomList
.flink-list-item
a(href=url_for(item.link) title=item.name target="_blank")
.flink-item-bar
sapn.flink-item-bar-yellow
sapn.flink-item-bar-green
sapn.flink-item-bar-red
sapn.flink-item-bar-x +
.flink-item-content
.flink-item-text
.flink-item-name= item.name
.flink-item-desc(title=item.descr)= item.descr
.flink-item-icon
img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
else if i.flink_style === 'ark'
.ark-flink-list
- let randomList = i.link_list.slice()
if i.random
- randomList.sort(() => Math.random() - 0.5)
each item in randomList
a.ark-flink-list-card(href=url_for(item.link) target='_blank' title=item.descr)
.ark-flink-progress-bar-A
.ark-flink-progress-bar-B
.ark-flink-progress-bar-C
.ark-flink-content
.ark-flink-name
.flink-sitename= item.name
.flink-block
.ark-flink-avatar
img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt='' )
.ark-flink-mask
.ark-flink-mask-left
.ark-flink-mask-right
.ark-flink-descr
.ark-flink-descr-text=item.descr
.ark-flink-siteshot
- var siteshot = item.siteshot ? url_for(item.siteshot) : 'https://s0.wp.com/mshots/v1/' + item.link + '?w=400&h=300'
img.no-lightbox(src=siteshot onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )

!= page.content

注意缩进问题,整个文件全部覆盖,可以看见这个文件中,下面是这段代码的解释:

  1. #article-container: 这是一个带有ID为article-container的HTML元素。

  2. if top_img === false: 如果top_img的值为false,则不需要顶部banner图片。

  3. h1.page-title= page.title: 在.flink类内部,如果条件满足,则创建一个h1标题元素,标题内容为page.title

  4. .flink: 这是一个类为flink的HTML元素。

  5. if site.data.link: 如果site.data.link存在,则执行以下内容。

  6. each i in site.data.link: 遍历每个组别,也就是不同site.data.link中的每个元素,将当前元素赋值给i

  7. if i.class_name: 如果i中存在class_name属性,则创建一个h2元素显示i.class_name

  8. if i.class_desc: 如果i中存在class_desc属性,则创建一个.flink-desc元素显示i.class_desc

    然后根据不同的主题风格去使用不同类名创建元素:

    1. if i.flink_style === 'butterfly': 如果i中的flink_style属性的值为butterfly,则执行以下内容,渲染一个蝴蝶风格的链接列表。
    2. else if i.flink_style === 'flexcard': 如果i中的flink_style属性的值为flexcard,则执行以下内容,渲染一个flexcard风格的链接列表。
    3. else if i.flink_style === 'volantis': 如果i中的flink_style属性的值为volantis,则执行以下内容,渲染一个volantis风格的链接列表。
    4. else if i.flink_style === 'byer': 如果i中的flink_style属性的值为byer,则执行以下内容,渲染一个byer风格的链接列表。
    5. else if i.flink_style === 'ark': 如果i中的flink_style属性的值为ark,则执行以下内容,渲染一个ark风格的链接列表。
  9. != page.content: 最后,将page.content的内容原样输出。

实际上理论不难,就是给不同样式的卡片添加不同的类名,后面根据不同类名进行不同的CSS渲染。

修改样式文件

打开:"[root]\themes\butterfly\source\css\_page\flink.styl"文件,覆盖以下内容:

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
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
.flink-desc
margin: .2rem 0 .5rem
//bf原生
.butterfly-flink-list
overflow: auto
padding: 10px 10px 0
text-align: center

& > .flink-list-item
position: relative
float: left
overflow: hidden
line-height: 17px
-webkit-transform: translateZ(0)
height: 100px;
padding: 10px;
width: calc(100% / 5 - 0.5rem)
margin: 0.5rem 0.25rem;
border-radius: 12px;
border: var(--style-border);
-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;

+maxWidth1200()
width: calc(50% - 15px) !important

+maxWidth600()
width: calc(100% - 15px) !important

&:hover
border-color: #101010 !important;
background-color: #eeeeee !important;
box-shadow: #cccccc !important;

.flink-item-icon
width: 0;
height: 0;
margin-left: -10px;


&:hover:before,
&:focus:before,
&:active:before
transform: scale(1)

a
color: var(--font-color)
text-decoration: none

.flink-item-icon
float: left
overflow: hidden
margin: 15px 10px
width: 60px
height: 60px
border-radius: 35px
transition: all .3s ease-out
margin: 8px 0 8px 0;
border-radius: 50%;
overflow: hidden;

img
width: 100%
height: 100%
transition: filter 375ms ease-in .2s, transform .3s
object-fit: cover

.img-alt
display: none

.flink-item-info
display: flex;
flex-wrap: wrap;
padding-left: 10px;
text-align: left;
flex-direction: column;

.flink-item-name
@extend .limit-one-line
padding: 12px 0 16px 0;
height: auto;
font-weight: bold
font-size: 1.2em

.flink-item-desc
@extend .limit-one-line
padding: 0
height: 35px
font-size: .93em
opacity: .7;
word-break: break-all;
white-space: break-spaces;
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;

.flink-name
margin-bottom: 5px
font-weight: bold
font-size: 1.5em
//flexcard卡片
#article-container img
margin-bottom: 0.5rem;
object-fit: cover;
max-height: 900px;
.flexcard-flink-list
overflow hidden
.flink-list-card
.wrapper img
transition: transform .5s ease-out !important;

& > a
width: calc(100% / 5 - 0.5rem);
height 150px
position relative
display block
margin: 0.5rem 0.25rem;
float left
overflow hidden
padding: 0;
border-radius: 8px;
transition all .3s ease 0s, transform .6s cubic-bezier(.6, .2, .1, 1) 0s
box-shadow none
border: var(--style-border)!important;
&:hover
.info
transform translateY(-100%)
.wrapper
img
transform scale(1.2)
&::before
position: fixed
width:inherit
margin:auto
left:0
right:0
top:10%
border-radius: 10px
text-align: center
z-index: 100
content: attr(data-title)
font-size: 20px
color: #fff
padding: 10px
background-color: rgba($theme-color,0.8)

.cover
width 100%
transition transform .5s ease-out
.wrapper
position relative
.fadeIn
animation coverIn .8s ease-out forwards
img
height 150px
pointer-events none
.info
display flex
flex-direction column
justify-content center
align-items center
width 100%
height 100%
overflow hidden
border-radius 3px
background-color hsla(0, 0%, 100%, .7)
transition transform .5s cubic-bezier(.6, .2, .1, 1) 0s
img
position relative
top 45px
width 80px
height 80px
border-radius 50%
box-shadow 0 0 10px rgba(0, 0, 0, .3)
z-index 1
text-align center
pointer-events none
span
padding 20px 10% 60px 10%
font-size 16px
width 100%
text-align center
box-shadow 0 0 10px rgba(0, 0, 0, .3)
background-color hsla(0, 0%, 100%, .7)
color var(--font-color)
white-space nowrap
overflow hidden
text-overflow ellipsis
.flexcard-flink-list>a .info,
.flexcard-flink-list>a .wrapper .cover
position absolute
top 0
left 0

@media screen and (max-width:1024px)
.flexcard-flink-list
& > a
width calc(33.33333% - 15px)

@media screen and (max-width:600px)
.flexcard-flink-list
& > a
width calc(50% - 15px)

[data-theme=dark]
.flexcard-flink-list a .info,
.flexcard-flink-list a .info span
background-color rgba(0, 0, 0, .6)
.flexcard-flink-list
& > a
&:hover
&:before
background-color: rgba(#121212,0.8);
.justified-gallery > div > img,
.justified-gallery > figure > img,
.justified-gallery > a > a > img,
.justified-gallery > div > a > img,
.justified-gallery > figure > a > img,
.justified-gallery > a > svg,
.justified-gallery > div > svg,
.justified-gallery > figure > svg,
.justified-gallery > a > a > svg,
.justified-gallery > div > a > svg,
.justified-gallery > figure > a > svg
position static!important




trans($time = 0.28s)
transition: all $time ease
-moz-transition: all $time ease
-webkit-transition: all $time ease
-o-transition: all $time ease

//volantis卡片,我的最爱
.volantis-flink-list
display: flex
flex-wrap: wrap
justify-content: flex-start
margin: -0.5 * 16px
align-items: stretch
.site-card
margin: 16px * 0.5
width: "calc(100% / 4 - %s)" % 16px
@media screen and (min-width: 2048px)
width: "calc(100% / 5 - %s)" % 16px
@media screen and (max-width: 768px)
width: "calc(100% / 3 - %s)" % 16px
@media screen and (max-width: 500px)
width: "calc(100% / 2 - %s)" % 16px
display: block
line-height: 1.4
height 100%
.img
width: 100%
height 150px
@media screen and (max-width: 500px)
height 100px
overflow: hidden
border-radius: 12px * 0.5
box-shadow: 0 1px 2px 0px rgba(0, 0, 0, 0.2)
background: #f6f6f6
img
width: 100%
height 100%
pointer-events:none;
// trans(.75s)
transition: transform 2s ease
object-fit: cover

.info
margin-top: 16px * 0.5
img
width: 32px
height: 32px
pointer-events:none;
border-radius: 16px
float: left
margin-right: 8px
margin-top: 2px
span
display: block
.title
font-weight: 600
font-size: var(--global-font-size)
color: #444
display: -webkit-box
-webkit-box-orient: vertical
overflow: hidden
-webkit-line-clamp: 1
trans()
.desc
font-size: var(--global-font-size)
word-wrap: break-word;
line-height: 1.2
color: #888
display: -webkit-box
-webkit-box-orient: vertical
overflow: hidden
-webkit-line-clamp: 2
.img
trans()
&:hover
.img
box-shadow: 0 4px 8px 0px rgba(0, 0, 0, 0.1), 0 2px 4px 0px rgba(0, 0, 0, 0.1), 0 4px 8px 0px rgba(0, 0, 0, 0.1), 0 8px 16px 0px rgba(0, 0, 0, 0.1)
.info .title
color: #ff5722
//byer卡片
#article-container
.flink
margin-bottom: 20px

.byer-flink-list
overflow: auto
padding: 10px 10px 0
text-align: center

& > .flink-list-item
position: relative
background: #ffffff
float: left
overflow: hidden
margin: 15px 7px
width: calc(100% / 3 - 15px)
height: 120px
border-radius: 2px
line-height: 17px
-webkit-transform: translateZ(0)
border: 1px solid
box-shadow: 3px 3px 1px 1px #fee34c;

+maxWidth1024()
width: calc(50% - 15px) !important

+maxWidth600()
width: calc(100% - 15px) !important

a
color: var(--font-color)
text-decoration: none
.flink-item-bar
height: 15px
border-width: 0 0 1px 0
border-style: none none solid none
background: #fde135
display: flex;
align-items: center;
flex-direction: row;
flex-wrap: nowrap;
padding: 0 3px 0 3px
sapn
width: 10px;
height: 10px;
margin: 0 1px 0 1px
border-radius: 50%;
display: block;
border: 1px solid;
display: flex;
align-items: center;
justify-content: flex-start;
&.flink-item-bar-yellow
background: #fde135
&.flink-item-bar-green
background: #249a33
&.flink-item-bar-red
background: #f13b06
&.flink-item-bar-x
background: transparent
border: 0px
margin-left: auto
transform: rotate(45deg);
font-size: 23px;
padding: 0px 0px 6px 0px;
.flink-item-content
display: flex;
height: 105px
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 0 5px 0 5px;
.flink-item-text
width: 60%;
display: flex;
flex-direction: column;
align-items: center;
.flink-item-name
@extend .limit-one-line
max-width: 100%;
padding: 0px 5px 0px 5px;
margin: 0px 0 6px 0;
height: 50%;
font-weight: bold;
font-size: 1.43em;
border-width: 0 0 7px 0;
border-style: solid;
border-color: #fbf19f;
.flink-item-desc
@extend .limit-one-line
max-width: 100%;
height: 50%;
padding: 5px 5px 5px 5px;
font-size: 0.93em;
position: relative
&:before
content: "";
background: transparent;
display: block;
height: calc(100% - 4px);
width: calc(100% - 4px);
position: absolute;
left: 0;
top: 0;
border-radius: 2px;
border: 1px solid;
clip-path: polygon(0 0, 100% 0, 100% 100%, 95% 100%, 95% 50%, 90% 50%, 90% 100%, 0 100%);


.flink-item-icon
overflow: hidden;
margin: 0px 5px;
width: 70px;
height: 70px;
border: 1px solid;
border-radius: 2px;
transition: width .3s ease-out
box-shadow: 2px 2px 1px 1px #fee34c;
img
width: 50px;
height: 50px;
margin: 9px 9px;
transition: filter 375ms ease-in .2s, transform .3s
object-fit: cover

.img-alt
display: none
//byer卡片暗夜模式改造
[data-theme=dark]
#article-container
.flink
.byer-flink-list
& > .flink-list-item
background: rgb(40,40,40)
box-shadow: 3px 3px 1px 1px #1B5A70;
a
.flink-item-bar
background: #1B5A70;
.flink-item-content
.flink-item-text
.flink-item-name
border-color: #5EBAD9;
.flink-item-icon
box-shadow: 2px 2px 1px 1px #1B5A70;



//下面是aki及其自定义配色:
:root
--ark-flink-default-color: rgba(153, 54, 44,0.8) /*主色调*/
--ark-flink-mask: #818181 /*遮罩层配色*/
--ark-flink-progress-default: rgba(227, 236, 238, 0.8) /*能量条默认配色*/
--ark-flink-progress-charge: #d97f17 /*能量条充能配色*/
--flink-name-border-color: #d97f17 /*ID边框配色*/

[data-theme="dark"]
--ark-flink-default-color: rgba(55, 112, 143,0.8)
--ark-flink-mask: #37708f
--ark-flink-progress-default: rgba(46, 160, 221, 0.8)
--ark-flink-progress-charge: rgba(227, 236, 238, 0.8)
--flink-name-border-color: rgba(227, 236, 238, 0.8)


//适配ark方舟友链卡片
#article-container
.flink
margin-bottom: 20px

.ark-flink-list
overflow: auto
padding: 10px 10px 0
text-align: center

& > .ark-flink-list-card
position: relative
display: block
color: var(--font-color)
text-decoration: none
float: left
overflow: hidden
margin: 15px 7px
width: calc(100% / 3 - 15px)
height: 220px
border-radius: 2px
line-height: 17px
-webkit-transform: translateZ(0)

+maxWidth1024()
width: calc(50% - 15px) !important

+maxWidth600()
width: calc(100% - 15px) !important


a.ark-flink-list-card
*
transition: all 0.3s cubic-bezier(.6, 0, .5, 1)
&:hover
*
transition: all 0.3s cubic-bezier(.6, 0, .5, 1)
.ark-flink-progress-bar-A,
.ark-flink-progress-bar-B,
.ark-flink-progress-bar-C
background: var(--ark-flink-progress-charge)
.ark-flink-content
.ark-flink-name
bottom: 0px;
.ark-flink-avatar
transform: rotateX(90deg)
.ark-flink-mask
.ark-flink-mask-left
transition-delay: 0.3s;
left: -35%;
.ark-flink-mask-right
transition-delay: 0.3s;
right: -55%;
.ark-flink-descr
.ark-flink-descr-text
transition-delay: 0.3s;
opacity: 1
animation: ark-flink-type 1.5s steps(20, end) 0.3s,ark-flink-blink .75s step-end infinite; /* 定义光标的闪烁动画 */
.ark-flink-progress-bar-A,
.ark-flink-progress-bar-B,
.ark-flink-progress-bar-C
display: block
position: absolute;
background: var(--ark-flink-progress-default)
z-index 6
.ark-flink-progress-bar-A
height: 8px;
width: 100px;
top: 3px;
left: 6px;
clip-path: polygon(0% 100%, 8% 0%, 28% 0%, 20% 100%, 23% 100%, 31% 0%, 46% 0%, 38% 100%, 41% 100%, 49% 0%, 64% 0%, 56% 100%, 59% 100%, 67% 0%, 82% 0%, 74% 100%, 77% 100%, 85% 0%, 100% 0%, 90% 100%);
.ark-flink-progress-bar-B
height: 8px;
width: 35px;
bottom: 35px;
left: 0;
clip-path: polygon(0% 0%, 40% 0%, 15% 100%, 25% 100%, 50% 0%, 85% 0%, 60% 100%, 70% 100%, 90% 0%, 100% 100%, 15% 100%);
.ark-flink-progress-bar-C
height: 100px
width: 8px
bottom: 50px
right: 0
clip-path: polygon( 0% 0%, 100% 8%, 100% 28%, 0% 20%, 0% 23%, 100% 31%,100% 46% ,0% 38% ,0% 41% ,100% 49% ,100% 64% ,0% 56% ,0% 59% ,100% 67% ,100% 82% ,0% 74% , 0% 77%,100% 85% , 100% 100%,0% 90% );
.ark-flink-content
display: block
position: absolute
background: radial-gradient(var(--ark-flink-default-color),transparent)
width: calc(100% - 10px)
height: 100%
top: 0
left: 0
clip-path: polygon(0 15px, 100px 15px, 115px 0, calc(100% - 45px) 0, calc(100% - 15px) 45px, 100% 45px, 100% calc(100% - 25px), calc(100% - 30px) calc(100% - 25px), calc(100% - 55px) calc(100% - 10px), calc(100% - 90px) calc(100% - 10px), calc(100% - 100px) 100%, 100px 100%, 90px calc(100% - 10px), 55px calc(100% - 10px), 35px calc(100% - 45px), 0% calc(100% - 45px));
.ark-flink-avatar,
.ark-flink-mask,
.ark-flink-descr,
.ark-flink-siteshot
position: absolute
width: 100%
height: 100%
top: 0
left: 0
.ark-flink-name
display: block;
position: absolute;
z-index: 5;
bottom: 10px;
left: 20%;
color: white;
text-shadow: 1px 1px 5px black;
background: transparent;
height: 40px;
width: 60%;
border-style: double;
border-width: 5px 5px 0 5px;
border-color: var(--flink-name-border-color);
transform: perspective(0.5em) rotateX(3deg);
.flink-sitename
transform: perspective(0.5em) rotateX(-3deg);
font-size: 15px;
margin: 5px 0 0 0;
.flink-block
transform: perspective(0.5em) rotateX(-10deg);
display: block;
width: 60%;
height: 13px;
background: var(--flink-name-border-color);
position: absolute;
bottom: 0px;
left: 20%;
.ark-flink-avatar
z-index 4
display: flex
align-items: center;
justify-content: center;
transform: rotateX(0deg)
img
width: 100px;
height: 100px;
margin: 0 auto 20px;
object-fit: cover
clip-path: polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
.img-alt
display: none
.ark-flink-mask
z-index 3
.ark-flink-mask-left
width: 100%;
height: 100%;
background: repeating-linear-gradient(0deg, var(--ark-flink-mask),transparent 1px);
clip-path: polygon(50% 0, 50% calc(50% - 60px), calc(50% - 20px) calc(50% - 60px), calc(50% - 50px) calc(50% - 30px), calc(50% - 50px) calc(50% + 10px), calc(50% - 20px) calc(50% + 40px), 50% calc(50% + 40px), 50% 100%, 0% 100%, 0% 0%);
left: 0%;
position: absolute;
.ark-flink-mask-right
background: repeating-linear-gradient(0deg, var(--ark-flink-mask),transparent 1px);
width: 100%;
height: 100%;
clip-path: polygon(100% 0%, 50% 0%, 50% calc(50% - 60px), calc(50% - 20px) calc(50% - 60px), calc(50% - 50px) calc(50% - 30px), calc(50% - 50px) calc(50% + 10px), calc(50% - 20px) calc(50% + 40px), 50% calc(50% + 40px), 50% 100%, 100% 100%);
right: 0%;
position: absolute;

.ark-flink-descr
z-index 2
display: flex
align-items: center;
justify-content: center;
.ark-flink-descr-text
color: white;
text-shadow: 1px 1px 5px black;
font-size: 1.5em;
height: 1.5em;
line-height: 1.5em;
overflow: hidden; /* 隐藏超出容器的文本 */
border-right: .15em solid orange; /* 打字效果的光标动画 */
white-space: nowrap; /* 确保文本在一行内显示,不换行 */
margin: 20px;
opacity: 0
.ark-flink-siteshot
z-index 1
display: flex
align-items: center;
justify-content: center;
img
height: 75%;
width: 100%;
margin: 0 auto 20px;
object-fit: cover;

/* 定义打字机动画 */
@keyframes ark-flink-type
from
width: 0;
to
width: 100%;


/* 定义光标闪烁动画 */
@keyframes ark-flink-blink
from,
to
border-color: transparent;
50%
border-color: orange

这里大家可以看到内容很多,我也在上面标注了,因为这个是五种友链的分别适配,如果你觉得太多了可以尝试删掉你不需要的部分,上面备注写的也比较详尽,但是建议在修改后将备注删除,防止因为备注导致编译失败,这个就不需要过多解释了,如果想要二次修改的,按照styl的格式,类似于css进行修改即可。

修改友链数据文件

打开:"[root]\source\_data\link.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
58
59
60
61
62
63
64
65
66
67
68
- class_name: 亦师亦友 · 给予帮助的人最可爱
flink_style: volantis
random: true
link_list:
- name: 清羽飞扬
link: https://blog.liushen.fun/
avatar: https://cdn.qyliu.top/i/2024/03/21/65fc56832e37d.png
descr: 柳影曳曳,清酒孤灯,扬笔撒墨,心境如霜
siteshot: https://cdn.qyliu.top/i/2024/03/21/65fc57d6b8615.png
- name: Akilarの糖果屋
link: https://akilar.top/
siteshot: https://cdn.qyliu.top/i/2024/03/21/65fc582187f19.png
avatar: https://cdn.qyliu.top/i/2024/04/06/661170950f7a2.png
descr: 欢迎光临糖果屋

- class_name: 暂时离开 · 临时的别离,只为更美的重逢
flink_style: flexcard
random: false
link_list:
- name: 清羽飞扬
link: https://blog.liushen.fun/
avatar: https://cdn.qyliu.top/i/2024/03/21/65fc56832e37d.png
descr: 柳影曳曳,清酒孤灯,扬笔撒墨,心境如霜
siteshot: https://cdn.qyliu.top/i/2024/03/21/65fc57d6b8615.png
- name: Akilarの糖果屋
link: https://akilar.top/
siteshot: https://cdn.qyliu.top/i/2024/03/21/65fc582187f19.png
avatar: https://cdn.qyliu.top/i/2024/04/06/661170950f7a2.png
descr: 欢迎光临糖果屋

- class_name: 推荐网站 · 很值得一试
flink_style: byer
random: false
link_list:
- name: Hexo官网
link: https://hexo.io/zh-tw/
avatar: https://cdn.qyliu.top/i/2024/04/07/66124225b85c1.png
descr: 快速、简单且强大的网志框架
- name: Weibo
link: https://www.weibo.com/
avatar: https://cdn.qyliu.top/i/2024/04/07/661242b5d48f0.png
descr: 中国最大社交分享平台

- class_name: 网站搭建 · 网站的每个部分都有他们的功劳
flink_style: butterfly
random: false
link_list:
- name: 柳神云图床
link: https://pic.qyliu.top/
avatar: https://cdn.qyliu.top/i/2024/03/21/65fc56832e37d.png
descr: 自建云图床
- name: TianLiGPT
link: https://summary.zhheo.com/admin
avatar: https://bu.dusays.com/2023/04/15/643a29d0cf651.png
descr: TianLiGPT管理

- class_name: 平时常用 · 便捷你我他
flink_style: ark
random: false
link_list:
- name: 统计鸟
link: https://www.tongjiniao.com/controlCenter/trafficStatistics
avatar: https://cdn.qyliu.top/i/2024/04/09/6614ebdc3eafe.png
descr: 提供网站数据统计分析
- name: 缤纷云
link: https://console.bitiful.com/buckets
avatar: https://cdn.qyliu.top/i/2024/04/09/6614ec3365b84.png
descr: 高性能对象存储及CDN

下面是相关选项的内容及可选项:

一级二级butterflyvolantisflexcardbyerark解释
class_name*(✅)(✅)(✅)(✅)(✅)每类提示词
flink_style*卡片样式
random*(✅)(✅)(✅)(✅)(✅)链接顺序随机
name卡片名称
link卡片链接
link_listavatar卡片头像
descr卡片描述
siteshot(✅)(✅)(✅)网站截图
  • ❌:不支持选项

  • (✅):可选选项,可以省略,比如siteshot,可以根据网站信息采用api截图生成,api如下:

    'https://s0.wp.com/mshots/v1/' + [link] + '?w=400&h=300'

  • ✅:必选项,不可省略

注意,上面link.yml文件给的代码仅供格式参考,由于本站图床已开启防盗链,你们需要修改为自己的信息才能正常显示,谢谢!参考样式也可以看我的友链链接。

butterfly预览

vloantis预览

flexcard预览

byer预览

ark预览

卡片魔改到此结束,下面我们进行友链朋友圈适配。

朋友圈适配

选择方法

为什么之前的不行了呢?究其原因还是因为修改了我们的类名,导致原本针对于原生bf的爬虫无法正常获取到对应类名导致无法正常获取,而本教程由于需要对于不同卡片进行不同渲染,所以也需要保证类名不一致,所以我们只能选择曲线方案,利用其它方法来适配朋友圈。可以看见朋友圈中有一种通用方式,如下:

所以在这里我决定使用这个通用教程来尝试适配我们的友链朋友圈。

获取json文件

首先我们需要从一个位置获得所有的友链信息,这里我们选择从link.yml文件中获取,毕竟这里是存所有友链信息的位置,在博客根目录下创建文件[root]\link.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
const YML = require('yamljs')
const fs = require('fs')

const blacklist = ['name1', 'name2']; // 由于某种原因,不想订阅的友链的名称

let friends = [],
data_f = YML.parse(fs.readFileSync('source/_data/link.yml').toString().replace(/(?<=rss:)\s*\n/g, ' ""\n'));

data_f.forEach((entry, index) => {
let lastIndex = data_f.length - 4; // 这里需要按照你们的修改,这里的4表示我后面四项是不需要的,我只需要爬取后四项之前的所有类别即可
if (index < lastIndex) {
const filteredLinkList = entry.link_list.filter(linkItem => !blacklist.includes(linkItem.name));
friends = friends.concat(filteredLinkList);
}
});

// 根据规定的格式构建 JSON 数据
const friendData = {
friends: friends.map(item => {
return [item.name, item.link, item.avatar];
})
};

// 将 JSON 对象转换为字符串
const friendJSON = JSON.stringify(friendData, null, 2);

// 写入 friend.json 文件
fs.writeFileSync('./source/friend.json', friendJSON);

console.log('friend.json 文件已生成。');

注意这个代码,需要确保friend.json文件名称没有被使用,如果被使用了自己换一个,除此之外,大家需要修改的内容为:

  • blacklist:友链朋友圈黑名单,可以在这里填入不想订阅的友链名称,和上面的name标签对应
  • lastIndex:可以直接修改为你想要订阅的前几个组别,比如我的,就只需要订阅前两个组别即可,后面不需要,也可以修改后面的数字,比如后四项无效。

正确修改后,在根目录运行:

1
node link.js

即可在source文件夹中获得一个friend.json文件,格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"friends": [
[
"清羽飞扬",
"https://blog.liushen.fun/",
"https://cdn.qyliu.top/i/2024/03/21/65fc56832e37d.png"
],
[
"Akilarの糖果屋",
"https://akilar.top/",
"https://cdn.qyliu.top/i/2024/04/06/661170950f7a2.png"
],
……
}

符合友链朋友圈的要求,实际上这个文件,如果你的友链数目比较少,你也可以自行按照格式创建。

修改朋友圈配置项

下面在友链朋友圈的配置文件/hexo_circle_of_friends/fc_settings.yaml中,屏蔽掉上面butterfly友链抓取装置,选择下面的通用选项:

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
# 友链页地址
# 参数说明:
# link:必填,在这里填写你的友链页面地址
# theme:必填,友链页的获取策略。需要指定该页面的主题,可选参数如下(这些是目前支持的主题):
# - common1: 通用主题1,请参考:https://fcircle-doc.yyyzyyyz.cn/#/developmentdoc?id=友链页适配
# - common2: 通用主题2,请参考:https://fcircle-doc.yyyzyyyz.cn/#/developmentdoc?id=友链页适配
# - butterfly:butterfly主题
# - fluid:fluid主题
# - matery:matery主题
# - nexmoe:nexmoe主题
# - stun:stun主题
# - sakura: sakura主题
# - volantis:volantis主题
# - Yun:Yun主题
# - stellar:stellar主题
# 支持配置多个友链页面并指定不同主题策略,每个用{}分隔,它们会被同时爬取,数据保存在一起。
LINK: [
# { link: "https://www.qyliu.top/link/", theme: "common1" }, # 友链页地址1,修改为你的友链页地址
# { link: "https://noionion.top/link/", theme: "butterfly" }, # 友链页地址2
# { link: "https://immmmm.com/about/", theme: "common1" }, # 友链页地址3
# ...
]

# 配置项友链
# enable:# 是否启用配置项友链 true/false(针对还未适配主题或者有定制需求的用户)
# json_api:通过json格式配置友链,详见:https://fcircle-doc.yyyzyyyz.cn/#/developmentdoc?id=配置项json友链
# list字段填写格式:["name", "link", "avatar","suffix"],其中:
# name:必填,友链的名字
# link:必填,友链主页地址
# avatar:必填,头像地址
# suffix:选填,自定义订阅后缀,主要针对不规范的网站订阅后缀,见示例2
SETTINGS_FRIENDS_LINKS: {
enable: true,
json_api: "https://blog.liushen.fun/friend.json" # 你的地址,需要生成文件部署之后才能使用网络文件的方式访问,这个道理应该都懂
list: [
# 示例1:
# ["贰猹の小窝", "https://noionion.top/", "https://pub-noionion.oss-cn-hangzhou.aliyuncs.com/head.jpg"],
# ["Akilarの糖果屋", "https://akilar.top/", "https://akilar.top/images/headimage.png"],
# # 示例2:使用suffix的配置如下
# # 程序目前自动获取 "atom.xml", "feed/atom", "rss.xml", "rss2.xml", "feed", "index.xml" 这些后缀
# # 如果目标地址的后缀不在其中,就可以在这里添加
# # 比如 https://elizen.me/hello.xml ,填写:
# ["elizen", "https://elizen.me/", "https://akilar.top/images/headimage.png", "hello.xml"]
]
}

# get links from gitee
# 从gitee issue中获取友链
GITEE_FRIENDS_LINKS: {
enable: false, # true 开启gitee issue兼容
type: "normal", # volantis/stellar用户请在这里填写volantis
owner: "ccknbc", # 填写你的gitee用户名
repo: "blogroll", # 填写你的gitee仓库名
state: "open", # 填写抓取的issue状态(open/closed)
}

# get links from github
# 从github issue中获取友链
GITHUB_FRIENDS_LINKS: {
enable: false, # true 开启github issue兼容
type: "normal", # volantis/stellar用户请在这里填写volantis
owner: "ccknbc", # 填写你的github用户名
repo: "ccknbc-actions", # 填写你的github仓库名
state: "open", # 填写抓取的issue状态(open/closed)
label: "active", # 填写抓取的issue标签
}

# block site list
# 添加屏蔽站点,支持正则表达式
BLOCK_SITE: [
# https://example.com/, # 屏蔽 https://example.com/
# .*\.com, # 含有.com的全部屏蔽
# ^http://, # http开头的全部屏蔽
]

# 每个友链最多获取几篇文章,此值越大,则抓取的文章上限越多,相应地运行速度也会降低,反之亦然
# 请设置一个正整数
MAX_POSTS_NUM: 4

# 启用HTTP代理,此项设为true,并且需要添加一个环境变量,名称为PROXY,值为[IP]:[端口],比如:192.168.1.106:8080
HTTP_PROXY: false

# 过期文章清除(天)
OUTDATE_CLEAN: 30

# 5.x以后默认为sqlite,同时不建议使用leancloud
# 存储方式,可选项:leancloud,mysql,sqlite,mongodb;默认为sqlite
DATABASE: "sqlite"

# 部署方式,可选项:github,server,docker;默认为github
DEPLOY_TYPE: "github"

修改如下:

  • 屏蔽其中的link中的所有链接:
  • 设置SETTINGS_FRIENDS_LINKSenable选项为true,然后在下面链接填写你的文件网络地址,注意是网络地址,好像还没有适配本地地址来着……,网络地址需要生成friend.json后上传并部署一次,然后才能通过网络路径[你的网站根地址]/friend.json访问。
  • (可选)下面的选项包括屏蔽站点(是我的功能多余了呵呵),每个友链获取文章数目等等选项,文档里写的很详细,这里就不教怎么部署了,如果有问题可以评论区提出来。

然后按照相关教程部署后,添加前端到对应页面即可。本站的预览页面:

好了本次教程结束啦!欢迎参考原教程进行更多的学习,链接在下面,如果有什么不懂的可以到评论区问我,我们下篇文章见!

参考教程

放松一下

超级好看的壁纸

——柳影曳曳,清酒孤灯,扬笔撒墨,心境如霜