<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>键盘控制6键弹钢琴</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
width: 100%;
max-width: 800px;
text-align: center;
}
h1 {
color: #333;
margin-bottom: 30px;
font-size: 2.5rem;
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
}
.piano {
display: flex;
justify-content: center;
margin-bottom: 40px;
perspective: 1000px;
}
.key {
width: 100px;
height: 300px;
margin: 0 5px;
border-radius: 0 0 10px 10px;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
padding-bottom: 20px;
font-weight: bold;
font-size: 20px;
color: white;
cursor: pointer;
user-select: none;
transition: all 0.1s ease;
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
position: relative;
overflow: hidden;
}
.key::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(rgba(255,255,255,0.3), transparent);
opacity: 0;
transition: opacity 0.2s;
}
.key.active {
transform: translateY(10px) scale(0.98);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2);
}
.key.active::before {
opacity: 1;
}
.key-label {
background-color: rgba(0, 0, 0, 0.4);
padding: 8px 15px;
border-radius: 15px;
margin-top: 15px;
font-size: 1.2rem;
transition: transform 0.2s;
}
.key.active .key-label {
transform: scale(1.1);
}
.instructions {
background-color: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
backdrop-filter: blur(5px);
}
.instructions p {
margin-bottom: 15px;
color: #555;
font-size: 1.1rem;
line-height: 1.6;
}
.keyboard-keys {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
margin-top: 15px;
}
.keyboard-key {
background: linear-gradient(145deg, #6e48aa, #9d50bb);
color: white;
padding: 10px 15px;
border-radius: 8px;
font-family: monospace;
font-size: 1.2rem;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
min-width: 40px;
}
.article-link {
display: inline-block;
margin-top: 40px;
padding: 12px 25px;
background: linear-gradient(145deg, #4776E6, #8E54E9);
color: white;
text-decoration: none;
border-radius: 30px;
font-weight: bold;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
transition: all 0.3s ease;
}
.article-link:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0,0,0,0.25);
}
@media (max-width: 768px) {
.key {
width: 60px;
height: 200px;
margin: 0 3px;
}
h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<div class="container">
<h1>键盘控制6键钢琴</h1>
<div class="instructions">
<p>使用键盘上的 A、S、D、F、G、H 键来弹奏钢琴</p>
<div class="keyboard-keys">
<span class="keyboard-key">A</span>
<span class="keyboard-key">S</span>
<span class="keyboard-key">D</span>
<span class="keyboard-key">F</span>
<span class="keyboard-key">G</span>
<span class="keyboard-key">H</span>
</div>
</div>
<div class="piano">
<div class="key" id="key1" style="background: linear-gradient(145deg, #FF416C, #FF4B2B);" data-note="C4">
<span class="key-label">A</span>
</div>
<div class="key" id="key2" style="background: linear-gradient(145deg, #FF9068, #FF7B54);" data-note="D4">
<span class="key-label">S</span>
</div>
<div class="key" id="key3" style="background: linear-gradient(145deg, #FFD166, #FFB347);" data-note="E4">
<span class="key-label">D</span>
</div>
<div class="key" id="key4" style="background: linear-gradient(145deg, #06D6A0, #1B9AAA);" data-note="F4">
<span class="key-label">F</span>
</div>
<div class="key" id="key5" style="background: linear-gradient(145deg, #118AB2, #073B4C);" data-note="G4">
<span class="key-label">G</span>
</div>
<div class="key" id="key6" style="background: linear-gradient(145deg, #7209B7, #560BAD);" data-note="A4">
<span class="key-label">H</span>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 创建音频上下文
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContext();
// 音符频率映射 (使用更丰富的音色)
const noteFrequencies = {
'C4': 261.63,
'D4': 293.66,
'E4': 329.63,
'F4': 349.23,
'G4': 392.00,
'A4': 440.00
};
// 键盘按键映射
const keyMap = {
'a': 'key1',
's': 'key2',
'd': 'key3',
'f': 'key4',
'g': 'key5',
'h': 'key6'
};
// 当前活动的振荡器
const activeOscillators = {};
// 创建更丰富的音色
function createOscillator(freq) {
const now = audioContext.currentTime;
// 主振荡器
const oscillator = audioContext.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.value = freq;
// 添加一些谐波
const oscillator2 = audioContext.createOscillator();
oscillator2.type = 'square';
oscillator2.frequency.value = freq * 2;
const oscillator3 = audioContext.createOscillator();
oscillator3.type = 'triangle';
oscillator3.frequency.value = freq * 3;
// 增益节点用于淡入淡出
const gainNode = audioContext.createGain();
gainNode.gain.value = 0;
// 滤波器
const filter = audioContext.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.value = 2000;
// 连接节点
oscillator.connect(gainNode);
oscillator2.connect(gainNode);
oscillator3.connect(gainNode);
gainNode.connect(filter);
filter.connect(audioContext.destination);
// 淡入效果
gainNode.gain.setValueAtTime(0, now);
gainNode.gain.linearRampToValueAtTime(0.2, now + 0.02);
// 启动振荡器
oscillator.start();
oscillator2.start();
oscillator3.start();
return {
oscillator: oscillator,
oscillator2: oscillator2,
oscillator3: oscillator3,
gainNode: gainNode,
filter: filter
};
}
// 播放音符函数
function playNote(note) {
if (activeOscillators[note]) return;
const freq = noteFrequencies[note];
activeOscillators[note] = createOscillator(freq);
}
// 停止音符函数
function stopNote(note) {
if (!activeOscillators[note]) return;
const now = audioContext.currentTime;
const { oscillator, oscillator2, oscillator3, gainNode } = activeOscillators[note];
// 淡出效果
gainNode.gain.setValueAtTime(gainNode.gain.value, now);
gainNode.gain.exponentialRampToValueAtTime(0.001, now + 0.1);
// 停止振荡器
setTimeout(() => {
oscillator.stop();
oscillator2.stop();
oscillator3.stop();
delete activeOscillators[note];
}, 100);
}
// 键盘按下事件
document.addEventListener('keydown', function(e) {
const key = e.key.toLowerCase();
if (keyMap[key]) {
const keyElement = document.getElementById(keyMap[key]);
const note = keyElement.getAttribute('data-note');
if (!keyElement.classList.contains('active')) {
keyElement.classList.add('active');
playNote(note);
}
}
});
// 键盘释放事件
document.addEventListener('keyup', function(e) {
const key = e.key.toLowerCase();
if (keyMap[key]) {
const keyElement = document.getElementById(keyMap[key]);
const note = keyElement.getAttribute('data-note');
keyElement.classList.remove('active');
stopNote(note);
}
});
// 鼠标/触摸事件
document.querySelectorAll('.key').forEach(key => {
// 鼠标/触摸按下
key.addEventListener('mousedown', function() {
const note = this.getAttribute('data-note');
this.classList.add('active');
playNote(note);
});
key.addEventListener('touchstart', function(e) {
e.preventDefault();
const note = this.getAttribute('data-note');
this.classList.add('active');
playNote(note);
});
// 鼠标/触摸释放
key.addEventListener('mouseup', function() {
const note = this.getAttribute('data-note');
this.classList.remove('active');
stopNote(note);
});
key.addEventListener('touchend', function(e) {
e.preventDefault();
const note = this.getAttribute('data-note');
this.classList.remove('active');
stopNote(note);
});
// 鼠标移出时也停止音符
key.addEventListener('mouseleave', function() {
if (this.classList.contains('active')) {
const note = this.getAttribute('data-note');
this.classList.remove('active');
stopNote(note);
}
});
});
// 窗口失去焦点时停止所有音符
window.addEventListener('blur', function() {
document.querySelectorAll('.key.active').forEach(key => {
key.classList.remove('active');
const note = key.getAttribute('data-note');
stopNote(note);
});
});
});
</script>
</body>
</html>