新手魔改博客前,最好是将自己的博客配置完,认真阅读过Butterfly官方文档,使用两三个月过后,再来逐步了解魔改的过程。这样就会避免大多数的上手难度。

其次,在你进行魔改前,必要时最好提前备份一遍你的源代码,以免造成不必要的麻烦。
譬如:效果不理想,没达到预期;魔改出问题,不小心改错了等等。

文件位置辨别

在本站的教程内,会写有[博客根目录][主题目录]两种。

  • 博客根目录:指你本地的博客项目文件夹,也就是你 clone 的项目文件夹。

  • 主题目录:指你butterfly主题文件夹,git安装或npm安装的主题文件夹。

如果你实在找不到你的主题目录,请移步: Butterfly官方文档 - 安装

添加自定义文件

魔改或美化都离不开添加自定义css和js文件,而这些的文件位置和使用是必不可少的。

一般情况下,推荐在博客根目录source文件夹内存放你自己的css和js文件。至于主题目录内的source文件,由于更新后会被覆盖掉,所以不建议。

  • 新建[博客根目录]/source/js/meuicat.js文件。

  • 新建[博客根目录]/source/css/meuicat.css文件。

  • _config.butterfly.yml主题配置文件中inject下的headbottom分别引入meuicat.css以及meuicat.js

1
2
3
4
5
6
7
inject:
head:
# 自定义css
- <link rel="stylesheet" href="/css/meuicat.css">
bottom:
# 自定义js
- <script src="/js/meuicat.js"></script>

如果你实在找不到你的主题配置文件,请移步: Butterfly官方文档 - 升级建议

CSS颜色变量

魔改或美化教程里有大量CSS变量代码,例如:color: var(–icat-theme) 等等。
所以当你首次使用本站魔改教程前,需要在你的css样式文件中,添加以下代码:

  • 创建[主题目录]/source/css/_global/meuicat.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
:root
--icat-000 0,0,0
--icat-010 255,255,255
--icat-020 60,60,67
--icat-black-op rgbas('var(--icat-000)', .15)
--icat-gray-op #9999992b
--icat-white rgbas('var(--icat-010)', 1)
--icat-white-op rgbas('var(--icat-010)', .15)
--icat-green #3e9f50
--icat-yellow #ffc93e
--icat-red #ff3842
--icat-purple #7a60d2
--icat-none rgbas('var(--icat-000)', 0)
--icat-border-main 1px solid var(--icat-theme)
--icat-border-always 1px solid var(--icat-card-border)
--icat-border-none 1px solid var(--icat-none)
--icat-border-dashed 1px dashed var(--icat-card-border)
--icat-shadow 0 8px 16px -4px

[data-theme='light']
--icat-theme rgbas('var(--icat-blue)', 1)
--icat-theme-op rgbas('var(--icat-blue)', .14)
--icat-theme-deep rgbas('var(--icat-blue)', .42)
--icat-blue 66,90,239
--icat-pink #d80020
--icat-fontcolor #363636
--icat-background #f7f9fe
--icat-card-bg rgbas('var(--icat-010)', 1)
--icat-card-overlap rgbas('var(--icat-010)', 1)
--icat-card-border #e3e8f7
--icat-shadow-border var(--icat-shadow) #2c2d300c
--icat-shadow-main var(--icat-shadow) var(--icat-theme-op)
--icat-mask rgbas('var(--icat-010)', .85)
--icat-mask-op rgbas('var(--icat-010)', .6)
--icat-secondbg #f7f7f9
--icat-secondtext rgbas('var(--icat-020)', .8)
--icat-sparent var(--icat-white-op)

if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
[data-theme='dark']
--icat-theme rgbas('var(--icat-orange)', 1)
--icat-theme-op rgbas('var(--icat-orange)', .14)
--icat-theme-deep rgbas('var(--icat-orange)', .42)
--icat-orange 255,149,62
--icat-pink #d44040
--icat-fontcolor #F7F7FA
--icat-background #18171d
--icat-card-bg #1b1c20
--icat-card-overlap #161823
--icat-card-border #3d3d3f
--icat-shadow-border var(--icat-shadow) rgbas('var(--icat-000)', .314)
--icat-shadow-main var(--icat-shadow) var(--icat-theme-op)
--icat-mask rgbas('var(--icat-000)', .85)
--icat-mask-op rgbas('var(--icat-000)', .6)
--icat-secondbg #21232a
--icat-secondtext #a1a2b8
--icat-sparent var(--icat-black-op)

版本迭代后,可能会有些许变化,可自行通过F12获取。

icon 图标

本站的魔改教程中,有部分包含icon图标。你可以选择引用我的图标库直接使用,又或者将其改成你自己的图标。

  • _config.butterfly.yml主题配置文件中inject下的head引入iconfont.css
1
2
3
4
5
6
inject:
head:
# 自定义css
- <link rel="stylesheet" href="/css/meuicat.css">
# 引入腾讯CoDesign图标库
- <link rel="stylesheet" href="https://cdn2.codesign.qq.com/icons/xnjPoz51lMm6KZL/latest/iconfont.css" media="print" onload="this.media='all'">

通用样式

为避免重复样式冗余,可自行选择对应的通用样式。小节内容会随着本站魔改教程系列的更新而更新。

cardHovers

  • 打开[主题目录]/source/css/_global/meuicat.styl样式文件,并新增以下内容。
1
2
3
4
5
6
7
8
9
10
.cardHovers
background: var(--icat-card-bg)
border-radius 8px
border var(--icat-border-always)
box-shadow: var(--icat-shadow-border)
transition: all .3s

&:hover
border var(--icat-border-main)
box-shadow: var(--icat-shadow-main)

aplayer

  • 创建[主题目录]/source/css/_layout/aplayer.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
.aplayer.aplayer-withlrc
background var(--icat-secondbg)
text-align center
border var(--icat-border-always)
border-radius 8px
box-shadow initial

.aplayer-pic
.aplayer-button path
transition all .1s ease

&:hover
.aplayer-button,
.aplayer-button path
fill var(--icat-theme)
border-color var(--icat-theme)

.aplayer-lrc
&:after,
&:before
display none

p.aplayer-lrc-current
color var(--icat-theme)

.aplayer-info
.aplayer-controller
.aplayer-bar-wrap .aplayer-bar
background var(--icat-card-border)
height 8px
border-radius 99px
transition .3s
overflow hidden

.aplayer-loaded
height 100%
background var(--icat-theme-op)

.aplayer-played
height 100%
border-radius 99px

.aplayer-thumb
display none

.aplayer-time
position initial

.aplayer-icon:hover path
fill var(--icat-theme)

.aplayer-music .aplayer-title
font-weight 700

通用函数

本站魔改教程中,有部分会使用到相同的辅助函数,为了减少重复代码,重复使用两次以上的通用辅助函数,将会放在本小节内。

小节内容会随着本站魔改教程系列的更新而更新。通用函数将添加在[博客根目录]/source/js/meuicat.js文件中,即可。
唯一需要注意的是,同一个常量函数,只需声明一次。

changeTime

所需范围:Butterfly的魔改教程:最新评论页、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
const changeTime = (time, more = false) => {
const currentDate = new Date()

const formatTimestamp = (date) => {
const d = new Date(date)
const pad = (num) => String(num).padStart(2, '0')
return `${pad(d.getFullYear())}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` + `${pad(d.getHours())}:${pad(d.getMinutes())}`
}

const calculateDiff = (date1, date2, unit) => {
const units = { day: 24 * 60 * 60 * 1000, hour: 60 * 60 * 1000 }
return Math.floor(Math.abs(date1 - date2) / units[unit])
}

const describeTime = (datetime) => {
const timeObj = new Date(datetime)
const diffDays = calculateDiff(timeObj, currentDate, 'day')
const diffHours = calculateDiff(timeObj, currentDate, 'hour')

if (diffHours < 1) return `最近`
if (diffHours <= 24) return `${diffHours}小时前`
if (diffDays === 1) return `昨天`
if (diffDays === 2) return `前天`
if (diffDays <= 7) return `${diffDays}天前`

const year = timeObj.getFullYear()
const month = timeObj.getMonth() + 1
const date = timeObj.getDate()
return year !== currentDate.getFullYear() ? `${year}/${month}/${date}` : `${month}/${date}`
}

if (more) return formatTimestamp(time)
if (time) return describeTime(time)

document.querySelectorAll('time.datatime').forEach((e) => { e.textContent = describeTime(e.getAttribute('datetime')) })
}