使用原生 JavaScript 构建一个网页版电子鼓

大家好!今天我想和大家分享一个非常有趣的 Web 项目 – 使用原生 JavaScript 构建一个网页版电子鼓。这个项目不仅能让我们学习 DOM 编程的核心概念,还能做出一个好玩的互动性项目。让我们开始吧!

项目概览

这个项目的最终效果是一个网页版的电子鼓界面,包含7个不同的鼓点按钮。用户可以通过以下两种方式来演奏:

  • 点击屏幕上的按钮
  • 使用键盘上对应的按键(w、a、s、d、j、k、l)

每个按钮/按键都会:

  • 播放对应的鼓点声音
  • 显示按下的视觉效果
  • 自动恢复到原始状态

如下视频所示⬇️

项目结构

DRUM KIT/
│
├── images/
│   ├── crash.png
│   ├── kick.png
│   ├── snare.png
│   ├── tom1.png
│   ├── tom2.png
│   ├── tom3.png
│   └── tom4.png
│
├── sounds/
│   ├── crash.mp3
│   ├── kick-bass.mp3 
│   ├── snare.mp3
│   ├── tom-1.mp3
│   ├── tom-2.mp3
│   ├── tom-3.mp3
│   └── tom-4.mp3
│
├── index.html
├── index.js
└── styles.css
  1. images/ 目录: 存放所有鼓的图片素材
    • 7个不同鼓的图片(.png格式)
  2. sounds/ 目录: 存放所有鼓的音效文件
    • 7个不同鼓的音效(.mp3格式)
  3. 根目录下的主要文件:
    • index.html: 网页主要结构
    • index.js: JavaScript 交互逻辑
    • styles.css: 样式表

为方便快速开始,我已经将项目大致的结构和样式写好,以便我们将核心内容放到javascript代码上。

项目练习的开始文件下载在下面👇

初始效果⬇️

完整项目文件在博客最后

初始HTML 结构

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title>Drum Kit</title>
  <link rel="stylesheet" href="styles.css">
  <link href="https://fonts.googleapis.com/css?family=Arvo" rel="stylesheet">
</head>

<body>

  <h1 id="title">Drum 🥁 Kit</h1>
  <div class="set">
    <button class="w drum">w</button>
    <button class="a drum">a</button>
    <button class="s drum">s</button>
    <button class="d drum">d</button>
    <button class="j drum">j</button>
    <button class="k drum">k</button>
    <button class="l drum">l</button>
  </div>


  <footer>
    Made with ❤️ in London.
  </footer>
<script src="index.js"></script>
</body>

</html>

这段 HTML 创建了7个按钮,每个按钮都有:

  • 一个通用的 drum 类
  • 一个独特的字母类(w、a、s 等)
  • 按钮内显示对应的字母

初始CSS结构

body {
  text-align: center;
  background-color: #283149;
}

h1 {
  font-size: 5rem;
  color: #DBEDF3;
  font-family: "Arvo", cursive;
  text-shadow: 3px 0 #DA0463;

}

footer {
  color: #DBEDF3;
  font-family: sans-serif;
}

.w {

}

.a {

}

.s {

}

.d {

}

.j {

}

.k {

}

.l {

}

.set {
  margin: 10% auto;
}

.game-over {
  background-color: red;
  opacity: 0.8;
}

.pressed {
  box-shadow: 0 3px 4px 0 #DBEDF3;
  opacity: 0.5;
}

.red {
  color: red;
}

.drum {
  outline: none;
  border: 10px solid #404B69;
  font-size: 5rem;
  font-family: 'Arvo', cursive;
  line-height: 2;
  font-weight: 900;
  color: #DA0463;
  text-shadow: 3px 0 #DBEDF3;
  border-radius: 15px;
  display: inline-block;
  width: 150px;
  height: 150px;
  text-align: center;
  margin: 10px;
  background-color: white;
}

这里定义了:

按钮被按下时的视觉效果(.pressed)

按钮的基本样式(.drum)

每个按钮还通过独特的类名设置了不同的背景图片(我们稍后添加):

JavaScript 实现

现在来看看最核心的部分 – 如何实现交互逻辑。我们将分步骤深入讲解。来到index.js文件里开始编写我们的js代码吧!

1. 检测按钮点击

现在我们点击页面上的任何按钮都没有反应,所以我们需要为所有鼓点按钮添加点击事件监听:

var numberOfButtons = document.querySelectorAll(".drum").length;

for (var i=0; i<numberOfButtons; i++){
    document.querySelectorAll(".drum")[i].addEventListener("click", function(){
        alert("I got clicked!");
});
};

这段代码的功能是:当用户点击页面上所有带有 class=”drum” 的元素时,弹出一个提示框(alert)。⬇️

代码分段详解:

var numberOfButtons = document.querySelectorAll(".drum").length;

• document.querySelectorAll(“.drum”):选择页面中所有的 class 为 “drum” 的元素。querySelectorAll 返回的是一个NodeList,也就是一个类似数组的对象,包含所有匹配 .drum 类的元素。

• .length:获取这个 NodeList 中元素的数量,赋值给变量 numberOfButtons。

作用:这行代码用于计算页面中有多少个元素带有 class=”drum”,并将这个数量存储在 numberButtons 变量中。

for (var i=0; i<numberOfButtons; i++){
    document.querySelectorAll(".drum")[i].addEventListener("click", function(){
        alert("I got clicked!");
});
};

• document.querySelectorAll(“.drum”)[i]:根据上面定义的 i,获取每一个 .drum 元素。i 会依次取值,代表 .drum 元素的下标位置。

• addEventListener(“click”, function() { … }):为每个 .drum 元素绑定一个点击事件监听器。也就是说,当用户点击该 .drum 元素时,function 这个回调函数会被触发。

• 在这个回调函数内部,我们用 alert(“I got clicked!”) 来弹出一个提示框,告诉用户“我被点击了!”

作用:每个 .drum 元素都会绑定一个点击事件,当点击时,弹出提示框。

2. 播放声音

这一步让我完成鼠标点击鼓点按钮不是弹出提示框而是会播放声音这个效果。

var numberOfButtons = document.querySelectorAll(".drum").length;

for (var i=0; i<numberOfButtons; i++){
    document.querySelectorAll(".drum")[i].addEventListener("click", function(){
        var audio = new Audio("sounds/tom-1.mp3");
        audio.play();
});
};

这里我们移除alert(“I got clicked!”),添加如下代码

var audio = new Audio("sounds/tom-1.mp3");
        audio.play();

var audio = new Audio(“sounds/tom-1.mp3”);:创建一个新的 Audio 对象,并将音频文件 “sounds/tom-1.mp3” 作为音源。

• audio.play();:调用 play() 方法播放音频。

这样的话就可以完成点击时播放音频的效果了。

接下来我们去css文件里把图片背景挨个加上,把下面位置的代码补全⬇️

.w {
  background-image: url("images/tom1.png");
}

.a {
  background-image: url("images/tom2.png");
}

.s {
  background-image: url("images/tom3.png");
}

.d {
  background-image: url("images/tom4.png");
}

.j {
  background-image: url("images/snare.png");
}

.k {
  background-image: url("images/crash.png");
}

.l {
  background-image: url("images/kick.png");
}

这样按钮就有背景图了,效果如下⬇️

3. 点击不同按钮播放对应声音

之前写的代码问题是我们点击每一个鼓点按钮都播放同一个声音(tom-1.mp3),现在的需求是点击每个鼓点按钮能响应对应不同的声音。

我们修改代码如下⬇️

var numberOfButtons = document.querySelectorAll(".drum").length;

for (var i=0; i<numberOfButtons; i++){
    document.querySelectorAll(".drum")[i].addEventListener("click", function(){

        var buttonInnerHTML = this.innerHTML;
        switch (buttonInnerHTML) {
            case "w":
                var tom1 = new Audio("sounds/tom-1.mp3");
                tom1.play();
                break;

            case "a":
                var tom2 = new Audio("sounds/tom-2.mp3");
                tom2.play();
                break;
            
            case "s":
                var tom3 = new Audio("sounds/tom-3.mp3");
                tom3.play();
                break;

            case "d":
                var tom4 = new Audio("sounds/tom-4.mp3");
                tom4.play();
                break;

            case "j":
                var snare = new Audio("sounds/snare.mp3");
                snare.play();
                break;
            
            case "k":
                var crash = new Audio("sounds/crash.mp3");
                crash.play();
                break;

            case "l":
                var kick = new Audio("sounds/kick-bass.mp3");
                kick.play();
                break;

            default: console.log(buttonInnerHTML)
        }
});
};

这里的关键逻辑是使用 switch语句根据按钮的文本(innerHTML)来判断播放哪个音频文件。

代码详解⬇️

• this.innerHTML:this 在这里指的是当前被点击的 .drum 元素。innerHTML 获取该元素的文本内容。

• switch (buttonInnerHTML):根据 buttonInnerHTML 的值,执行不同的 case,每个 case 对应一种按键(如 w、a、s 等),播放不同的音频文件。

比如:如果 buttonInnerHTML 是 w,那么会播放 “sounds/tom-1.mp3” 音频。

每个 case 中,都通过 new Audio() 创建一个音频对象,并使用 play() 方法播放该音频。

4. 监听键盘按键事件

现在让我们修改代码,使其能够按下键盘上的特定键时,播放相应的音效。

var numberOfButtons = document.querySelectorAll(".drum").length;

for (var i=0; i<numberOfButtons; i++){
    document.querySelectorAll(".drum")[i].addEventListener("click", function(){

        var buttonInnerHTML = this.innerHTML;

        makeSound(buttonInnerHTML)
        
});
};


document.addEventListener("keypress", function(event){

    makeSound(event.key);
    
});

function makeSound(key){
    switch (key) {
        case "w":
            var tom1 = new Audio("sounds/tom-1.mp3");
            tom1.play();
            break;

        case "a":
            var tom2 = new Audio("sounds/tom-2.mp3");
            tom2.play();
            break;
        
        case "s":
            var tom3 = new Audio("sounds/tom-3.mp3");
            tom3.play();
            break;

        case "d":
            var tom4 = new Audio("sounds/tom-4.mp3");
            tom4.play();
            break;

        case "j":
            var snare = new Audio("sounds/snare.mp3");
            snare.play();
            break;
        
        case "k":
            var crash = new Audio("sounds/crash.mp3");
            crash.play();
            break;

        case "l":
            var kick = new Audio("sounds/kick-bass.mp3");
            kick.play();
            break;

        default: console.log(buttonInnerHTML)
    };
};


首先我们将之前的switch逻辑封装到makeSound 函数里面方便调用,然后添加监听键盘按键事件⬇️

document.addEventListener("keypress", function(event) {
    makeSound(event.key);
});

• document.addEventListener(“keypress”, function(event) {…}):监听全局的 keypress 事件,当用户按下任意键时,执行回调函数。

• event.key:event 对象表示触发事件的信息。key 是按下的键的键值(如 w、a、s 等)。

• makeSound(event.key):调用 makeSound 函数,传递按下的键值,播放相应的音频。

作用:监听键盘按键,当用户按下键盘上的某个键时,播放与该键相对应的音效。

5. 给按钮添加动画效果

当某个按钮被按下时,按钮会短暂地显示一个按下的状态,然后恢复原状。如下图所示⬇️

要完成此效果,我们只需定义一个函数 buttonAnimation⬇️

function buttonAnimation(currentKey){
    var activeButton = document.querySelector("." + currentKey);

    activeButton.classList.add("pressed");
    
    setTimeout(function(){
        activeButton.classList.remove("pressed");
    }, 100);
}

代码逐行分析:

• document.querySelector(“.” + currentKey):通过 currentKey 获取对应类名的元素。currentKey 是一个字符串,所以我们使用 “.” + currentKey 来动态创建类选择器。比如,如果 currentKey 是 “w”,这行代码将会选择类名为 .w 的按钮。

• classList.add(“pressed”):将 pressed 类添加到 activeButton 元素的类列表中。pressed 类是我们之前在 CSS 中定义的,用来设置按钮在按下时的样式⬇️

.pressed {
  box-shadow: 0 3px 4px 0 #DBEDF3;
  opacity: 0.5;
}

• setTimeout(function(){…}, 100):设置一个定时器,延迟 100 毫秒后执行一个回调函数。

• 这里的 100 毫秒指的是按钮按下后,持续的时间。也就是说,按钮会在按下后短暂的 100 毫秒内保持“按下”状态,然后恢复正常。

• activeButton.classList.remove(“pressed”):在 100 毫秒后,移除 pressed 类,按钮恢复到原来的样式。

项目全部完整js代码如下⬇️

// 获取页面上所有类名为 "drum" 的按钮个数
var numberOfButtons = document.querySelectorAll(".drum").length;

// 遍历每个按钮,并为每个按钮添加点击事件监听器
for (var i = 0; i < numberOfButtons; i++) {
    // 为每个按钮绑定点击事件
    document.querySelectorAll(".drum")[i].addEventListener("click", function(){
        // 获取被点击按钮的内容(例如 "w"、"a" 等)
        var buttonInnerHTML = this.innerHTML;

        // 调用 makeSound 函数播放对应的音效
        makeSound(buttonInnerHTML);

        // 调用 buttonAnimation 函数,给按钮添加按下动画效果
        buttonAnimation(buttonInnerHTML);
    });
}

// 监听键盘按键事件
document.addEventListener("keypress", function(event){
    // 调用 makeSound 函数播放对应的音效
    makeSound(event.key);

    // 调用 buttonAnimation 函数,给按钮添加按下动画效果
    buttonAnimation(event.key);
});

// 根据按键播放相应的音效
function makeSound(key) {
    // 判断按下的是哪个键,播放对应的音效
    switch (key) {
        case "w":
            var tom1 = new Audio("sounds/tom-1.mp3");
            tom1.play(); // 播放音效
            break;

        case "a":
            var tom2 = new Audio("sounds/tom-2.mp3");
            tom2.play(); // 播放音效
            break;

        case "s":
            var tom3 = new Audio("sounds/tom-3.mp3");
            tom3.play(); // 播放音效
            break;

        case "d":
            var tom4 = new Audio("sounds/tom-4.mp3");
            tom4.play(); // 播放音效
            break;

        case "j":
            var snare = new Audio("sounds/snare.mp3");
            snare.play(); // 播放音效
            break;

        case "k":
            var crash = new Audio("sounds/crash.mp3");
            crash.play(); // 播放音效
            break;

        case "l":
            var kick = new Audio("sounds/kick-bass.mp3");
            kick.play(); // 播放音效
            break;

        // 如果按下的不是定义的键,打印键名
        default: console.log(buttonInnerHTML);
    };
}

// 按钮按下时添加动画效果
function buttonAnimation(currentKey) {
    // 获取与按键对应的按钮元素
    var activeButton = document.querySelector("." + currentKey);

    // 给按钮添加 "pressed" 类,用于显示按下的效果
    activeButton.classList.add("pressed");

    // 100毫秒后移除 "pressed" 类,恢复原样
    setTimeout(function(){
        activeButton.classList.remove("pressed");
    }, 100);
}

完整项目代码下载⬇️

学习要点总结

通过这个项目,我们学习了:

  1. DOM 操作基础
  • 选择元素
  • 修改样式
  • 添加/移除类
  • 处理事件
  1. JavaScript 核心概念
  • 事件监听器
  • 回调函数
  • this 关键字
  • switch 语句
  • 定时器函数
  1. 音频处理
  • 创建 Audio 对象
  • 播放声音
  • 控制播放时机
  1. 动画效果
  • CSS 类操作
  • setTimeout 的使用
  • 视觉反馈的实现

结语

这个网页版电子鼓项目虽然看起来简单,但它涵盖了 Web 开发中的许多重要概念。通过实现这个项目,我们不仅创建了一个有趣的交互式应用,还深入学习了 JavaScript DOM 编程的核心知识。

项目的完整代码并不长,但每一行都经过精心设计,展示了如何将 HTML、CSS 和 JavaScript 结合起来创建流畅的用户体验。大家可以在理解代码的基础上,尝试添加自己的创意,扩展更多有趣的功能。

祝编程愉快😗!


Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注