博客UI优化之旅:打造个性化Hexo博客

博客UI优化之旅:打造个性化Hexo博客

在搭建个人博客的过程中,除了内容的质量,博客的视觉体验同样重要。一个精心设计的UI不仅能提升用户体验,还能彰显个人品味。本文将分享我在优化Hexo博客UI过程中的经验和技巧,希望能为同样热爱折腾的你提供一些启发。

🎨 整体设计理念

在开始具体的优化工作前,我花了不少时间思考博客的整体设计理念。最终,我确定了以下几点:

  • 简约而不简单:避免过多的视觉干扰,但保留足够的设计细节
  • 响应式设计:在各种设备上都能提供良好的浏览体验
  • 性能优先:确保加载速度快,交互流畅
  • 个性化表达:融入个人风格,与大众博客有所区别

带着这些理念,我开始了一系列的优化工作。

🚀 加载体验优化

自定义加载动画

网站加载时的第一印象非常重要。我设计了一个简洁的巫师加载动画,让等待变得有趣。

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
// 修复巫师加载动画显示时间和隐藏问题
(function() {
'use strict';

console.log('🧙‍♂️ 巫师加载动画修复脚本启动');

let loadingStartTime = Date.now();
let isLoadingHidden = false;

// 智能隐藏加载动画
function smartHideLoading() {
if (isLoadingHidden) return;

const loadingBox = document.getElementById('loading-box');
if (!loadingBox) return;

const currentTime = Date.now();
const elapsedTime = currentTime - loadingStartTime;
const minDisplayTime = 2500; // 最少显示2.5秒

if (elapsedTime < minDisplayTime) {
// 如果显示时间不足,延迟隐藏
const remainingTime = minDisplayTime - elapsedTime;
setTimeout(() => {
hideLoadingAnimation();
}, remainingTime);
} else {
// 时间足够,立即隐藏
hideLoadingAnimation();
}
}

// 隐藏加载动画
function hideLoadingAnimation() {
if (isLoadingHidden) return;

const loadingBox = document.getElementById('loading-box');
if (!loadingBox) return;

// 标记为已隐藏
isLoadingHidden = true;

// 添加loaded类,触发主题的隐藏动画
loadingBox.classList.add('loaded');

// 确保页面可以正常滚动
document.body.style.overflow = 'auto';
}
})();

这段代码解决了加载动画显示时间过短或过长的问题,确保用户体验的一致性。同时,我还添加了超时保护机制,防止加载动画卡住。

图片懒加载优化

为了加快页面加载速度,我对图片懒加载进行了优化:

1
2
3
4
5
6
# 图片懒加载
lazyload:
enable: true
field: site
placeholder: data:image/svg+xml;base64,...
blur: true

这种配置不仅能延迟加载图片,还能在加载前显示模糊的占位图,提供更好的视觉过渡。

🌙 暗色模式增强

暗色模式切换修复

暗色模式是现代网站的标配,但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
// 修复夜间模式按钮点击问题
(function() {
'use strict';

function initDarkmodeFix() {
const darkmodeButton = document.getElementById('darkmode');

if (!darkmodeButton) return;

// 检查是否已有点击事件
const hasClickEvent = darkmodeButton.onclick ||
darkmodeButton.getAttribute('onclick') ||
darkmodeButton.hasAttribute('data-click-bound');

if (hasClickEvent) return;

// 添加点击事件
darkmodeButton.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();

// 方法1:使用主题的darkmode函数
if (typeof btf !== 'undefined' && typeof btf.darkmode === 'function') {
btf.darkmode();
return;
}

// 方法2:手动切换主题
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

// 切换主题
document.documentElement.setAttribute('data-theme', newTheme);

// 保存到本地存储
try {
if (typeof btf !== 'undefined' && btf.saveToLocal) {
btf.saveToLocal.set('theme', newTheme, 2);
} else {
localStorage.setItem('theme', newTheme);
}
} catch (error) {
console.warn('保存主题设置失败:', error);
}
});

// 标记已绑定事件
darkmodeButton.setAttribute('data-click-bound', 'true');
}

// 多种初始化时机
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initDarkmodeFix);
} else {
initDarkmodeFix();
}

// 延迟初始化,确保主题脚本已加载
setTimeout(initDarkmodeFix, 500);
})();

自定义暗色模式样式

除了修复功能外,我还为暗色模式定制了专属样式:

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
/* 暗色模式适配 */
[data-theme="dark"] .nav-search-input {
border-color: rgba(255, 255, 255, 0.2);
background: rgba(0, 0, 0, 0.3);
color: #fff;
}

[data-theme="dark"] .nav-search-input:focus {
border-color: rgba(255, 255, 255, 0.4);
background: rgba(0, 0, 0, 0.5);
}

[data-theme="dark"] .nav-search-results {
background: #2d3748;
border: 1px solid #4a5568;
}

[data-theme="dark"] .nav-search-item {
border-bottom-color: #4a5568;
}

[data-theme="dark"] .nav-search-item:hover {
background-color: #4a5568;
}

[data-theme="dark"] .nav-search-title {
color: #e2e8f0;
}

[data-theme="dark"] .nav-search-content {
color: #a0aec0;
}

这些样式确保了暗色模式下各元素的视觉协调性,提供了更舒适的夜间阅读体验。

🔍 搜索功能增强

导航栏搜索

为了方便用户快速查找内容,我在导航栏添加了搜索功能:

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
// 轻量级导航栏搜索功能
(function() {
'use strict';

let searchData = [];
let isDataLoaded = false;
let searchTimeout = null;

// 初始化搜索功能
function initSearch() {
const searchInput = document.getElementById('nav-search-input');
const searchResults = document.getElementById('nav-search-results');

if (!searchInput || !searchResults) return;

// 绑定事件
searchInput.addEventListener('input', handleInput);
searchInput.addEventListener('focus', handleFocus);
searchInput.addEventListener('keydown', handleKeydown);

// 点击外部关闭结果
document.addEventListener('click', function(e) {
if (!e.target.closest('.nav-search-container')) {
hideResults();
}
});

// 加载搜索数据
loadSearchData();
}

// 加载搜索数据
async function loadSearchData() {
try {
const response = await fetch('/search.xml');
if (!response.ok) throw new Error('Failed to fetch search data');

const xmlText = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, 'text/xml');

const entries = xmlDoc.querySelectorAll('entry');
searchData = Array.from(entries).map(entry => ({
title: entry.querySelector('title')?.textContent || '',
content: entry.querySelector('content')?.textContent?.replace(/<[^>]+>/g, '') || '',
url: entry.querySelector('url')?.textContent || ''
}));

isDataLoaded = true;
} catch (error) {
console.warn('Search data loading failed:', error);
}
}
})();

搜索历史记录

为了提升用户体验,我还添加了搜索历史记录功能:

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
// 搜索功能增强 - 性能优化版
(function() {
'use strict';

let searchHistory = [];
let isInitialized = false;

// 延迟初始化,避免阻塞页面加载
setTimeout(initSearchEnhance, 1000);

function initSearchEnhance() {
if (isInitialized) return;
isInitialized = true;

// 加载搜索历史
loadSearchHistory();

// 添加键盘快捷键
addKeyboardShortcuts();

// 监听搜索输入
enhanceSearchInput();
}

// 加载搜索历史
function loadSearchHistory() {
try {
const stored = localStorage.getItem('blog-search-history');
searchHistory = stored ? JSON.parse(stored) : [];
} catch (e) {
searchHistory = [];
}
}

// 保存搜索历史
function saveSearchHistory(query) {
if (!query || query.length < 2) return;

// 移除重复项并添加到开头
searchHistory = searchHistory.filter(item => item !== query);
searchHistory.unshift(query);

// 限制历史记录数量
searchHistory = searchHistory.slice(0, 8);

try {
localStorage.setItem('blog-search-history', JSON.stringify(searchHistory));
} catch (e) {
// 忽略存储错误
}
}
})();

🤖 AI摘要功能

为了让读者快速了解文章内容,我添加了AI摘要功能:

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
// 轻量级AI摘要功能 - 性能优化版
(function() {
'use strict';

// 只在文章页面显示AI摘要
if (!document.querySelector('.post-content')) return;

// 延迟初始化,避免阻塞页面加载
setTimeout(initAISummary, 500);

function initAISummary() {
const aiSummaryHTML = `
<div class="ai-summary" id="ai-summary">
<div class="ai-summary-header">
<div class="ai-summary-left">
<div class="ai-summary-icon">AI</div>
<h3 class="ai-summary-title">AI摘要</h3>
</div>
<div class="ai-summary-right">
<span class="ai-summary-badge">智能生成</span>
<button class="ai-summary-refresh" onclick="refreshAISummary()">
<i class="fas fa-sync-alt"></i>
</button>
</div>
</div>
<div class="ai-summary-content">
<div class="ai-summary-text" id="ai-summary-text">
<div class="loading-dots">
<span></span>
<span></span>
<span></span>
</div>
正在生成摘要...
</div>
</div>
<div class="ai-summary-footer">
<div class="ai-summary-info">
<i class="fas fa-robot"></i>
由AI智能生成
</div>
</div>
</div>
`;

const postContent = document.querySelector('.post-content');
if (postContent) {
postContent.insertAdjacentHTML('beforebegin', aiSummaryHTML);
setTimeout(generateAISummary, 800);
}
}

// 打字机效果函数
function typeWriter(element, text, speed = 40) {
element.innerHTML = '';
let i = 0;

function type() {
if (i < text.length) {
element.innerHTML += text.charAt(i);
i++;
setTimeout(type, speed);
} else {
// 打字完成后添加光标闪烁效果
element.innerHTML += '<span class="typing-cursor"></span>';
}
}

type();
}
})();

这个功能通过分析文章内容,自动生成摘要,并以打字机效果呈现,增加了交互的趣味性。

📊 性能监控

为了持续优化博客性能,我添加了轻量级性能监控:

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
// 轻量级性能监控
(function() {
'use strict';

// 只在开发环境或需要时启用
const ENABLE_MONITORING = false; // 设置为 true 启用监控

if (!ENABLE_MONITORING) return;

let performanceData = {
pageLoadTime: 0,
domContentLoadedTime: 0,
firstPaintTime: 0,
resourceLoadTime: 0,
jsExecutionTime: 0
};

// 监控页面加载性能
function monitorPageLoad() {
window.addEventListener('load', function() {
setTimeout(() => {
const navigation = performance.getEntriesByType('navigation')[0];
const paint = performance.getEntriesByType('paint');

if (navigation) {
performanceData.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.fetchStart);
performanceData.domContentLoadedTime = Math.round(navigation.domContentLoadedEventEnd - navigation.fetchStart);
}

if (paint.length > 0) {
const firstPaint = paint.find(entry => entry.name === 'first-paint');
if (firstPaint) {
performanceData.firstPaintTime = Math.round(firstPaint.startTime);
}
}

// 输出性能数据
console.group('🚀 页面性能监控');
console.log('页面加载时间:', performanceData.pageLoadTime + 'ms');
console.log('DOM加载时间:', performanceData.domContentLoadedTime + 'ms');
console.log('首次绘制时间:', performanceData.firstPaintTime + 'ms');

// 性能评级
const rating = getPerformanceRating(performanceData.pageLoadTime);
console.log('性能评级:', rating.text, rating.emoji);
console.groupEnd();
}, 100);
});
}
})();

🎨 自定义CSS样式

除了功能性优化,我还添加了大量自定义CSS样式,使博客更具个性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 评论区容器 - 高级磨砂卡片效果 */
.waline-container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
margin: 2rem 0;
padding: 1.5rem;
transition: all 0.3s ease;
}

.waline-container:hover {
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}

这些样式为博客添加了现代感和立体感,提升了整体视觉效果。

📱 响应式设计优化

为了确保在各种设备上的良好体验,我对响应式设计进行了优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* 响应式设计 */
@media (max-width: 768px) {
.nav-search-container {
display: none;
}

.waline-container {
padding: 1rem !important;
}

.wl-avatar {
width: 28px !important;
height: 28px !important;
}

.wl-head,
.wl-content,
.wl-meta {
padding: 0.5rem !important;
}
}

这些样式确保了在移动设备上的良好显示效果,提供了更好的移动端体验。

🔧 配置优化

最后,我对Butterfly主题的配置进行了优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 代码块设置
code_blocks:
theme: light
macStyle: false
height_limit: false
word_wrap: false
copy: true
language: true
shrink: false
fullpage: false

# 社交链接
social:
fab fa-github: https://github.com/swkajnBruceee || Github || '#24292e'
fas fa-envelope: mailto:your-email@example.com || Email || '#4a7dbe'
fab fa-twitter: https://twitter.com/ || Twitter || '#00acee'
fab fa-telegram: https://t.me/ || Telegram || '#0088cc'

这些配置使博客的功能更加丰富,同时保持了视觉的一致性。

🎁 总结与展望

通过这一系列的UI优化,我的Hexo博客不仅在视觉上更加吸引人,功能上也更加完善。当然,博客优化是一个持续的过程,未来我计划:

  1. 进一步优化加载速度:使用更先进的资源压缩和缓存策略
  2. 添加更多交互元素:如阅读进度条、目录自动展开等
  3. 优化SEO:提升博客在搜索引擎中的排名
  4. 增强无障碍功能:让博客对所有用户都友好

希望这篇文章能给你的博客优化之旅带来一些启发。如果你有任何问题或建议,欢迎在评论区留言交流!


本文首发于个人博客,转载请注明出处。
如果你喜欢这篇文章,欢迎分享给更多的人!