这个示例将使用 纯 HTML、CSS 和 JavaScript 实现,不依赖任何外部框架(如 Vue 或 React),以便您能清晰地理解其结构和实现原理。

(图片来源网络,侵删)
最终效果预览
我们将创建一个功能完整的单页应用,具有以下特点:
- 响应式设计:适配不同屏幕尺寸。
- 交互功能:包括导航栏切换、Tab 选项卡、轮播图、歌单卡片悬停效果等。
- 现代风格:使用 CSS 变量、Flexbox 和 Grid 布局,模仿 QQ 音乐的视觉风格。
第一步:项目文件结构
创建一个文件夹,并在其中创建以下三个文件:
qq-music-clone/
├── index.html
├── style.css
└── script.js
第二步:HTML 代码 (index.html)
这是页面的骨架,包含了所有可见的元素和结构。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">音乐馆 - QQ音乐</title>
<link rel="stylesheet" href="style.css">
<!-- 引入字体图标库 (这里使用 Font Awesome 作为示例) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<!-- 顶部导航栏 -->
<header class="main-header">
<div class="header-container">
<div class="logo">
<i class="fab fa-qq"></i>
<span>QQ音乐</span>
</div>
<nav class="main-nav">
<a href="#" class="active">音乐馆</a>
<a href="#">我的音乐</a>
<a href="#">客户端</a>
</nav>
<div class="search-bar">
<input type="text" placeholder="搜索音乐、MV、歌单、用户">
<button><i class="fas fa-search"></i></button>
</div>
<div class="user-actions">
<button class="login-btn">登录</button>
</div>
</div>
</header>
<!-- 主内容区 -->
<main class="main-content">
<!-- Banner 轮播图 -->
<section class="banner-section">
<div class="carousel-container">
<div class="carousel-wrapper">
<!-- 轮播图内容 -->
<div class="carousel-slide active">
<img src="https://picsum.photos/seed/banner1/1200/400.jpg" alt="Banner 1">
<div class="slide-info">
<h2>夏日歌单</h2>
<p>炎炎夏日,总需要一些旋律来降温</p>
</div>
</div>
<div class="carousel-slide">
<img src="https://picsum.photos/seed/banner2/1200/400.jpg" alt="Banner 2">
<div class="slide-info">
<h2>新歌首发</h2>
<p>本周新歌,抢先聆听</p>
</div>
</div>
<div class="carousel-slide">
<img src="https://picsum.photos/seed/banner3/1200/400.jpg" alt="Banner 3">
<div class="slide-info">
<h2>经典老歌</h2>
<p>回忆杀,那些年的感动</p>
</div>
</div>
</div>
<!-- 轮播控制按钮 -->
<button class="carousel-btn prev"><i class="fas fa-chevron-left"></i></button>
<button class="carousel-btn next"><i class="fas fa-chevron-right"></i></button>
<!-- 轮播指示器 -->
<div class="carousel-indicators">
<span class="indicator active" data-index="0"></span>
<span class="indicator" data-index="1"></span>
<span class="indicator" data-index="2"></span>
</div>
</div>
</section>
<!-- 推荐歌单 -->
<section class="recommend-section">
<div class="section-header">
<h2>推荐歌单</h2>
<div class="tabs">
<button class="tab-btn active" data-tab="personal">个性推荐</button>
<button class="tab-btn" data-tab="official">官方歌单</button>
<button class="tab-btn" data-tab="ranking">排行榜</button>
</div>
</div>
<div class="playlist-grid">
<!-- 歌单卡片 1 -->
<div class="playlist-card">
<div class="playlist-cover">
<img src="https://picsum.photos/seed/playlist1/200/200.jpg" alt="歌单封面">
<div class="play-overlay">
<i class="fas fa-play-circle"></i>
</div>
<span class="play-count"><i class="fas fa-play"></i> 128万</span>
</div>
<div class="playlist-info">
<h3>深夜emo,一个人听</h3>
<p>为你推荐,适合深夜独处时聆听的音乐</p>
</div>
</div>
<!-- 歌单卡片 2 -->
<div class="playlist-card">
<div class="playlist-cover">
<img src="https://picsum.photos/seed/playlist2/200/200.jpg" alt="歌单封面">
<div class="play-overlay">
<i class="fas fa-play-circle"></i>
</div>
<span class="play-count"><i class="fas fa-play"></i> 89万</span>
</div>
<div class="playlist-info">
<h3>华语经典 · 90年代</h3>
<p>带你重温那个华语乐坛黄金年代的旋律</p>
</div>
</div>
<!-- 歌单卡片 3 -->
<div class="playlist-card">
<div class="playlist-cover">
<img src="https://picsum.photos/seed/playlist3/200/200.jpg" alt="歌单封面">
<div class="play-overlay">
<i class="fas fa-play-circle"></i>
</div>
<span class="play-count"><i class="fas fa-play"></i> 256万</span>
</div>
<div class="playlist-info">
<h3>欧美流行 Top 50</h3>
<p>最新最热的欧美流行歌曲,走在潮流前线</p>
</div>
</div>
<!-- 歌单卡片 4 -->
<div class="playlist-card">
<div class="playlist-cover">
<img src="https://picsum.photos/seed/playlist4/200/200.jpg" alt="歌单封面">
<div class="play-overlay">
<i class="fas fa-play-circle"></i>
</div>
<span class="play-count"><i class="fas fa-play"></i> 45万</span>
</div>
<div class="playlist-info">
<h3>轻音乐 · 专注阅读</h3>
<p>没有歌词干扰,让你沉浸在阅读的世界里</p>
</div>
</div>
<!-- 歌单卡片 5 -->
<div class="playlist-card">
<div class="playlist-cover">
<img src="https://picsum.photos/seed/playlist5/200/200.jpg" alt="歌单封面">
<div class="play-overlay">
<i class="fas fa-play-circle"></i>
</div>
<span class="play-count"><i class="fas fa-play"></i> 167万</span>
</div>
<div class="playlist-info">
<h3>电音节拍 · 摇滚起来</h3>
<p>强劲的节奏,释放你的激情与活力</p>
</div>
</div>
<!-- 歌单卡片 6 -->
<div class="playlist-card">
<div class="playlist-cover">
<img src="https://picsum.photos/seed/playlist6/200/200.jpg" alt="歌单封面">
<div class="play-overlay">
<i class="fas fa-play-circle"></i>
</div>
<span class="play-count"><i class="fas fa-play"></i> 92万</span>
</div>
<div class="playlist-info">
<h3>治愈系 · 民谣小调</h3>
<p>一把吉他,一段故事,温暖你的心房</p>
</div>
</div>
</div>
<div class="more-link">
<a href="#">更多 <i class="fas fa-chevron-right"></i></a>
</div>
</section>
<!-- 新歌首发 -->
<section class="new-songs-section">
<div class="section-header">
<h2>新歌首发</h2>
<a href="#">更多 <i class="fas fa-chevron-right"></i></a>
</div>
<div class="songs-list">
<div class="song-item">
<div class="song-cover">
<img src="https://picsum.photos/seed/song1/80/80.jpg" alt="歌曲封面">
</div>
<div class="song-info">
<h3>夏日清风</h3>
<p>歌手:林俊杰</p>
</div>
<div class="song-actions">
<button class="play-btn"><i class="fas fa-play"></i></button>
<button class="add-btn"><i class="fas fa-plus"></i></button>
</div>
</div>
<div class="song-item">
<div class="song-cover">
<img src="https://picsum.photos/seed/song2/80/80.jpg" alt="歌曲封面">
</div>
<div class="song-info">
<h3>城市夜景</h3>
<p>歌手:邓紫棋</p>
</div>
<div class="song-actions">
<button class="play-btn"><i class="fas fa-play"></i></button>
<button class="add-btn"><i class="fas fa-plus"></i></button>
</div>
</div>
<div class="song-item">
<div class="song-cover">
<img src="https://picsum.photos/seed/song3/80/80.jpg" alt="歌曲封面">
</div>
<div class="song-info">
<h3>追梦赤子心</h3>
<p>歌手:GALA</p>
</div>
<div class="song-actions">
<button class="play-btn"><i class="fas fa-play"></i></button>
<button class="add-btn"><i class="fas fa-plus"></i></button>
</div>
</div>
<div class="song-item">
<div class="song-cover">
<img src="https://picsum.photos/seed/song4/80/80.jpg" alt="歌曲封面">
</div>
<div class="song-info">
<h3>小幸运</h3>
<p>歌手:田馥甄</p>
</div>
<div class="song-actions">
<button class="play-btn"><i class="fas fa-play"></i></button>
<button class="add-btn"><i class="fas fa-plus"></i></button>
</div>
</div>
</div>
</section>
<!-- 排行榜 -->
<section class="ranking-section">
<div class="section-header">
<h2>排行榜</h2>
<a href="#">更多 <i class="fas fa-chevron-right"></i></a>
</div>
<div class="ranking-list">
<div class="ranking-card">
<div class="ranking-cover">
<img src="https://picsum.photos/seed/rank1/150/150.jpg" alt="排行榜封面">
<span class="ranking-badge hot">热</span>
</div>
<div class="ranking-info">
<h3>飙升榜</h3>
<p>每日更新,反映歌曲受欢迎程度的实时变化</p>
</div>
</div>
<div class="ranking-card">
<div class="ranking-cover">
<img src="https://picsum.photos/seed/rank2/150/150.jpg" alt="排行榜封面">
<span class="ranking-badge new">新</span>
</div>
<div class="ranking-info">
<h3>新歌榜</h3>
<p>收录最新发布的歌曲,让你第一时间听到新旋律</p>
</div>
</div>
<div class="ranking-card">
<div class="ranking-cover">
<img src="https://picsum.photos/seed/rank3/150/150.jpg" alt="排行榜封面">
<span class="ranking-badge king">王</span>
</div>
<div class="ranking-info">
<h3>原创榜</h3>
<p>为原创音乐人打造的舞台,发现独立好声音</p>
</div>
</div>
</div>
</section>
</main>
<!-- 页脚 -->
<footer class="main-footer">
<p>© 2025 QQ音乐 Clone. 仅供学习参考。</p>
</footer>
<script src="script.js"></script>
</body>
</html>
第三步:CSS 样式 (style.css)
这是页面的样式表,负责美化页面,定义布局、颜色、字体和动画效果。

(图片来源网络,侵删)
/* --- 全局样式与变量 --- */
:root {
--primary-color: #31c27c; /* QQ音乐绿 */
--secondary-color: #333;
--background-color: #f5f5f5;
--card-background: #fff;
--text-color: #333;
--text-secondary: #999;
--border-color: #e0e0e0;
--hover-color: #f0f0f0;
--transition-speed: 0.3s;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.6;
}
a {
text-decoration: none;
color: inherit;
}
ul, ol {
list-style: none;
}
img {
max-width: 100%;
height: auto;
display: block;
}
/* --- 布局与容器 --- */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* --- 顶部导航栏 --- */
.main-header {
background-color: var(--card-background);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: sticky;
top: 0;
z-index: 1000;
}
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
height: 70px;
padding: 0 20px;
}
.logo {
display: flex;
align-items: center;
font-size: 24px;
font-weight: bold;
color: var(--primary-color);
}
.logo i {
margin-right: 8px;
font-size: 30px;
}
.main-nav a {
margin: 0 15px;
font-weight: 500;
color: var(--text-color);
position: relative;
}
.main-nav a.active::after {
content: '';
position: absolute;
bottom: -20px;
left: 0;
width: 100%;
height: 3px;
background-color: var(--primary-color);
}
.search-bar {
display: flex;
align-items: center;
background-color: var(--hover-color);
border-radius: 20px;
padding: 8px 15px;
width: 300px;
}
.search-bar input {
border: none;
background: none;
outline: none;
width: 100%;
padding-right: 10px;
}
.search-bar button {
border: none;
background: none;
color: var(--text-secondary);
cursor: pointer;
}
.user-actions .login-btn {
background-color: var(--primary-color);
color: white;
border: none;
padding: 8px 20px;
border-radius: 20px;
cursor: pointer;
font-weight: 500;
transition: background-color var(--transition-speed);
}
.user-actions .login-btn:hover {
background-color: #2aa96e;
}
/* --- 主内容区 --- */
.main-content {
padding: 30px 0;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.section-header h2 {
font-size: 24px;
font-weight: bold;
}
.tabs {
display: flex;
}
.tab-btn {
border: none;
background: none;
color: var(--text-secondary);
padding: 8px 15px;
cursor: pointer;
font-size: 16px;
border-bottom: 2px solid transparent;
transition: all var(--transition-speed);
}
.tab-btn.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
.more-link a {
color: var(--primary-color);
font-weight: 500;
}
.more-link a i {
font-size: 12px;
margin-left: 5px;
}
/* --- Banner 轮播图 --- */
.banner-section {
margin-bottom: 40px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.carousel-container {
position: relative;
height: 400px;
overflow: hidden;
}
.carousel-wrapper {
display: flex;
transition: transform 0.5s ease-in-out;
height: 100%;
}
.carousel-slide {
min-width: 100%;
position: relative;
}
.carousel-slide img {
width: 100%;
height: 100%;
object-fit: cover;
}
.slide-info {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 40px;
background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
color: white;
}
.slide-info h2 {
font-size: 32px;
margin-bottom: 10px;
}
.carousel-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgba(255, 255, 255, 0.8);
color: #333;
border: none;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
transition: background-color var(--transition-speed);
z-index: 10;
}
.carousel-btn:hover {
background-color: white;
}
.prev { left: 20px; }
.next { right: 20px; }
.carousel-indicators {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
}
.indicator {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.5);
cursor: pointer;
transition: background-color var(--transition-speed);
}
.indicator.active {
background-color: white;
width: 30px;
border-radius: 5px;
}
/* --- 推荐歌单 --- */
.playlist-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.playlist-card {
background-color: var(--card-background);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: transform var(--transition-speed), box-shadow var(--transition-speed);
cursor: pointer;
}
.playlist-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
.playlist-cover {
position: relative;
padding-top: 100%; /* 1:1 宽高比 */
}
.playlist-cover img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.play-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity var(--transition-speed);
}
.playlist-card:hover .play-overlay {
opacity: 1;
}
.play-overlay i {
font-size: 50px;
color: white;
}
.play-count {
position: absolute;
bottom: 5px;
right: 5px;
background-color: rgba(0, 0, 0, 0.6);
color: white;
padding: 2px 8px;
border-radius: 15px;
font-size: 12px;
}
.playlist-info {
padding: 15px;
}
.playlist-info h3 {
font-size: 16px;
margin-bottom: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.playlist-info p {
font-size: 13px;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* --- 新歌首发 --- */
.songs-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.song-item {
background-color: var(--card-background);
border-radius: 8px;
display: flex;
align-items: center;
padding: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: background-color var(--transition-speed);
}
.song-item:hover {
background-color: var(--hover-color);
}
.song-cover {
width: 80px;
height: 80px;
border-radius: 4px;
overflow: hidden;
margin-right: 15px;
}
.song-cover img {
width: 100%;
height: 100%;
object-fit: cover;
}
.song-info {
flex-grow: 1;
}
.song-info h3 {
font-size: 16px;
margin-bottom: 5px;
}
.song-info p {
font-size: 14px;
color: var(--text-secondary);
}
.song-actions {
display: flex;
gap: 15px;
}
.song-actions button {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
font-size: 18px;
transition: color var(--transition-speed);
}
.song-actions .play-btn:hover {
color: var(--primary-color);
}
.song-actions .add-btn:hover {
color: var(--primary-color);
}
/* --- 排行榜 --- */
.ranking-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
.ranking-card {
background-color: var(--card-background);
border-radius: 8px;
display: flex;
align-items: center;
padding: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
cursor: pointer;
transition: transform var(--transition-speed);
}
.ranking-card:hover {
transform: scale(1.02);
}
.ranking-cover {
position: relative;
width: 80px;
height: 80px;
border-radius: 4px;
overflow: hidden;
margin-right: 15px;
}
.ranking-cover img {
width: 100%;
height: 100%;
object-fit: cover;
}
.ranking-badge {
position: absolute;
top: -5px;
right: -5px;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: bold;
}
.ranking-badge.hot { background-color: #ff6b6b; }
.ranking-badge.new { background-color: #4ecdc4; }
.ranking-badge.king { background-color: #f7b731; }
.ranking-info h3 {
font-size: 18px;
margin-bottom: 5px;
}
.ranking-info p {
font-size: 14px;
color: var(--text-secondary);
}
/* --- 页脚 --- */
.main-footer {
text-align: center;
padding: 20px;
color: var(--text-secondary);
font-size: 14px;
border-top: 1px solid var(--border-color);
margin-top: 50px;
}
第四步:JavaScript 代码 (script.js)
这是页面的交互逻辑,负责实现轮播图自动播放、Tab 切换等动态效果。
document.addEventListener('DOMContentLoaded', () => {
// --- Banner 轮播图功能 ---
const carouselWrapper = document.querySelector('.carousel-wrapper');
const slides = document.querySelectorAll('.carousel-slide');
const prevBtn = document.querySelector('.carousel-btn.prev');
const nextBtn = document.querySelector('.carousel-btn.next');
const indicators = document.querySelectorAll('.indicator');
let currentIndex = 0;
const totalSlides = slides.length;
let autoPlayInterval;
// 更新轮播图位置
function updateCarousel() {
carouselWrapper.style.transform = `translateX(-${currentIndex * 100}%)`;
// 更新指示器
indicators.forEach((indicator, index) => {
indicator.classList.toggle('active', index === currentIndex);
});
}
// 下一张
function nextSlide() {
currentIndex = (currentIndex + 1) % totalSlides;
updateCarousel();
}
// 上一张
function prevSlide() {
currentIndex = (currentIndex - 1 + totalSlides) % totalSlides;
updateCarousel();
}
// 自动播放
function startAutoPlay() {
autoPlayInterval = setInterval(nextSlide, 5000); // 每5秒切换一次
}
function stopAutoPlay() {
clearInterval(autoPlayInterval);
}
// 事件监听
nextBtn.addEventListener('click', () => {
nextSlide();
stopAutoPlay();
startAutoPlay(); // 重置自动播放计时器
});
prevBtn.addEventListener('click', () => {
prevSlide();
stopAutoPlay();
startAutoPlay();
});
indicators.forEach((indicator, index) => {
indicator.addEventListener('click', () => {
currentIndex = index;
updateCarousel();
stopAutoPlay();
startAutoPlay();
});
});
// 鼠标悬停时暂停自动播放
const carouselContainer = document.querySelector('.carousel-container');
carouselContainer.addEventListener('mouseenter', stopAutoPlay);
carouselContainer.addEventListener('mouseleave', startAutoPlay);
// 启动自动播放
startAutoPlay();
// --- Tab 切换功能 ---
const tabBtns = document.querySelectorAll('.tab-btn');
// 注意:在实际应用中,不同tab的内容会不同,这里我们只做视觉切换演示
// 如果需要切换内容,可以在这里添加逻辑来隐藏/显示不同的歌单列表
tabBtns.forEach(btn => {
btn.addEventListener('click', () => {
// 移除所有active类
tabBtns.forEach(b => b.classList.remove('active'));
// 给当前点击的按钮添加active类
btn.classList.add('active');
// 这里可以添加切换内容的逻辑,
// const tabName = btn.dataset.tab;
// showTabContent(tabName);
});
});
// --- 模拟播放按钮点击 ---
const playButtons = document.querySelectorAll('.play-btn, .play-overlay i');
playButtons.forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation(); // 防止事件冒泡到父元素
alert('播放功能已触发!(这是一个模拟提示)');
});
});
// --- 模拟添加按钮点击 ---
const addButtons = document.querySelectorAll('.add-btn');
addButtons.forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
alert('已添加到歌单!(这是一个模拟提示)');
});
});
});
如何运行
- 将上述三段代码分别复制到
index.html、style.css和script.js文件中。 - 确保这三个文件在同一个文件夹内。
- 用浏览器打开
index.html文件,您就可以看到仿 QQ 音乐音乐馆的完整效果了。
总结与扩展
这个仿写版本涵盖了 QQ 音乐音乐馆的核心视觉元素和交互逻辑,您可以基于此进行扩展,
- 引入后端数据:使用
fetch或axios从 API 获取真实的歌单、歌曲和排行榜数据。 - 播放器组件:创建一个固定的底部播放器,实现真正的音乐播放功能(可以使用 Web Audio API 或集成第三方播放器库)。
- 更多页面:创建“我的音乐”、“歌手详情”等其他页面,并使用前端路由(如
History API)进行跳转。 - 更复杂的交互:实现拖拽排序、歌词同步显示等高级功能。

(图片来源网络,侵删)
