
火芯的小窝 - 个人主页技术解析(后续更改不更新文章!)
项目地址:https://www.hxdxw.cn
技术栈:原生 HTML + CSS + JavaScript,无任何框架依赖
这是一个极简风格个人主页,采用「单页 + 组件化」架构。页面仅包含四个核心组件:
- 👤 头像卡片:带动画气泡的个人资料展示
- 🔗 导航按钮:跳转到博客等子站
- 🎵 音乐播放器:悬浮式 HTML5 播放器
- 🌙 主题切换:支持明/暗模式一键切换
设计理念:简约不简单,每个组件都经过精心打磨。
📑 目录
目录结构
www.hxdxw.cn/
├── index.html # 主页面
├── style.css # 全局样式
├── CNAME # 域名配置
├── nginx.htaccess # Nginx 配置
├── README.md # 项目说明
└── xf-MusicPlayer/ # 音乐播放器组件
├── js/
├── css/
├── icon/
├── images/
└── README.mdHTML 结构
整个页面结构清晰,采用语义化标签:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>火芯的小窝</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- 主题切换按钮 -->
<button class="theme-toggle" id="theme-toggle" aria-label="切换主题">
<svg id="theme-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<!-- 太阳图标(白天) -->
<circle cx="12" cy="12" r="5"/>
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42..."/>
</svg>
</button>
<!-- 个人资料卡片 -->
<section class="profile-wrap" aria-label="个人资料">
<div class="avatar-wrap">
<!-- 气泡(悬停显示) -->
<div class="bubble" aria-hidden="true">Hello World!</div>
<!-- 头像 -->
<img class="avatar"
src="https://q1.qlogo.cn/g?b=qq&nk=915678975&s=640"
alt="火芯Ah 的头像" />
</div>
<div class="profile-info">
<p class="profile-name">火芯Ah</p>
<p class="profile-desc">这是一句个人描述。</p>
</div>
</section>
<!-- 导航按钮 -->
<nav class="nav-buttons" aria-label="站点导航">
<a class="nav-btn" href="http://blog.hxdxw.cn" target="_blank">
<svg>...</svg>
<span>火芯の博客</span>
</a>
</nav>
<!-- 音乐播放器 -->
<div id="xf-MusicPlayer"
data-fadeOutAutoplay
data-themeColor="xf-wineRed"
data-randomSongList="1">
</div>
<script src="/xf-MusicPlayer/js/xf-MusicPlayer.min.js"></script>
</body>
</html>设计要点:
aria-label提供无障碍访问支持- SVG 图标内联,无额外请求
- 播放器通过
data-*属性配置,开箱即用
CSS 架构
1. CSS 变量系统
使用 CSS Custom Properties 实现主题切换:
/* 明亮主题变量 */
:root {
--bg: #f5f5f7;
--card-bg: #ffffff;
--text-primary: #1d1d1f;
--text-secondary: #6e6e73;
--accent: #0071e3;
--accent-hover: #0077ed;
--shadow-sm: 0 2px 8px rgba(0,0,0,.08);
--shadow-md: 0 8px 32px rgba(0,0,0,.12);
--radius: 18px;
--transition: .3s cubic-bezier(.4,0,.2,1);
}
/* 暗色主题变量 */
[data-theme="dark"] {
--bg: #292929;
--card-bg: #393939;
--text-primary: #f5f5f5;
--text-secondary: #b0b0b0;
--accent: #0a84ff;
--accent-hover: #1a9cff;
--shadow-sm: 0 2px 8px rgba(0,0,0,.28);
--shadow-md: 0 8px 32px rgba(0,0,0,.35);
}优势:
- 只需切换
data-theme属性,所有颜色自动切换 - 变量统一管理,维护成本低
cubic-bezier自定义缓动曲线,过渡更自然
2. 头像卡片组件
/* 头像容器 */
.avatar-wrap {
position: relative;
flex-shrink: 0;
}
/* 气泡 - 默认隐藏 */
.bubble {
position: absolute;
bottom: calc(100% - 8px); /* 紧贴头像顶部 */
left: calc(100% - 8px); /* 紧贴头像右侧 */
background: var(--accent);
color: #fff;
font-size: 12px;
padding: 5px 10px;
border-radius: 12px 12px 12px 0; /* 左下角直角 → 尖角 */
opacity: 0;
transform: scale(.7) translateY(4px);
transform-origin: bottom left;
transition: opacity var(--transition), transform var(--transition);
pointer-events: none;
}
/* 气泡尖角 */
.bubble::after {
content: '';
position: absolute;
bottom: -6px;
left: 0;
border-width: 6px 6px 0 0;
border-style: solid;
border-color: var(--accent) transparent transparent transparent;
}
/* 头像 */
.avatar {
width: 96px;
height: 96px;
border-radius: 50%;
object-fit: cover;
box-shadow: var(--shadow-md);
cursor: pointer;
transition: transform var(--transition), box-shadow var(--transition);
border: 3px solid var(--card-bg);
}
/* 悬停时气泡 + 头像动画 */
.profile-wrap:hover .bubble {
opacity: 1;
transform: scale(1) translateY(0);
}
.profile-wrap:hover .avatar {
transform: scale(1.05);
box-shadow: 0 12px 40px rgba(0,0,0,.16);
}技术亮点:
transform-origin: bottom left控制气泡从尖角处展开calc()精确定位气泡尖角指向头像- 悬停时双重动画:气泡淡入 + 头像缩放
3. 导航按钮
.nav-btn {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 10px 22px;
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
background: var(--card-bg);
border: 1.5px solid rgba(0,0,0,.08);
border-radius: 50px;
text-decoration: none;
box-shadow: var(--shadow-sm);
position: relative;
overflow: hidden; /* 关键:裁剪伪元素 */
transition: color var(--transition), border-color var(--transition),
box-shadow var(--transition), transform var(--transition);
}
/* 渐变背景层 */
.nav-btn::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, var(--accent) 0%, #5ac8fa 100%);
opacity: 0;
transition: opacity var(--transition);
border-radius: inherit;
z-index: 0;
}
/* 文字和图标提升到最上层 */
.nav-btn span,
.nav-btn svg {
position: relative;
z-index: 1;
}
/* 悬停效果 */
.nav-btn:hover::before {
opacity: 1;
}
.nav-btn:hover {
color: #fff;
border-color: transparent;
box-shadow: 0 6px 24px rgba(0,113,227,.28);
transform: translateY(-2px);
}设计亮点:
- 伪元素实现渐变填充效果
position: relative+z-index控制层叠顺序inset: 0简写替代top/right/bottom/left: 0
4. 主题切换
.theme-toggle {
position: fixed;
bottom: 20px;
right: 20px;
width: 35px;
height: 35px;
border-radius: 50%;
background: var(--card-bg);
border: 1.5px solid rgba(0,0,0,.08);
box-shadow: var(--shadow-sm);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all var(--transition);
z-index: 1000;
}
.theme-toggle:hover {
transform: scale(1.1);
box-shadow: var(--shadow-md);
}
.theme-toggle svg {
width: 24px;
height: 24px;
color: var(--text-primary);
transition: color var(--transition);
}
/* 暗色模式下边框变亮 */
[data-theme="dark"] .theme-toggle {
border-color: rgba(255,255,255,.1);
}JavaScript 功能
1. 主题切换逻辑
// 获取元素
const themeToggle = document.getElementById('theme-toggle');
const themeIcon = document.getElementById('theme-icon');
const html = document.documentElement;
// 检查系统偏好和本地存储
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const savedTheme = localStorage.getItem('theme');
// 初始化主题
if (savedTheme) {
html.setAttribute('data-theme', savedTheme);
updateIcon(savedTheme);
} else if (prefersDark) {
html.setAttribute('data-theme', 'dark');
updateIcon('dark');
}
// 点击切换
themeToggle.addEventListener('click', () => {
const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme); // 持久化
updateIcon(newTheme);
});
// 更新图标(太阳 ↔ 月亮)
function updateIcon(theme) {
if (theme === 'dark') {
// 月亮图标
themeIcon.innerHTML = `<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>`;
} else {
// 太阳图标
themeIcon.innerHTML = `
<circle cx="12" cy="12" r="5"/>
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42..."/>
`;
}
}优先级:用户选择 > 系统偏好 > 默认(light)
2. 系统偏好监听
// 监听系统主题变化
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
// 仅当用户未手动选择主题时响应
if (!localStorage.getItem('theme')) {
const newTheme = e.matches ? 'dark' : 'light';
html.setAttribute('data-theme', newTheme);
updateIcon(newTheme);
}
});音乐播放器组件
播放器采用第三方插件 xf-MusicPlayer,配置简单:
<div id="xf-MusicPlayer"
data-fadeOutAutoplay <!-- 淡出自动播放 -->
data-themeColor="xf-wineRed" <!-- 酒红主题 -->
data-randomSongList="1"> <!-- 随机歌单 -->
</div>
<script src="/xf-MusicPlayer/js/xf-MusicPlayer.min.js"></script>支持的 data 属性:
| 属性 | 说明 | 可选值 |
|---|---|---|
data-themeColor | 主题颜色 | xf-original, xf-sky, xf-orange, xf-wineRed, xf-girlPink |
data-randomSongList | 随机歌单 | 1 或空 |
data-songChart | 歌榜类型 | 热歌榜, 新歌榜, 飙升榜, 原创榜 |
data-songList | 歌单ID | 网易云歌单ID |
data-memory | 记忆播放 | 1 |
data-bottomHeight | 底部距离 | 100px 等 |
文件结构
huoxin_cn/
├── index.html # 主页面(简洁单文件)
├── style.css # 全局样式(约230行)
├── CNAME # hxdxw.cn 域名
├── nginx.htaccess # Nginx 配置(CDN/缓存)
├── README.md # 项目说明
└── xf-MusicPlayer/ # 第三方播放器组件
├── js/ # 播放器脚本
├── css/ # 播放器样式
├── icon/ # 图标字体
└── images/ # 默认封面技术总结
| 技术点 | 应用场景 |
|---|---|
| CSS Custom Properties | 主题切换系统 |
| CSS 变量覆盖 | 暗色模式实现 |
::before / ::after | 气泡、渐变背景 |
transform + transition | 悬停动画效果 |
localStorage | 用户偏好持久化 |
matchMedia API | 系统主题监听 |
data-* 属性 | 组件配置化 |
| 第三方组件集成 | 音乐播放器 |
设计理念:
- 极简主义:页面干净清爽,无多余元素
- 细节打磨:每个动画、每处交互都经过思考
- 配置优先:通过 HTML 属性而非 JS 配置组件
希望这篇解析对你理解前端项目开发有所帮助! 🚀
个人页面预览:www.hxdxw.cn
个人页面github仓库:https://github.com/shihuoxin/hxdxw.github.io
xf-MusicPlayer作者github仓库:https://github.com/cenguigui/xf-MusicPlayer-master
太厉害了👍🏻😍😍😍