HTML5技术实现的小钢琴

HTML5技术实现的小钢琴

用HTML5实现的小钢琴,按下钢琴键上的相应字母用或用鼠标点击钢琴键发声,源码如下:

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>
  <style>
    .key {
      width: 50px;
      height: 150px;
      border: 1px solid black;
      display: inline-block;
      margin: 5px;
      text-align: center;
      line-height: 150px;
      cursor: pointer;
      position: relative; /* 为了绝对定位伪元素而需要 */
      transition: transform 0.1s; /* 按键按下时平滑过渡效果 */
    }
    /* 当按键被按下时的样式 */
    .key.active {
      transform: translateY(4px); /* 将按键向下移动 */
    }
  </style>
</head>
<body>
  <div id="A" class="key">A</div>
  <div id="S" class="key">S</div>
  <div id="D" class="key">D</div>
  <div id="F" class="key">F</div>
  <div id="G" class="key">G</div>
  <div id="H" class="key">H</div>
  <div id="J" class="key">J</div>
  <div id="K" class="key">K</div>


  <script>
    // 创建一个新的AudioContext实例,用于处理音频
    const audioContext = new (window.AudioContext || window.webkitAudioContext)();

    // 播放特定频率的声音
    function playSound(frequency) {
      const oscillator = audioContext.createOscillator();
      oscillator.type = 'sine'; // 选择波形类型,这里是正弦波
      oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime); // 设置频率
      oscillator.connect(audioContext.destination); // 连接到音频输出
      oscillator.start(); // 开始播放
      oscillator.stop(audioContext.currentTime + 0.3); // 0.3秒后停止播放
    }

    // 获取所有的琴键元素
    const keys = document.querySelectorAll('.key');
    keys.forEach(key => {
      // 为每个琴键添加鼠标点击事件
      key.addEventListener('click', () => {
        playNote(key.textContent); // 播放琴键对应的音符
        animatePress(key); // 触发按键动画效果
      });
    });

    // 监听键盘按下事件
    document.addEventListener('keydown', (event) => {
      const keyName = event.key.toUpperCase(); // 获取按键名并转为大写
      const keyElement = document.getElementById(keyName); // 获取对应的琴键元素
      if (keyElement) {
        playNote(keyName); // 播放琴键对应的音符
        animatePress(keyElement); // 触发按键动画效果
      }
    });

    // 触发琴键按下的动画效果
    function animatePress(keyElement) {
      keyElement.classList.add('active'); // 添加按下状态的样式类
      setTimeout(() => {
        keyElement.classList.remove('active'); // 在100毫秒后移除按下状态的样式类
      }, 100); // 这个时间应该与CSS中的过渡时间一致
    }

    // 根据音符播放声音
    function playNote(note) {
      let frequency;
      switch (note) {
        case 'A':
          frequency = 261.63;
          break;
        case 'S':
          frequency = 293.66;
          break;
        case 'D':
          frequency = 329.63;
          break;
        case 'F':
          frequency = 349.23;
          break;
        case 'G':
          frequency = 392.00;
          break;
        case 'H':
          frequency = 440.00;
          break;
        case 'J':
          frequency = 493.88;
          break;
        case 'K':
          frequency = 523.25;
          break;
        default:
          return; // 如果不是琴键对应的字母,则不执行任何操作
      }
      playSound(frequency); // 调用函数播放声音
    }
  </script>
</body>
</html>

代码中使用了JavaScript中的箭头函数(Arrow Function)语法。箭头函数是ES6中的一种函数声明方式,它的语法比较简洁。箭头函数的基本格式是:(参数) => {函数体}。

keys.forEach(key => { })

箭头函数的参数是key,表示forEach遍历的每个元素。

key.addEventListener('click', () => { })

它是给每个琴键添加鼠标点击事件的代码。addEventListener 方法用于给元素添加事件监听器。这里添加了一个点击事件的监听器,当用户点击琴键时,箭头函数会被执行。

我对乐理了解很少,相关音高(pitch)请参考这里https://www.autopiano.cn/toolbox/pitch

用浏览器运行效果如下:

你可以用英文状态下按下琴键上的字符,或用鼠标单击琴键试试了。

钢琴演奏中,滑音是通过在音符之间平滑地滑动手指,使音符之间产生连贯的过渡效果。

上面的代码滑动按键能产生了滑音效果,但滑动鼠标不能,现在修改上面程序,使用鼠标都能产生了滑音效果。

修改后源码如下:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>用HTML5实现的小钢琴2</title>
  <style>
    .key {
      width: 50px;
      height: 150px;
      border: 1px solid black;
      display: inline-block;
      margin: 5px;
      text-align: center;
      line-height: 150px;
      cursor: pointer;
      position: relative;
      transition: transform 0.1s;
    }
    .key.active {
      transform: translateY(4px);
    }
  </style>
</head>
<body>
  <div id="A" class="key">A</div>
  <div id="S" class="key">S</div>
  <div id="D" class="key">D</div>
  <div id="F" class="key">F</div>
  <div id="G" class="key">G</div>
  <div id="H" class="key">H</div>
  <div id="J" class="key">J</div>
  <div id="K" class="key">K</div>

  <script>
    const audioContext = new (window.AudioContext || window.webkitAudioContext)();
    let isMouseDown = false; // 记录鼠标是否被按下

    function playSound(frequency) {
      const oscillator = audioContext.createOscillator();
      oscillator.type = 'sine';
      oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
      oscillator.connect(audioContext.destination);
      oscillator.start();
      oscillator.stop(audioContext.currentTime + 0.3);
    }

    const keys = document.querySelectorAll('.key');

    keys.forEach(key => {
      key.addEventListener('mousedown', (event) => {
        isMouseDown = true; // 设置鼠标按下状态为true
        playNoteFromElement(event.target);
      });

      key.addEventListener('mouseenter', (event) => {
        if (isMouseDown) { // 如果鼠标处于按下状态,则播放音符
          playNoteFromElement(event.target);
        }
      });

      // 鼠标按下并移动到其他琴键时,无需额外mouseup事件监听
    });

    // 监听文档的mouseup事件,以处理鼠标在琴键外松开的情况
    document.addEventListener('mouseup', () => {
      isMouseDown = false;
    });

    // 监听键盘按下事件
    document.addEventListener('keydown', (event) => {
      const keyName = event.key.toUpperCase(); // 获取按键名并转为大写
      const keyElement = document.getElementById(keyName); // 获取对应的琴键元素
      if (keyElement && !keyElement.classList.contains('active')) {
        playNoteFromElement(keyElement);
      }
    });

    function playNoteFromElement(element) {
      const note = element.textContent;
      playNote(note);
      animatePress(element);
    }

    function animatePress(keyElement) {
      keyElement.classList.add('active');
      setTimeout(() => {
        keyElement.classList.remove('active');
      }, 100);
    }

    function playNote(note) {
      let frequency;
      switch (note) {
        case 'A':
          frequency = 261.63;
          break;
        case 'S':
          frequency = 293.66;
          break;
        case 'D':
          frequency = 329.63;
          break;
        case 'F':
          frequency = 349.23;
          break;
        case 'G':
          frequency = 392.00;
          break;
        case 'H':
          frequency = 440.00;
          break;
        case 'J':
          frequency = 493.88;
          break;
        case 'K':
          frequency = 523.25;
          break;
        default:
          return;
      }
      playSound(frequency);
    }
  </script>
</body>
</html>

请注意,这段代码包含了对鼠标按下、移动和松开,以及键盘按下的事件监听。

你可以在此基础上继续改进优化。

相关推荐
卸任5 小时前
Vibe coding工具使用心得
前端·ai编程
brien95 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance6 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M6 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写6 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李6 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易6 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马6 小时前
前沿视界
前端
kyriewen6 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易7 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员