📌 前言

版本信息:
Butterfly 主题版本:5.5.0
教程更新日期:2025-10-13
本教程将为 Butterfly 主题添加华丽的昼夜切换动画效果

📂 文件修改清单

✅ 新增文件(2个)

  1. themes/butterfly/source/js/sun_moon.js - 动画核心逻辑
  2. themes/butterfly/source/css/_layout/sun_moon.styl - 动画样式

✏️ 修改文件(3个)

  1. themes/butterfly/source/css/index.styl
  2. themes/butterfly/layout/includes/additional-js.pug
  3. themes/butterfly/source/js/main.js

🔧 详细实现步骤

步骤一:创建动画 JavaScript 文件

文件路径: themes/butterfly/source/js/sun_moon.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
function updateDarkmodeIcon(mode) {
const buttonElement = document.getElementById('darkmode');
if (buttonElement) {
const iconElement = buttonElement.querySelector('i');
if (iconElement) {
if (mode === 'sun') {
iconElement.classList.remove('fa-moon');
iconElement.classList.add('fa-sun');
} else {
iconElement.classList.remove('fa-sun');
iconElement.classList.add('fa-moon');
}
}
}
}


function switchNightMode() {
const existingSky = document.querySelector('.Cuteen_DarkSky');
if (existingSky) {
existingSky.remove();
}

const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';

const bodyElement = document.querySelector('body');
bodyElement.insertAdjacentHTML('beforeend', '<div class="Cuteen_DarkSky"><div class="Cuteen_DarkPlanet"></div></div>');

const planetElement = document.querySelector('.Cuteen_DarkPlanet');

if (nowMode === 'dark') {
planetElement.classList.add('to-moon');
} else {
planetElement.classList.add('to-sun');
}

setTimeout(function() {
if (nowMode === 'light') {
planetElement.classList.remove('to-sun');
planetElement.classList.add('to-moon');
} else {
planetElement.classList.remove('to-moon');
planetElement.classList.add('to-sun');
}
}, 1000);

setTimeout(function() {
const willChangeMode = nowMode === 'light' ? 'dark' : 'light';

if (nowMode === 'light') {
btf.activateDarkMode();
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night);
updateDarkmodeIcon('sun');
} else {
btf.activateLightMode();
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day);
updateDarkmodeIcon('moon');
}

btf.saveToLocal.set('theme', willChangeMode, 2);

typeof utterancesTheme === 'function' && utterancesTheme();
typeof FB === 'object' && window.loadFBComment();
window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200);
}, 100);

setTimeout(function() {
const skyElement = document.querySelector('.Cuteen_DarkSky');
if (skyElement) {
skyElement.style.transition = 'opacity 1.5s';
skyElement.style.opacity = '0';
setTimeout(function() {
if (skyElement.parentNode) {
skyElement.remove();
}
}, 1500);
}
}, 2000);
}

document.addEventListener('DOMContentLoaded', function() {
const currentMode = document.documentElement.getAttribute('data-theme');
if (currentMode === 'dark') {
updateDarkmodeIcon('sun');
} else {
updateDarkmodeIcon('moon');
}
});

步骤二:创建动画样式文件

文件路径: themes/butterfly/source/css/_layout/sun_moon.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
.Cuteen_DarkSky,
.Cuteen_DarkSky:before
content ''
position fixed
left 0
right 0
top 0
bottom 0
z-index 88888888

.Cuteen_DarkSky
background linear-gradient(to bottom, #87CEEB 0%, #98D8E8 50%, #E0F6FF 100%)

&:before
transition 2s ease all
opacity 0
background linear-gradient(to bottom, #0a0e27 0%, #1a1f3a 30%, #2d1b4e 70%, #1e0533 100%)

[data-theme="dark"] .Cuteen_DarkSky:before
opacity 1

.Cuteen_DarkPlanet
z-index 99999999
position fixed
left -50%
top -50%
width 200%
height 200%
-webkit-animation CuteenPlanetMove 2s cubic-bezier(0.7, 0, 0, 1)
animation CuteenPlanetMove 2s cubic-bezier(0.7, 0, 0, 1)
transform-origin center bottom

@-webkit-keyframes CuteenPlanetMove
0%
transform: rotate(0)
to
transform: rotate(360deg)

@keyframes CuteenPlanetMove
0%
transform: rotate(0)
to
transform: rotate(360deg)

.Cuteen_DarkPlanet:after
position absolute
left 35%
top 40%
width 9.375rem
height 9.375rem
border-radius 50%
content ''
background linear-gradient(135deg, #FDB813 0%, #FFE5A0 50%, #FFFBEA 100%)
box-shadow 0 0 60px rgba(255, 220, 100, 0.8), 0 0 100px rgba(255, 200, 50, 0.4)
transition all 0.3s ease

.Cuteen_DarkPlanet:before
content ''
position absolute
left 35%
top 40%
width 9.375rem
height 9.375rem
border-radius 50%
z-index 1
opacity 0
background-image radial-gradient(circle at 45% 35%, rgba(100, 100, 100, 0.4) 0%, transparent 8%), radial-gradient(circle at 65% 50%, rgba(80, 80, 80, 0.5) 0%, transparent 12%), radial-gradient(circle at 30% 60%, rgba(90, 90, 90, 0.45) 0%, transparent 6%), radial-gradient(circle at 55% 70%, rgba(70, 70, 70, 0.4) 0%, transparent 10%), radial-gradient(circle at 75% 30%, rgba(85, 85, 85, 0.35) 0%, transparent 5%), radial-gradient(circle at 25% 45%, rgba(95, 95, 95, 0.3) 0%, transparent 7%), radial-gradient(circle at 80% 65%, rgba(75, 75, 75, 0.4) 0%, transparent 9%), radial-gradient(circle at 40% 80%, rgba(88, 88, 88, 0.35) 0%, transparent 6%), radial-gradient(circle at 60% 25%, rgba(78, 78, 78, 0.3) 0%, transparent 5%), radial-gradient(circle at 20% 25%, rgba(92, 92, 92, 0.35) 0%, transparent 8%)
pointer-events none
transition opacity 0.3s ease

.Cuteen_DarkPlanet.to-moon:after
background radial-gradient(circle at 30% 30%, #f4f4f4, #e0e0e0 40%, #c8c8c8 70%, #a8a8a8)
box-shadow 0 0 80px rgba(230, 230, 255, 0.6), 0 0 120px rgba(200, 200, 255, 0.3), inset -20px -20px 40px rgba(0, 0, 0, 0.2)

.Cuteen_DarkPlanet.to-moon:before
opacity 1

.Cuteen_DarkPlanet.to-sun:before
opacity 0

步骤三:引入样式文件

文件路径: themes/butterfly/source/css/index.styl

原版内容(第1-15行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if hexo-config('css_prefix')
@import 'nib'

@import '_third-party/normalize.min.css'
// project
@import 'var'
@import '_global/*'
@import '_highlight/highlight'
@import '_page/*'
@import '_layout/*'
@import '_tags/*'
@import '_mode/*'

// search
@import '_search/index'

修改后(第1-18行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if hexo-config('css_prefix')
@import 'nib'

@import '_third-party/normalize.min.css'
// project
@import 'var'
@import '_global/*'
@import '_highlight/highlight'
@import '_page/*'
@import '_layout/*'
@import '_tags/*'
@import '_mode/*'

// search
@import '_search/index'

// custom //新增
@import '_layout/sun_moon' //新增

修改说明: 在文件末尾添加 @import '_layout/sun_moon' 引入动画样式。


步骤四:引入 JavaScript 文件

文件路径: themes/butterfly/layout/includes/additional-js.pug

搜索位置: 约第41-42行,找到 partial("includes/third-party/chat/index") 之后

原版内容:

1
2
3
4
!= partial("includes/third-party/effect", {}, { cache: true })
!= partial("includes/third-party/chat/index", {}, { cache: true })

if theme.aplayerInject && theme.aplayerInject.enable

修改后:

1
2
3
4
5
6
7
!= partial("includes/third-party/effect", {}, { cache: true })
!= partial("includes/third-party/chat/index", {}, { cache: true })

if theme.sun_moon_animation && theme.sun_moon_animation.enable //新增
script(src=url_for('/js/sun_moon.js')) // 新增

if theme.aplayerInject && theme.aplayerInject.enable

修改说明:

  • chat/indexaplayerInject 之间插入代码
  • 重要!注意缩进: if 使用 2 个空格,script 使用 4 个空格
  • Pug 对缩进非常敏感,缩进错误会导致 “Buffered code cannot have a block” 错误

步骤五:修改主题切换逻辑

文件路径: themes/butterfly/source/js/main.js

搜索位置: 约第607行,搜索 darkmode: () =>darkmode: 找到函数

原版内容:

1
2
3
4
5
6
7
8
9
10
11
12
darkmode: () => { // switch between light and dark mode
const willChangeMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'
if (willChangeMode === 'dark') {
btf.activateDarkMode()
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
} else {
btf.activateLightMode()
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
}
btf.saveToLocal.set('theme', willChangeMode, 2)
handleThemeChange(willChangeMode)
},

修改后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
darkmode: () => { // switch between light and dark mode
if (typeof switchNightMode === 'function') { //新增
switchNightMode() //新增
} else {
const willChangeMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'
if (willChangeMode === 'dark') {
btf.activateDarkMode()
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
} else {
btf.activateLightMode()
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
}
btf.saveToLocal.set('theme', willChangeMode, 2)
handleThemeChange(willChangeMode) // 如果原有这行,请务必保留
}
},

修改说明:

  • 在函数开头添加 if (typeof switchNightMode === 'function') 判断
  • if/else 包裹原有代码,不改变原有逻辑
  • 注意! 如果你的 main.jshandleThemeChange(willChangeMode) 这行,请保留
  • 函数结尾的 }, 别忘了

步骤六:添加配置开关

文件路径: _config.butterfly.yml

添加配置项:

1
2
3
# 昼夜切换动画
sun_moon_animation:
enable: true # 开启动画效果

配置说明:

  • enable: true - 启用昼夜切换动画
  • enable: false - 关闭动画,使用原版切换

🎨 技术亮点

1️⃣ 渐变背景设计

  • 白天天空: 从天蓝 #87CEEB 渐变到浅蓝 #E0F6FF
  • 夜晚星空: 从深蓝 #0a0e27 渐变到紫黑 #1e0533
  • 使用 linear-gradient 实现自然过渡

2️⃣ 太阳/月亮切换

  • 太阳渐变: 金黄 #FDB813 → 浅黄 #FFFBEA,带光晕 box-shadow
  • 月亮渐变: 白色 #f4f4f4 → 灰色 #a8a8a8,带内阴影
  • 月球陨石坑: 使用多个 radial-gradient 模拟真实纹理

3️⃣ 图标智能切换

  • 深色模式: 显示太阳图标 fa-sun(点击回到白天)
  • 浅色模式: 显示月亮图标 fa-moon(点击进入黑夜)
  • 页面加载时自动同步图标状态

4️⃣ 性能优化

  • 使用 setTimeout 控制动画时序
  • 动画结束后自动移除 DOM 元素
  • 防止重复动画(检查并移除已存在的元素)
  • z-index 层级管理(88888888 确保最顶层显示)

🚀 使用方法

1. 安装步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. 创建 JS 文件
新建 themes/butterfly/source/js/sun_moon.js
复制完整代码(见步骤一)

# 2. 创建样式文件
新建 themes/butterfly/source/css/_layout/sun_moon.styl
复制完整代码(见步骤二)

# 3. 修改引入文件
编辑 themes/butterfly/source/css/index.styl(见步骤三)
编辑 themes/butterfly/layout/includes/additional-js.pug(见步骤四)
编辑 themes/butterfly/source/js/main.js(见步骤五)

# 4. 添加配置
编辑 _config.butterfly.yml(见步骤六)

# 5. 清理缓存并生成
hexo clean
hexo generate

# 6. 本地预览
hexo server

2. 配置说明

1
2
3
# _config.butterfly.yml
sun_moon_animation:
enable: true # 是否启用动画

3. 效果测试

  1. 访问 http://localhost:4000
  2. 点击右侧昼夜切换按钮
  3. 观察动画效果:
    • 天空渐变
    • 太阳/月亮旋转

🎁 扩展建议

自定义颜色

修改 sun_moon.styl 中的渐变色值:

1
2
3
4
5
6
7
8
9
10
11
// 白天天空
background linear-gradient(to bottom, #你的颜色 0%, #你的颜色 100%)

// 夜晚星空
background linear-gradient(to bottom, #你的颜色 0%, #你的颜色 100%)

// 太阳渐变
background linear-gradient(135deg, #你的颜色 0%, #你的颜色 100%)

// 月亮渐变
background radial-gradient(circle at 30% 30%, #你的颜色, #你的颜色)

调整动画时长

修改 sun_moon.js 中的 setTimeout 时间:

1
2
3
4
5
// 旋转时长(默认 1000ms)
setTimeout(function() { ... }, 1000);

// 整体动画时长(默认 2000ms)
setTimeout(function() { ... }, 2000);

📝 总结

本教程通过 2 个新增文件3 个简单修改,为 Butterfly 主题添加了华丽的昼夜切换动画。核心技术包括:

  • ✅ CSS3 渐变和动画
  • ✅ JavaScript DOM 操作
  • ✅ 时序控制和状态管理
  • ✅ 性能优化和内存管理

动画效果自然流畅,代码结构清晰,易于维护和扩展。适合所有 Butterfly 主题用户使用!