HTML5 敲击乐:典型前端项目入门

43 阅读7分钟

HTML5 敲击乐:前端项目入门实战指南

在本教程中,我们将使用现代前端开发的核心技术(HTML5 + CSS3 + JavaScript)构建一个响应式的 HTML5 敲击乐 Web 应用。整个过程将严格遵循前端工程化最佳实践,并融合关键知识点:CSS Reset、选择器规范、背景处理、相对单位(rem/vh)、弹性布局(Flexbox)


一、项目目标

  • 用户点击或按键盘按键时,播放对应音效。
  • 页面结构清晰、样式统一、交互流畅。
  • 适配不同屏幕尺寸(尤其是移动端)。
  • 代码模块化、可维护、可扩展。

  • 最终界面样式 image.png

二、HTML 结构设计

我们使用语义化标签构建页面骨架,每个“鼓键”用 <div class="key"> 表示,并通过 data-key 属性绑定键盘码,便于 JS 控制。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>HTML5 敲击乐</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div class="keys">
    <!-- dom 上添加一个数据属性 -->
    <div class="key" data-key="65">
      <h3>A</h3>
      <span class="sound">clap</span>
    </div>
    <div class="key"  data-key="83">
      <h3>S</h3>
      <span class="sound">hihat</span>
    </div>
    <div class="key">
      <h3>D</h3>
      <span class="sound">kick</span>
    </div>
    <div class="key">
      <h3>F</h3>
      <span class="sound">openhat</span>
    </div>
    <div class="key">
      <h3>G</h3>
      <span class="sound">boom</span>
    </div>
    <div class="key">
      <h3>H</h3>
      <span class="sound">ride</span>
    </div>
    <div class="key">
      <h3>J</h3>
      <span class="sound">snare</span>
    </div>
    <div class="key">
      <h3>K</h3>
      <span class="sound">tom</span>
    </div>
    <div class="key">
      <h3>L</h3>
      <span class="sound">tink</span>
    </div>
  </div>
  <script src="./script.js"></script>
</body>
</html>
  • 模块运行结构 `-
HTML5 敲击乐

A

clap

S

hihat

D

kick

F

openhat

G

boom

H

ride

J

snare

K

tom

L

tink
`

说明

  • 使用 类选择器 .key 而非标签选择器,提高可维护性。
  • 每个按键包含字母提示和音效名称,结构清晰。
  • 其中编码使用emet简化编码提高开发效率
  • css的导入要在head处,script导入在body前一行使得不会阻塞流程运行

三、CSS 样式:专业、响应、高性能

1. 使用业界推荐的 CSS Reset(避免 *

/*
  Eric Meyer's Reset CSS v3.0.0 (https://meyerweb.com/eric/tools/css/reset/)
  License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, i, u, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
  display: block;
}
body {
  line-height: 1;
}
ol, ul {
  list-style: none;
}
blockquote, q {
  quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
  content: '';
  content: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}

/* 建议补充:现代开发常用的全局设置 */
*, *::before, *::after {
  box-sizing: border-box;
}

img {
  max-width: 100%;
  height: auto;
  display: block; /* 避免图片下方出现间隙 */
}

a {
  text-decoration: none;
  color: inherit;
}

/* 业务样式 */
html, body {  
  height: 100%;
}
html {
  font-size: 10px;
  background: url('./background.jpg') bottom center;
  background-size: cover;
}
.keys {
  display:flex; /*弹性布局*/
  min-height: 100vh;/*现代的相对单位,不同手机,高度不一样,100vh 占满整个
  不同设备间的兼容
  */
  /* background: green; 背景颜色调试法*/
  align-items: center;
  justify-content: center;
}
.key {
  /* background: red; */
  border: .4rem solid black;
  border-radius: 0.5rem;
  margin: 1rem;
  font-size: 1.5rem;
  padding: 1rem 0.5rem;
  width: 10rem;
  text-align: center;
  color: white;
  background: rgba(0,0,0, 0.4);
  text-shadow: 0 0 .5rem black;
}
.key h3 {
  display: block;
  font-size: 4rem;
}
.key .sound {
  font-size: 1.2rem;
  text-transform: uppercase;
  letter-spacing: 0.1rem;
  color: #ffc600;
}

.playing {
  transform: scale(1.1);/*变基属性 放大1.5倍*/
  border-color: #ffc600;
  box-shadow: 0 0 1rem #ffc600;
}

为什么不用 *
* 会匹配所有元素,包括不需要重置的(如 <svg><canvas>),影响渲染性能。


2. 设置根字体大小,启用 rem 单位

html {
  font-size: 10px; /* 1rem = 10px */
}

body {
  background: #2c3e50;
  min-height: 100vh;
}

3. 使用 Flex 布局实现居中 & 自适应

html {
  font-size: 10px;
  background: url('./background.jpg') bottom center;
  background-size: cover;
}

优势

  • display: flex 解决多端布局兼容问题。
  • min-height: 100vh 确保容器占满视口高度。
  • 使用 remvh 替代 px,实现真正响应式。

4. 按键样式 + 背景图处理(可选)

如果你想为每个按键添加背景图(如鼓面纹理):

.key {
  width: 12rem;
  height: 12rem;
  background-image: url('drum-bg.jpg');
  background-size: cover;          /* 覆盖整个按键,可能裁剪 */
  background-position: bottom center;
  background-repeat: no-repeat;
  border-radius: 1rem;
  box-shadow: 0 0.5rem 1.5rem rgba(0,0,0,0.3);
  color: white;
  text-align: center;
  padding: 2rem 1rem;
  cursor: pointer;
  transition: transform 0.1s ease;
}

.key:active {
  transform: scale(0.95);
}

背景属性说明

  • cover:以容器为主,图片等比缩放覆盖全部区域(适合装饰性背景)。
  • contain:以图片为主,完整显示图片(适合图标类)。
  • no-repeat + bottom center:精准控制位置。

四、JavaScript 交互:延迟执行,不阻塞渲染

将脚本放在 </body> 前,确保静态内容优先加载。

// 页面的最底部,在静态页面出现之后再执行
// document 整个文档 添加了一个事件监听
// 首要渲染界面, html + css, 不需要js 参与
// DOMContentLoaded html 文档加载完后在执行
// DOM 文档结构
// script阻塞html 的下载
document.addEventListener('DOMContentLoaded', function () {
  // 页面加载完成后执行的代码
  // 可以获取页面元素、添加事件监听器等
  function playSound(event) {
    // 事件对象, 在事件发生的时候会给回调函数
    // keyCode 按下的键的编码
    console.log(event.keyCode, '/////////');
    let keyCode = event.keyCode;
    let element = document.querySelector('.key[data-key="'+ keyCode +'"]');
    // 
    console.log(element);
    // 动态DOM编程 增加类
    element.classList.add('playing');
  }
  // 事件监听
  window.addEventListener('keydown', playSound);
});

关键点

  • 使用 DOMContentLoaded 确保 DOM 就绪后再操作。
  • 支持 键盘 + 鼠标 双交互。
  • 添加 .playing 类实现点击动画反馈(可在 CSS 中定义)。

五、总结:核心知识点回顾

技术点应用方式目的
CSS Reset列出具体标签重置统一浏览器默认样式,提升性能
类选择器.key, .letter提高可维护性与复用性
背景处理background-size: cover + no-repeat美观且不失真的背景展示
相对单位rem(基于 html { font-size: 10px }),vh适配移动端多尺寸屏幕
Flex 布局display: flex + justify-content/align-items实现完美居中与自适应排列
JS 引入位置<script> 放在 </body>不阻塞 HTML 渲染,提升首屏速度

六、下一步建议

  • 添加真实的 <audio> 元素(隐藏)用于播放音效。
  • 使用 CSS 动画增强 .playing 状态反馈。
  • 打包工具(如 Vite)优化资源加载。
  • PWA 支持,实现离线使用。

通过这个小项目,你不仅掌握了敲击乐应用的开发,更深入理解了现代前端开发的核心理念:结构、样式、行为分离 + 响应式 + 性能优先。现在,去敲出属于你的节奏吧!🥁

五、源代码

  • HTML代码板块

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>HTML5 敲击乐</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div class="keys">
    <!-- dom 上添加一个数据属性 -->
    <div class="key" data-key="65">
      <h3>A</h3>
      <span class="sound">clap</span>
    </div>
    <div class="key"  data-key="83">
      <h3>S</h3>
      <span class="sound">hihat</span>
    </div>
    <div class="key">
      <h3>D</h3>
      <span class="sound">kick</span>
    </div>
    <div class="key">
      <h3>F</h3>
      <span class="sound">openhat</span>
    </div>
    <div class="key">
      <h3>G</h3>
      <span class="sound">boom</span>
    </div>
    <div class="key">
      <h3>H</h3>
      <span class="sound">ride</span>
    </div>
    <div class="key">
      <h3>J</h3>
      <span class="sound">snare</span>
    </div>
    <div class="key">
      <h3>K</h3>
      <span class="sound">tom</span>
    </div>
    <div class="key">
      <h3>L</h3>
      <span class="sound">tink</span>
    </div>
  </div>
  <script src="./script.js"></script>
</body>
</html>

  • CSS板块
/*
  Eric Meyer's Reset CSS v3.0.0 (https://meyerweb.com/eric/tools/css/reset/)
  License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, i, u, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
  display: block;
}
body {
  line-height: 1;
}
ol, ul {
  list-style: none;
}
blockquote, q {
  quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
  content: '';
  content: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}

/* 建议补充:现代开发常用的全局设置 */
*, *::before, *::after {
  box-sizing: border-box;
}

img {
  max-width: 100%;
  height: auto;
  display: block; /* 避免图片下方出现间隙 */
}

a {
  text-decoration: none;
  color: inherit;
}

/* 业务样式 */
html, body {  
  height: 100%;
}
html {
  font-size: 10px;
  background: url('./background.jpg') bottom center;
  background-size: cover;
}
.keys {
  display:flex; /*弹性布局*/
  min-height: 100vh;/*现代的相对单位,不同手机,高度不一样,100vh 占满整个
  不同设备间的兼容
  */
  /* background: green; 背景颜色调试法*/
  align-items: center;
  justify-content: center;
}
.key {
  /* background: red; */
  border: .4rem solid black;
  border-radius: 0.5rem;
  margin: 1rem;
  font-size: 1.5rem;
  padding: 1rem 0.5rem;
  width: 10rem;
  text-align: center;
  color: white;
  background: rgba(0,0,0, 0.4);
  text-shadow: 0 0 .5rem black;
}
.key h3 {
  display: block;
  font-size: 4rem;
}
.key .sound {
  font-size: 1.2rem;
  text-transform: uppercase;
  letter-spacing: 0.1rem;
  color: #ffc600;
}

.playing {
  transform: scale(1.1);/*变基属性 放大1.5倍*/
  border-color: #ffc600;
  box-shadow: 0 0 1rem #ffc600;
}
  • JavaScript 板块
// 页面的最底部,在静态页面出现之后再执行
// document 整个文档 添加了一个事件监听
// 首要渲染界面, html + css, 不需要js 参与
// DOMContentLoaded html 文档加载完后在执行
// DOM 文档结构
// script阻塞html 的下载
document.addEventListener('DOMContentLoaded', function () {
  // 页面加载完成后执行的代码
  // 可以获取页面元素、添加事件监听器等
  function playSound(event) {
    // 事件对象, 在事件发生的时候会给回调函数
    // keyCode 按下的键的编码
    console.log(event.keyCode, '/////////');
    let keyCode = event.keyCode;
    let element = document.querySelector('.key[data-key="'+ keyCode +'"]');
    // 
    console.log(element);
    // 动态DOM编程 增加类
    element.classList.add('playing');
  }
  // 事件监听
  window.addEventListener('keydown', playSound);
});