项目简介

大家好,我是唐朝,来自汤阴县豫唐网络科技有限公司。今天给大家分享我们团队的一个作品——豫唐·星空钢琴。

在线体验:https://www.ytecn.com/lab/piano

源码地址:https://gitee.com/ytecnsong/piano


目录

  1. 项目背景

  2. 技术选型

  3. 核心代码实现

  4. 项目结构

  5. 注意事项与踩坑

  6. 总结与展望


项目背景

钢琴学习门槛高、费用高,对初学者很不友好。我们的目标是:

  • 零门槛:浏览器即用,无需安装

  • 趣味化:星空主题,边看边学

  • 可扩展:支持自定义曲目


技术选型

技术栈

用途

Vue 3 (Composition API)

界面与状态

Vite

快速构建

Tone.js

Web Audio 封装

localStorage

数据存储

为什么选 Tone.js?

原生 Web Audio API 太复杂,Tone.js 封装了 polyphony(多音)、scheduling(调度)、effects(效果)等功能,代码量减少约 70%。


核心代码实现

1. 音频引擎封装

import * as Tone from 'tone'

export const audioEngine = {
  synth: null,

  async init() {
    await Tone.start() // 必须在用户交互后调用!
    this.synth = new Tone.PolySynth(Tone.Synth).toDestination()
    this.synth.volume.value = -6
  },

  playNote(note, duration = '8n') {
    if (this.synth) {
      this.synth.triggerAttackRelease(note, duration)
    }
  }
}

2. 键盘映射与事件监听

const KEY_MAP = {
  'z': 'C3', 'x': 'D3', 'c': 'E3', 'v': 'F3', 'b': 'G3', 'n': 'A3', 'm': 'B3',
  'a': 'C4', 's': 'D4', 'd': 'E4', 'f': 'F4', 'g': 'G4', 'h': 'A4', 'j': 'B4',
  'q': 'C5', 'w': 'D5', 'e': 'E5', 'r': 'F5', 't': 'G5', 'y': 'A5', 'u': 'B5'
}

window.addEventListener('keydown', (e) => {
  if (activeKeys.has(e.key)) return
  activeKeys.add(e.key)
  const note = KEY_MAP[e.key.toLowerCase()]
  if (note) audioEngine.playNote(note)
})

3. 曲目播放与高亮

function playSong(song, onNote) {
  let index = 0
  const interval = setInterval(() => {
    if (index >= song.notes.length) {
      clearInterval(interval)
      return
    }
    const { note } = song.notes[index]
    audioEngine.playNote(note)
    onNote(index) // 高亮当前键
    index++
  }, 500)
}

4. 导入导出功能

// 导出为 JSON 文件
function exportSong(song) {
  const blob = new Blob([JSON.stringify(song, null, 2)], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.download = `${song.name}.json`
  a.href = url
  a.click()
  URL.revokeObjectURL(url)
}

// 导入文件
function importSong(file) {
  const reader = new FileReader()
  reader.onload = (e) => {
    const data = JSON.parse(e.target.result)
    songManager.addSong(data)
  }
  reader.readAsText(file)
}

项目结构

yutang-piano/
├── index.html
├── package.json
├── vite.config.js
└── src/
    ├── main.js
    ├── App.vue
    ├── audio-engine.js
    ├── song-manager.js
    └── components/
        ├── SplashScreen.vue
        ├── PianoKeyboard.vue
        ├── SongPanel.vue
        ├── SongEditor.vue
        └── VolumeControl.vue

注意事项与踩坑

  1. 浏览器自动播放策略:Tone.start() 必须用户触发(如点击)后调用

  2. localStorage 大小限制:约 5MB,别存太多大曲目

  3. 移动端键盘问题:手机平板建议只做触摸弹奏,不支持键盘输入

  4. Tone.js 初始化时机:先点页面,再初始化音频引擎


总结与展望

我们实现了一个轻量级的在线钢琴学习平台。未来方向:

  • MIDI 输入支持

  • 多音色切换

  • 录音回放

  • 云存储同步


相关资源

更多推荐