文章摘要
静莹莹

前言

众所周知啊,Solitude 主题的核心是魔改,我也在Solitude魔改记录这篇文章里面分享过一些我的魔改经历,本来这篇文章的内容也是想放在那篇文章里面的,但是我想了想,那篇文章是5月31号发布的,到现在已经快3个月了,而且排在那么后面,估计是没人看了(事实证明也是如此,那篇文章到现在为止也只有9的浏览量)所以我就想趁着我更新这个机会,也来做个教程嘛,毕竟这个侧边栏卡片也是我最开始魔改的时候第一个就想弄的了(而且关键是没弄成)

OK,那话不多说,我们开始!

前置准备

创建自定义 css/js 文件

自定义 css 和 js 是一切魔改的基础,后面几乎所有的魔改都要依靠它的!一定要先准备好噢!

  1. 创建文件夹:

    找到里面有 _post 文件夹的那个 source 文件夹(blog/source,不是blog/themes/solitude/source),在里面创建 js 和 css 文件夹。

  2. 创建css/js文件:

    在 css 文件夹里面创建 custom.css 文件,在 js 文件夹里面创建 custom.js 文件。(这个文件也可以是其他名字,只要后缀名是 .css/.js 就行。)

  3. 引入css/js:

    在主题配置文件(不是站点的)里面的扩展部分中引入(Solitude 主题是在第770行左右)。

    css 文件在 head 引入,js 文件在 bottom 里引入。有的 js 要求在 head 引入,不然无法生效。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # --------------------------- start ---------------------------
    # Extend
    # 扩展
    extends:
    # Insert in head
    # 插入到 head
    head:
    - <link rel="stylesheet" href="/css/custom.css">
    # Insert in body
    # 插入到 body
    body:
    - <script defer pjax src="/js/custom.js"></script>
    # --------------------------- end ---------------------------

    引入多个文件可以直接往下复制粘贴多添加一行然后改一下文件名就行。

创建 aside.yml 文件

在 _data 文件夹中创建 aside.yml 文件,这个是专门用来放侧边栏卡片的配置文件的

来访者卡片

效果

来访者卡片

教程

  1. aside.yml 中添加以下配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - name: welcome
    title: 来访者
    class: card-welcome
    id:
    icon: fas fa-location-dot
    content_id: welcome-info
    content_css: 'height:160px;overflow:hidden'
    content_html: '<div class="welcome_swiper-container" id="welcome-container" style="width: 100%;height: 100%;margin-top: 6px">
    <div id="welcome_container_wrapper" class="swiper-wrapper"></div>
    </div>'
  2. custom.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
    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
    	let ipLocation; // 确保 ipLocation 在全局范围内定义

    // 进行 fetch 请求
    fetch('https://api.nsmao.net/api/ipip/query?key=密钥')
    .then(response => {
    if (!response.ok) {
    throw new Error('Network response was not ok');
    }
    return response.json();
    })
    .then(data => {
    ipLocation = data;
    if (isHomePage()) {
    showWelcome();
    }
    })
    .catch(error => console.error('Error:', error));

    function getDistance(e1, n1, e2, n2) {
    const R = 6371;
    const { sin, cos, asin, PI, hypot } = Math;
    let getPoint = (e, n) => {
    e *= PI / 180;
    n *= PI / 180;
    return { x: cos(n) * cos(e), y: cos(n) * sin(e), z: sin(n) };
    };

    let a = getPoint(e1, n1);
    let b = getPoint(e2, n2);
    let c = hypot(a.x - b.x, a.y - b.y, a.z - b.z);
    let r = asin(c / 2) * 2 * R;
    return Math.round(r);
    }

    function showWelcome() {
    if (!ipLocation || !ipLocation.data) {
    return;
    }

    let dist = getDistance(111.749181, 40.842585, ipLocation.data.lng, ipLocation.data.lat);
    let pos = ipLocation.data.country;
    let ip = ipLocation.ip;
    let posdesc;

    switch (ipLocation.data.country) {
    case "日本":
    posdesc = "よろしく,一起去看樱花吗";
    break;
    case "美国":
    posdesc = "Let us live in peace!";
    break;
    case "英国":
    posdesc = "想同你一起夜乘伦敦眼";
    break;
    case "俄罗斯":
    posdesc = "干了这瓶伏特加!";
    break;
    case "法国":
    posdesc = "C'est La Vie";
    break;
    case "德国":
    posdesc = "Die Zeit verging im Fluge.";
    break;
    case "澳大利亚":
    posdesc = "一起去大堡礁吧!";
    break;
    case "加拿大":
    posdesc = "拾起一片枫叶赠予你";
    break;
    case "中国":
    pos = ipLocation.data.province + " " + ipLocation.data.city;
    switch (ipLocation.data.province) {
    case "北京":
    posdesc = "北——京——欢迎你~~~";
    break;
    case "上海":
    posdesc = "走在外滩,感受历史与现代的交融。";
    break;
    case "广东":
    switch (ipLocation.data.city) {
    case "广州":
    posdesc = "看小蛮腰,喝早茶了嘛~";
    break;
    case "深圳":
    posdesc = "今天你逛商场了嘛~";
    break;
    case "珠海":
    posdesc = "浪漫之城珠海,海风轻拂。";
    break;
    case "东莞":
    posdesc = "东莞,制造业之都,经济活跃。";
    break;
    case "佛山":
    posdesc = "佛山,武术之乡,陶瓷文化深厚。";
    break;
    default:
    posdesc = "带你感受广东的热情与美食!";
    break;
    }
    break;
    case "浙江":
    switch (ipLocation.data.city) {
    case "杭州":
    posdesc = "西湖美景,三月天~";
    break;
    case "宁波":
    posdesc = "来宁波,感受大海的气息。";
    break;
    case "温州":
    posdesc = "温州人杰地灵,商贸繁荣。";
    break;
    case "绍兴":
    posdesc = "绍兴,酒乡文化,古韵悠长。";
    break;
    case "湖州":
    posdesc = "湖州,太湖之滨,风景如画。";
    break;
    default:
    posdesc = "这里是浙江,充满江南的韵味!";
    break;
    }
    break;
    case "四川":
    switch (ipLocation.data.city) {
    case "成都":
    posdesc = "宽窄巷子,成都慢生活。";
    break;
    case "绵阳":
    posdesc = "享受科技城的宁静与创新。";
    break;
    case "自贡":
    posdesc = "自贡的盐文化与灯会,独具魅力。";
    break;
    case "德阳":
    posdesc = "德阳,历史悠久,文化底蕴深厚。";
    break;
    case "乐山":
    posdesc = "乐山大佛,世界文化遗产。";
    break;
    default:
    posdesc = "来四川,品麻辣火锅,赏壮丽山河。";
    break;
    }
    break;
    case "福建":
    switch (ipLocation.data.city) {
    case "厦门":
    posdesc = "鼓浪屿听海,厦门美食让人流连忘返。";
    break;
    case "福州":
    posdesc = "有福之州,来此感受千年古城。";
    break;
    case "泉州":
    posdesc = "泉州,海上丝绸之路的起点。";
    break;
    case "漳州":
    posdesc = "漳州,古城文化与美食的结合。";
    break;
    case "南平":
    posdesc = "南平,武夷山的自然风光。";
    break;
    default:
    posdesc = "福建山水如画,美景无处不在。";
    break;
    }
    break;
    case "山东":
    switch (ipLocation.data.city) {
    case "青岛":
    posdesc = "来青岛喝啤酒,看大海吧!";
    break;
    case "济南":
    posdesc = "泉城济南,四面荷花三面柳。";
    break;
    case "烟台":
    posdesc = "烟台的葡萄酒与海鲜,令人陶醉。";
    break;
    case "潍坊":
    posdesc = "潍坊,风筝之都,文化底蕴深厚。";
    break;
    case "德州":
    posdesc = "德州,扒鸡闻名,文化悠久。";
    break;
    default:
    posdesc = "山东好客,欢迎来感受齐鲁文化!";
    break;
    }
    break;
    case "江苏":
    switch (ipLocation.data.city) {
    case "南京":
    posdesc = "六朝古都南京,历史与现代的碰撞。";
    break;
    case "苏州":
    posdesc = "来苏州,感受园林之美。";
    break;
    case "无锡":
    posdesc = "无锡太湖美景,灵山大佛令人心旷神怡。";
    break;
    case "常州":
    posdesc = "常州,文化与科技的交汇点。";
    break;
    case "南通":
    posdesc = "南通,海门潮涌,文化底蕴深厚。";
    break;
    default:
    posdesc = "水乡泽国,江南佳丽地。";
    break;
    }
    break;
    case "河北":
    posdesc = "燕赵大地,英雄辈出的河北,等你探索!";
    break;
    case "河南":
    switch (ipLocation.data.city) {
    case "郑州":
    posdesc = "中原大地,郑州是交通枢纽与历史重镇。";
    break;
    case "洛阳":
    posdesc = "千年古都洛阳,牡丹花开的城。";
    break;
    case "开封":
    posdesc = "开封,古都文化与美食的汇聚地。";
    break;
    case "新乡":
    posdesc = "新乡,历史悠久,文化底蕴深厚。";
    break;
    case "焦作":
    posdesc = "焦作,云台山的自然风光。";
    break;
    default:
    posdesc = "这里是河南,历史悠久文化灿烂。";
    break;
    }
    break;
    case "湖南":
    switch (ipLocation.data.city) {
    case "长沙":
    posdesc = "热辣长沙,吃小龙虾逛黄兴路步行街。";
    break;
    case "岳阳":
    posdesc = "岳阳楼,洞庭湖的美景尽收眼底。";
    break;
    case "株洲":
    posdesc = "株洲,火车制造业的发源地。";
    break;
    case "湘潭":
    posdesc = "湘潭,伟人故里,文化底蕴深厚。";
    break;
    default:
    posdesc = "湖南,烟雨迷蒙的湘江流过这片土地。";
    break;
    }
    break;
    case "湖北":
    switch (ipLocation.data.city) {
    case "武汉":
    posdesc = "来大武汉,过长江大桥,吃热干面!";
    break;
    case "宜昌":
    posdesc = "三峡大坝,壮丽的自然奇观。";
    break;
    case "荆州":
    posdesc = "荆州,历史文化名城,古韵悠长。";
    break;
    case "襄阳":
    posdesc = "襄阳,古城文化与美食的结合。";
    break;
    default:
    posdesc = "湖北,长江中游的明珠,风景秀丽。";
    break;
    }
    break;
    case "安徽":
    switch (ipLocation.data.city) {
    case "合肥":
    posdesc = "创新之城合肥,科教文化汇聚地。";
    break;
    case "黄山":
    posdesc = "黄山,天下第一奇山,风景如画。";
    break;
    case "芜湖":
    posdesc = "芜湖,长江之畔,文化底蕴深厚。";
    break;
    case "马鞍山":
    posdesc = "马鞍山,文化与自然的完美结合。";
    break;
    default:
    posdesc = "安徽山水,黄山、九华山欢迎你。";
    break;
    }
    break;
    case "广西":
    switch (ipLocation.data.city) {
    case "桂林":
    posdesc = "桂林山水甲天下,风景如画。";
    break;
    case "南宁":
    posdesc = "绿城南宁,宜居宜游。";
    break;
    case "柳州":
    posdesc = "柳州的螺蛳粉,独具风味。";
    break;
    case "防城港":
    posdesc = "防城港,海洋资源丰富,风景迷人。";
    break;
    default:
    posdesc = "广西山清水秀,民俗风情浓郁。";
    break;
    }
    break;
    case "贵州":
    switch (ipLocation.data.city) {
    case "贵阳":
    posdesc = "贵阳,山城之美,民族风情浓郁。";
    break;
    case "遵义":
    posdesc = "遵义,红色之城,历史悠久。";
    break;
    case "安顺":
    posdesc = "安顺,黄果树瀑布的故乡,风景如画。";
    break;
    case "毕节":
    posdesc = "毕节,拥有丰富的自然资源与人文景观。";
    break;
    case "六盘水":
    posdesc = "六盘水,凉爽的夏天,避暑胜地。";
    break;
    case "铜仁":
    posdesc = "铜仁,秀美的山水与独特的民族文化。";
    break;
    case "凯里":
    posdesc = "凯里,苗族文化的发源地,风情独特。";
    break;
    default:
    posdesc = "来贵州,品茅台,赏黄果树瀑布。";
    break;
    }
    break;
    case "云南":
    switch (ipLocation.data.city) {
    case "昆明":
    posdesc = "春城昆明,四季如春,风景秀丽。";
    break;
    case "大理":
    posdesc = "苍山洱海,大理古城,你来了就不想走。";
    break;
    case "丽江":
    posdesc = "丽江古城,纳西文化的瑰宝。";
    break;
    case "西双版纳":
    posdesc = "西双版纳,热带雨林的奇妙之地。";
    break;
    default:
    posdesc = "云南风景独特,风情万种。";
    break;
    }
    break;
    case "西藏":
    switch (ipLocation.data.city) {
    case "拉萨":
    posdesc = "拉萨,西藏的首府,布达拉宫的故乡。";
    break;
    case "日喀则":
    posdesc = "日喀则,历史悠久的文化名城。";
    break;
    case "林芝":
    posdesc = "林芝,素有“西藏江南”之称,风景如画。";
    break;
    case "昌都":
    posdesc = "昌都,历史悠久,文化底蕴深厚。";
    break;
    case "山南":
    posdesc = "山南,藏文化的发源地之一。";
    break;
    case "那曲":
    posdesc = "那曲,草原风光,牧民生活的地方。";
    break;
    case "阿里":
    posdesc = "阿里,神秘的西部,拥有壮丽的自然景观。";
    break;
    default:
    posdesc = "西藏,神秘而纯净,等待你的探索。";
    break;
    }
    break;
    case "新疆维吾尔":
    posdesc = "辽阔新疆,民族风情与壮丽景观并存。";
    switch (ipLocation.data.city) {
    case "乌鲁木齐":
    posdesc = "乌鲁木齐,天山脚下的城市,文化多元。";
    break;
    case "喀什":
    posdesc = "喀什,古丝绸之路的重要节点,历史悠久。";
    break;
    case "克拉玛依":
    posdesc = "克拉玛依,石油之城,经济发展迅速。";
    break;
    case "吐鲁番":
    posdesc = "吐鲁番,火焰山的故乡,葡萄之乡。";
    break;
    case "哈密":
    posdesc = "哈密,哈密瓜的发源地,风景如画。";
    break;
    case "博乐":
    posdesc = "博乐,草原风光,民族文化交融。";
    break;
    case "阿克苏":
    posdesc = "阿克苏,苹果之乡,风景秀丽。";
    break;
    case "和田":
    posdesc = "和田,玉石之乡,历史文化深厚。";
    break;
    default:
    posdesc = "新疆的城市各具特色,等待你的探索。";
    break;
    }
    break;
    case "内蒙古":
    switch (ipLocation.data.city) {
    case "呼和浩特":
    posdesc = "呼和浩特,内蒙古的首府,历史悠久。";
    break;
    case "包头":
    posdesc = "包头,钢铁之城,经济发展迅速。";
    break;
    case "乌兰察布":
    posdesc = "乌兰察布,草原文化与现代城市的结合。";
    break;
    case "赤峰":
    posdesc = "赤峰,拥有丰富的自然资源与人文景观。";
    break;
    case "通辽":
    posdesc = "通辽,草原文化的发源地,风情独特。";
    break;
    case "鄂尔多斯":
    posdesc = "鄂尔多斯,现代化城市与草原文化的交融。";
    break;
    case "巴彦淖尔":
    posdesc = "巴彦淖尔,黄河之畔,风景如画。";
    break;
    case "锡林郭勒":
    posdesc = "锡林郭勒,草原辽阔,马背上的民族风情。";
    break;
    default:
    posdesc = "草原辽阔的内蒙古,等你来策马奔腾。";
    break;
    }
    break;
    case "宁夏回族":
    posdesc = "宁夏,塞上江南,黄河流经的美丽地方。";
    break;
    case "海南":
    posdesc = "阳光、沙滩、椰风海韵,欢迎来海南度假。";
    break;
    case "陕西":
    switch (ipLocation.data.city) {
    case "西安":
    posdesc = "西安,古都文化与兵马俑的故乡。";
    break;
    case "咸阳":
    posdesc = "咸阳,历史悠久,文化底蕴深厚。";
    break;
    default:
    posdesc = "陕西,历史与文化的交汇之地。";
    break;
    }
    break;
    case "甘肃":
    switch (ipLocation.data.city) {
    case "兰州":
    posdesc = "兰州,黄河之滨,牛肉面闻名。";
    break;
    case "天水":
    posdesc = "天水,历史悠久,文化底蕴深厚。";
    break;
    default:
    posdesc = "甘肃,丝绸之路的重要节点。";
    break;
    }
    break;
    case "青海":
    switch (ipLocation.data.city) {
    case "西宁":
    posdesc = "西宁,青海湖的门户,风景如画。";
    break;
    default:
    posdesc = "青海,湖泊与草原的美丽结合。";
    break;
    }
    break;
    case "吉林":
    switch (ipLocation.data.city) {
    case "长春":
    posdesc = "长春,汽车城,文化底蕴深厚。";
    break;
    case "吉林市":
    posdesc = "吉林市,松花江畔,风景如画。";
    break;
    default:
    posdesc = "吉林,冰雪与文化的交融之地。";
    break;
    }
    break;
    case "黑龙江":
    switch (ipLocation.data.city) {
    case "哈尔滨":
    posdesc = "哈尔滨,冰雪之城,俄罗斯风情浓厚。";
    break;
    case "齐齐哈尔":
    posdesc = "齐齐哈尔,黑龙江的明珠,文化底蕴深厚。";
    break;
    default:
    posdesc = "黑龙江,冰雪与文化的交汇之地。";
    break;
    }
    break;
    default:
    posdesc = "带我去你的城逛逛吧!";
    break;
    }
    break;
    default:
    posdesc = "带我去你的国家逛逛吧";
    break;
    }

    // 根据本地时间切换欢迎语
    let timeChange;
    let date = new Date();
    if (date.getHours() >= 5 && date.getHours() < 11) timeChange = "<span>🌤️ 早上好,一日之计在于晨</span>";
    else if (date.getHours() >= 11 && date.getHours() < 13) timeChange = "<span>☀️ 中午好,记得午休喔~</span>";
    else if (date.getHours() >= 13 && date.getHours() < 17) timeChange = "<span>🕞 下午好,饮茶先啦!</span>";
    else if (date.getHours() >= 17 && date.getHours() < 19) timeChange = "<span>🚶‍♂️ 即将下班,记得按时吃饭~</span>";
    else if (date.getHours() >= 19 && date.getHours() < 24) timeChange = "<span>🌙 晚上好,夜生活嗨起来!</span>";
    else timeChange = "夜深了,早点休息,少熬夜";

    let welcomeInfoElement = document.getElementById("welcome-info");

    if (welcomeInfoElement) {
    welcomeInfoElement.innerHTML =
    `欢迎来自 <b><span style="color: var(--efu-main)">${pos}</span></b> 的小友💖<br>当前位置距博主约 <b><span style="color: var(--efu-main)">${dist.toFixed(2)}</span></b> 公里!<br>${timeChange}<br>Tip:<b><span style="font-size: 15px;">${posdesc}</span></b>`;
    }
    }

    function handlePjaxComplete() {
    if (isHomePage()) {
    showWelcome();
    }
    }

    function isHomePage() {
    return window.location.pathname === '/' || window.location.pathname === '/index.html';
    }

    window.onload = function () {
    if (isHomePage()) {
    showWelcome();
    }
    document.addEventListener("pjax:complete", handlePjaxComplete);
    }
  3. 获取奶思猫 API 密钥

    1. 打开以下网站:

    2. 注册账号

    3. 进入后台 => 密钥管理 => 复制密钥

    4. 填写到 js 代码中(第4行,将“密钥”换成你自己的密钥)

  4. 获取你的位置经纬度

    1. 打开以下网站:

    2. 搜索你现在所在的位置,复制结果

    3. 填写到 js 代码中(第40行,将“111.749181, 40.842585”换成你自己的经纬度)

  5. 打开Solitude配置文件(不是站点的),在 Aside 项中配置(Solitude 主题在120行左右):

    1
    2
    3
    4
    aside: 
    home: # on the homepage
    noSticky: "about,welcome"
    Sticky: "allInfo"

参考

日历卡片

效果

日历卡片

教程

  1. 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
    .card-widget {
    padding: 10px !important;
    max-height: calc(100vh - 100px);
    position: relative;
    }

    .card-times a,
    .card-times div {
    color: var(--efu-fontcolor);
    }

    #card-widget-calendar .item-content {
    display: flex;
    }

    #calendar-area-left {
    width: 45%;
    }

    #calendar-area-right {
    width: 55%;
    }

    #calendar-area-left,
    #calendar-area-right {
    height: 100%;
    padding: 4px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    }

    #calendar-main {
    width: 100%;
    }

    #calendar-week {
    height: 1.2rem;
    font-size: 14px;
    letter-spacing: 1px;
    font-weight: 700;
    align-items: center;
    display: flex;
    }

    #calendar-date {
    height: 3rem;
    line-height: 1.3;
    font-size: 64px;
    letter-spacing: 3px;
    color: var(--efu-main);
    font-weight: 700;
    align-items: center;
    display: flex;
    position: absolute;
    top: calc(50% - 2.1rem);
    }

    #calendar-lunar,
    #calendar-solar {
    height: 1rem;
    font-size: 12px;
    align-items: center;
    display: flex;
    position: absolute;
    }

    #calendar-solar {
    bottom: 2.1rem;
    }

    #calendar-lunar {
    bottom: 1rem;
    color: var(--efu-secondtext);
    }

    #calendar-main a {
    height: 1rem;
    width: 1rem;
    border-radius: 50%;
    font-size: 12px;
    line-height: 12px;
    display: flex;
    justify-content: center;
    align-items: center;
    }

    #calendar-main a.now {
    background: var(--efu-main);
    color: var(--efu-card-bg);
    }

    #calendar-main .calendar-rh a {
    color: var(--efu-secondtext);
    }

    .calendar-r0,
    .calendar-r1,
    .calendar-r2,
    .calendar-r3,
    .calendar-r4,
    .calendar-r5,
    .calendar-rh {
    height: 1.2rem;
    display: flex;
    }

    .calendar-d0,
    .calendar-d1,
    .calendar-d2,
    .calendar-d3,
    .calendar-d4,
    .calendar-d5,
    .calendar-d6 {
    width: calc(100% / 7);
    display: flex;
    justify-content: center;
    align-items: center;
    }

    #card-widget-schedule .item-content {
    display: flex;
    }

    #schedule-area-left,
    #schedule-area-right {
    height: 100px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    }

    #schedule-area-left {
    width: 30%;
    }

    #schedule-area-right {
    width: 70%;
    padding: 0 5px;
    }

    .schedule-r0,
    .schedule-r1,
    .schedule-r2 {
    height: 2rem;
    width: 100%;
    align-items: center;
    display: flex;
    }

    .schedule-d0 {
    width: 30px;
    margin-right: 5px;
    text-align: center;
    font-size: 12px;
    }

    .schedule-d1 {
    width: calc(100% - 35px);
    height: 1.5rem;
    align-items: center;
    display: flex;
    }

    progress::-webkit-progress-bar {
    background: linear-gradient(
    to right,
    var(--efu-main-op-deep),
    var(--efu-main-op),
    var(--efu-main-op-light)
    );
    border-radius: 5px;
    overflow: hidden;
    }

    progress::-webkit-progress-value {
    background: var(--efu-main);
    border-radius: 5px;
    }

    .aside-span1,
    .aside-span2 {
    height: 1rem;
    font-size: 12px;
    z-index: 1;
    display: flex;
    align-items: center;
    position: absolute;
    }

    .aside-span1 {
    margin-left: 5px;
    }

    .aside-span2 {
    right: 20px;
    color: var(--efu-secondtext);
    }

    .aside-span2 a {
    margin: 0 3px;
    }

    #pBar_month,
    #pBar_week,
    #pBar_year {
    width: 100%;
    border-radius: 5px;
    height: 100%;
    }

    #schedule-date,
    #schedule-days,
    #schedule-title {
    display: flex;
    align-items: center;
    }

    #schedule-title {
    height: 25px;
    line-height: 1;
    font-size: 14px;
    font-weight: 700;
    }

    #schedule-days {
    height: 40px;
    line-height: 1;
    font-size: 30px;
    font-weight: 900;
    color: var(--efu-main);
    }

    #schedule-date {
    height: 20px;
    line-height: 1;
    font-size: 12px;
    color: var(--efu-secondtext);
    }
  2. custom.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
    	document.addEventListener("DOMContentLoaded", () => {
    initializeCard();
    });

    document.addEventListener("pjax:complete", () => {
    initializeCard();
    });

    function initializeCard() {
    cardTimes();
    cardRefreshTimes();
    }

    let year,
    month,
    week,
    date,
    dates,
    weekStr,
    monthStr,
    asideTime,
    asideDay,
    asideDayNum,
    animalYear,
    ganzhiYear,
    lunarMon,
    lunarDay;
    const now = new Date();

    function cardRefreshTimes() {
    const e = document.getElementById("card-widget-schedule");
    if (e) {
    asideDay = (now - asideTime) / 1e3 / 60 / 60 / 24;
    e.querySelector("#pBar_year").value = asideDay;
    e.querySelector("#p_span_year").innerHTML =
    ((asideDay / 365) * 100).toFixed(1) + "%";
    e.querySelector(
    ".schedule-r0 .schedule-d1 .aside-span2"
    ).innerHTML = `还剩<a> ${(365 - asideDay).toFixed(0)} </a>天`;
    e.querySelector("#pBar_month").value = date;
    e.querySelector("#pBar_month").max = dates;
    e.querySelector("#p_span_month").innerHTML =
    ((date / dates) * 100).toFixed(1) + "%";
    e.querySelector(
    ".schedule-r1 .schedule-d1 .aside-span2"
    ).innerHTML = `还剩<a> ${dates - date} </a>天`;
    e.querySelector("#pBar_week").value = week === 0 ? 7 : week;
    e.querySelector("#p_span_week").innerHTML =
    (((week === 0 ? 7 : week) / 7) * 100).toFixed(1) + "%";
    e.querySelector(
    ".schedule-r2 .schedule-d1 .aside-span2"
    ).innerHTML = `还剩<a> ${7 - (week === 0 ? 7 : week)} </a>天`;
    }
    }

    function cardTimes() {
    year = now.getFullYear();
    month = now.getMonth();
    week = now.getDay();
    date = now.getDate();

    const e = document.getElementById("card-widget-calendar");
    if (e) {
    const isLeapYear =
    (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
    weekStr = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"][week];
    const monthData = [
    { month: "1月", days: 31 },
    { month: "2月", days: isLeapYear ? 29 : 28 },
    { month: "3月", days: 31 },
    { month: "4月", days: 30 },
    { month: "5月", days: 31 },
    { month: "6月", days: 30 },
    { month: "7月", days: 31 },
    { month: "8月", days: 31 },
    { month: "9月", days: 30 },
    { month: "10月", days: 31 },
    { month: "11月", days: 30 },
    { month: "12月", days: 31 },
    ];
    monthStr = monthData[month].month;
    dates = monthData[month].days;

    const t = (week + 8 - (date % 7)) % 7;
    let n = "",
    d = false,
    s = 7 - t;
    const o =
    (dates - s) % 7 === 0
    ? Math.floor((dates - s) / 7) + 1
    : Math.floor((dates - s) / 7) + 2;
    const c = e.querySelector("#calendar-main");
    const l = e.querySelector("#calendar-date");

    l.style.fontSize = ["64px", "48px", "36px"][Math.min(o - 3, 2)];

    for (let i = 0; i < o; i++) {
    if (!c.querySelector(`.calendar-r${i}`)) {
    c.innerHTML += `<div class='calendar-r${i}'></div>`;
    }
    for (let j = 0; j < 7; j++) {
    if (i === 0 && j === t) {
    n = 1;
    d = true;
    }
    const r = n === date ? " class='now'" : "";
    if (!c.querySelector(`.calendar-r${i} .calendar-d${j} a`)) {
    c.querySelector(
    `.calendar-r${i}`
    ).innerHTML += `<div class='calendar-d${j}'><a${r}>${n}</a></div>`;
    }
    if (n >= dates) {
    n = "";
    d = false;
    }
    if (d) {
    n += 1;
    }
    }
    }

    const lunarDate = chineseLunar.solarToLunar(new Date(year, month, date));
    animalYear = chineseLunar.format(lunarDate, "A");
    ganzhiYear = chineseLunar.format(lunarDate, "T").slice(0, -1);
    lunarMon = chineseLunar.format(lunarDate, "M");
    lunarDay = chineseLunar.format(lunarDate, "d");

    const newYearDate = new Date("2026/02/17 00:00:00");
    const daysUntilNewYear = Math.floor(
    (newYearDate - now) / 1e3 / 60 / 60 / 24
    );
    asideTime = new Date(`${new Date().getFullYear()}/01/01 00:00:00`);
    asideDay = (now - asideTime) / 1e3 / 60 / 60 / 24;
    asideDayNum = Math.floor(asideDay);
    const weekNum =
    week - (asideDayNum % 7) >= 0
    ? Math.ceil(asideDayNum / 7)
    : Math.ceil(asideDayNum / 7) + 1;

    e.querySelector(
    "#calendar-week"
    ).innerHTML = `第${weekNum}周&nbsp;${weekStr}`;
    e.querySelector("#calendar-date").innerHTML = date
    .toString()
    .padStart(2, "0");
    e.querySelector(
    "#calendar-solar"
    ).innerHTML = `${year}${monthStr}&nbsp;第${asideDay.toFixed(0)}天`;
    e.querySelector(
    "#calendar-lunar"
    ).innerHTML = `${ganzhiYear}${animalYear}年&nbsp;${lunarMon}${lunarDay}`;
    document.getElementById("schedule-days").innerHTML = daysUntilNewYear;
    }
    }
  3. aside.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
    	- name: schedule
    id: card-widget-schedule
    content_html: |
    <div id="schedule-area-left">
    <div id="schedule-title">距离除夕</div>
    <div id="schedule-days"></div>
    <div id="schedule-date">2025-01-28</div>
    </div>
    <div id="schedule-area-right">
    <div class="schedule-r0">
    <div class="schedule-d0">本年</div>
    <div class="schedule-d1">
    <span id="p_span_year" class="aside-span1"></span>
    <span class="aside-span2">还剩<a></a>天</span>
    <progress max="365" id="pBar_year"></progress>
    </div>
    </div>
    <div class="schedule-r1">
    <div class="schedule-d0">本月</div>
    <div class="schedule-d1">
    <span id="p_span_month" class="aside-span1"></span>
    <span class="aside-span2">还剩<a></a>天</span>
    <progress max="30" id="pBar_month"></progress>
    </div>
    </div>
    <div class="schedule-r2">
    <div class="schedule-d0">本周</div>
    <div class="schedule-d1">
    <span id="p_span_week" class="aside-span1"></span>
    <span class="aside-span2">还剩<a></a>天</span>
    <progress max="7" id="pBar_week"></progress>
    </div>
    </div>
    </div>

    - name: calendar
    id: card-widget-calendar
    content_html: |
    <div id="calendar-area-left">
    <div id="calendar-week"></div>
    <div id="calendar-date" style="font-size: 48px;"></div>
    <div id="calendar-solar"></div>
    <div id="calendar-lunar"></div>
    </div>
    <div id="calendar-area-right">
    <div id="calendar-main">
    </div>
    </div>
  4. 在 Solitude 配置文件中引入(Solitude 主题是在第770行左右):

    1
    2
    3
    4
    5
    6
    7
    # --------------------------- start ---------------------------
    extends:
    head:
    body:
    - <script src="https://open.lightxi.com/unpkg/chinese-lunar@0.1.4/lib/chinese-lunar.js"></script>
    # --------------------------- end ---------------------------

  5. 在 Aside 项中配置:

    1
    2
    3
    4
    aside: 
    home: # on the homepage
    noSticky: "about,calendar,schedule"
    Sticky: "allInfo"
  6. 修改倒计时日期(如果需要)

    aside.yml,第7行:

    1
    <div id="schedule-date">2025-01-28</div>

    custom.js,第128行:

    1
    const newYearDate = new Date("2026/02/17 00:00:00");

    把这两个修改成你自己的日期就可以啦!

参考

那年今日卡片

效果

那年今日卡片

教程

  1. 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
    	#aside-content .card-history .swiper-wrapper {
    flex-direction: column
    }

    #aside-content .card-history .item-headline {
    margin-left: 0;
    }

    #aside-content .card-history .history_slide {
    text-align: left;
    display: flex !important;
    flex-direction: column;
    align-items: flex-start
    }

    #aside-content .card-history .history_slide_time {
    color: var(--efu-secondtext);
    font-size: 14px;
    font-style: italic;
    font-weight: lighter
    }

    #aside-content .card-history .history_slide_link {
    line-height: 1.5;
    -webkit-line-clamp: 2;
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical
    }

    #aside-content .card-history .history_slide_link a {
    color: var(--efu-main);
    padding: 0 4px;
    border-radius: 4px
    }

    #aside-content .card-history .history_slide_link a:hover {
    color: var(--efu-white);
    background: var(--efu-main)
    }
  2. custom.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
    	 document.addEventListener('DOMContentLoaded', function () {
    async function cardHistory() {
    const historyContainer = document.getElementById('history-container');
    if (!historyContainer) return;

    const data = await fetchHistoryData();
    const html = data.map(item => `
    <div class="swiper-slide history_slide">
    <span class="history_slide_time">A.D.${item.year}</span>
    <span class="history_slide_link">${item.title}</span>
    </div>
    `).join('');

    const swiperContainer = document.querySelector('.history_swiper-container');
    document.getElementById('history_container_wrapper').innerHTML = html;

    const swiperHistory = new Swiper(swiperContainer, {
    loop: true,
    direction: 'vertical',
    autoplay: {disableOnInteraction: true, delay: 5000},
    mousewheel: false,
    });

    historyContainer.onmouseenter = () => swiperHistory.autoplay.stop();
    historyContainer.onmouseleave = () => swiperHistory.autoplay.start();
    }

    cardHistory();
    document.addEventListener('pjax:complete', cardHistory);

    async function fetchHistoryData() {
    const myDate = new Date();
    const month = `${myDate.getMonth() + 1}`.padStart(2, '0');
    const day = `${myDate.getDate()}`.padStart(2, '0');
    const formattedDate = `${month}${day}`;
    const historyDataUrl = `https://v2.xxapi.cn/api/history`; //申请:https://api.nsmao.net

    try {
    const response = await fetch(historyDataUrl);
    const result = await response.json();

    if (result.code === 200) {
    const data = result.data;
    const formattedData = Object.entries(data).map(([year, event]) => ({
    year: year.replace(/年$/, ''),
    title: event
    }));
    return formattedData;
    } else {
    console.error('Error fetching history data:', result.msg);
    return [];
    }
    } catch (error) {
    console.error('Fetch error:', error);
    return [];
    }
    }

    cardHistory()
    document.addEventListener('pjax:complete', cardHistory);
    })
  3. aside.yml 中添加以下配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    - name: history
    title: 那年今日
    class: card-history
    id:
    icon: fas fa-clock
    content_class:
    content_id: history-baidu
    content_css: 'height:80px;overflow:hidden'
    content_html: '<div class="history_swiper-container" id="history-container" style="width: 100%;height: 100%;margin-top: 6px">
    <div id="history_container_wrapper" class="swiper-wrapper"></div>
    </div>'
  4. 在 Aside 项中配置:

    1
    2
    3
    4
    aside: 
    home: # on the homepage
    noSticky: "about,history"
    Sticky: "allInfo"

参考

大功告成!

运行 hexo 三部曲就可以辣!

1
hexo cl && hexo g && hexo s