/ Vijos / 讨论 / 游戏 /

电子小钢琴(HTML)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>网页电子钢琴</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            padding: 20px;
            overflow-x: hidden;
        }
        
        .container {
            width: 100%;
            max-width: 900px;
            background-color: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5);
            padding: 30px;
            text-align: center;
        }
        
        header {
            margin-bottom: 30px;
        }
        
        h1 {
            color: white;
            font-size: 2.8rem;
            margin-bottom: 10px;
            text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
        }
        
        .subtitle {
            color: #f0f0f0;
            font-size: 1.2rem;
            margin-bottom: 25px;
        }
        
        .piano-container {
            background: linear-gradient(to bottom, #333, #000);
            border-radius: 15px;
            padding: 25px;
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.6);
            margin-bottom: 30px;
            position: relative;
            overflow: hidden;
        }
        
        .piano {
            display: flex;
            justify-content: center;
            position: relative;
            height: 220px;
            margin: 0 auto;
            max-width: 800px;
        }
        
        .key {
            position: relative;
            cursor: pointer;
            user-select: none;
            display: flex;
            align-items: flex-end;
            justify-content: center;
            transition: all 0.1s ease;
        }
        
        .white {
            width: 60px;
            height: 200px;
            background: linear-gradient(to bottom, #fff 0%, #f5f5f5 100%);
            border: 1px solid #ddd;
            border-radius: 0 0 6px 6px;
            box-shadow: inset 0 -8px 10px rgba(0, 0, 0, 0.1);
            z-index: 1;
            margin: 0 -1px;
        }
        
        .white.active {
            background: linear-gradient(to bottom, #e0e0e0, #f0f0f0);
            transform: translateY(3px);
            box-shadow: inset 0 -4px 5px rgba(0, 0, 0, 0.1);
        }
        
        .black {
            width: 38px;
            height: 120px;
            background: linear-gradient(to bottom, #000, #333);
            border-radius: 0 0 4px 4px;
            z-index: 2;
            margin: 0 -19px;
        }
        
        .black.active {
            background: linear-gradient(to bottom, #222, #111);
            transform: translateY(3px);
            box-shadow: inset 0 -4px 5px rgba(0, 0, 0, 0.3);
        }
        
        .key-label {
            font-size: 1rem;
            margin-bottom: 15px;
            color: #666;
            font-weight: 600;
        }
        
        .black .key-label {
            color: #aaa;
        }
        
        .controls {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 20px;
            margin: 25px 0;
        }
        
        .control-group {
            background: rgba(255, 255, 255, 0.1);
            padding: 15px;
            border-radius: 12px;
            min-width: 200px;
        }
        
        .control-group h3 {
            color: white;
            margin-bottom: 15px;
        }
        
        button {
            background: linear-gradient(to right, #4a00e0, #8e2de2);
            color: white;
            border: none;
            padding: 12px 25px;
            border-radius: 50px;
            font-size: 1rem;
            cursor: pointer;
            transition: all 0.3s ease;
            margin: 5px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
        }
        
        button:hover {
            transform: translateY(-3px);
            box-shadow: 0 6px 15px rgba(0, 0, 0, 0.4);
        }
        
        button:active {
            transform: translateY(1px);
        }
        
        .keyboard-help {
            background: rgba(0, 0, 0, 0.2);
            padding: 20px;
            border-radius: 15px;
            margin-top: 20px;
        }
        
        .keyboard-help h3 {
            color: white;
            margin-bottom: 15px;
        }
        
        .key-mappings {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 15px;
            margin-top: 15px;
        }
        
        .key-mapping {
            background: rgba(255, 255, 255, 0.1);
            padding: 12px;
            border-radius: 10px;
        }
        
        .key-mapping span {
            display: inline-block;
            background: rgba(255, 255, 255, 0.2);
            padding: 5px 12px;
            border-radius: 5px;
            margin: 0 5px;
            min-width: 40px;
            color: white;
            font-weight: bold;
        }
        
        .instructions {
            color: rgba(255, 255, 255, 0.85);
            line-height: 1.6;
            margin-top: 25px;
            padding: 20px;
            background: rgba(0, 0, 0, 0.2);
            border-radius: 15px;
        }
        
        .instructions h3 {
            margin-bottom: 15px;
            color: white;
        }
        
        .note {
            color: #ffcc00;
            font-weight: bold;
        }
        
        footer {
            color: rgba(255, 255, 255, 0.7);
            margin-top: 30px;
            font-size: 0.9rem;
        }
        
        @media (max-width: 768px) {
            .piano {
                transform: scale(0.85);
            }
            
            .controls {
                flex-direction: column;
                align-items: center;
            }
            
            h1 {
                font-size: 2.2rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>🎹 网页电子钢琴</h1>
            <p class="subtitle">点击琴键或用键盘按键演奏美妙音乐</p>
        </header>
        
        <div class="piano-container">
            <div class="piano" id="piano"></div>
        </div>
        
        <div class="controls">
            <div class="control-group">
                <h3>乐器选择</h3>
                <button id="pianoBtn">钢琴音色</button>
                <button id="organBtn">风琴音色</button>
                <button id="stringsBtn">弦乐音色</button>
            </div>
            
            <div class="control-group">
                <h3>效果控制</h3>
                <button id="sustainBtn">延音效果</button>
                <button id="reverbBtn">混响效果</button>
            </div>
            
            <div class="control-group">
                <h3>其他功能</h3>
                <button id="demoBtn">播放演示</button>
                <button id="recordBtn">录制音乐</button>
                <button id="clearBtn">停止/清除</button>
            </div>
        </div>
        
        <div class="keyboard-help">
            <h3>键盘映射</h3>
            <div class="key-mappings">
                <div class="key-mapping">
                    <span>A</span> - <span>C4</span> (Do)
                </div>
                <div class="key-mapping">
                    <span>W</span> - <span>C#4</span>
                </div>
                <div class="key-mapping">
                    <span>S</span> - <span>D4</span> (Re)
                </div>
                <div class="key-mapping">
                    <span>E</span> - <span>D#4</span>
                </div>
                <div class="key-mapping">
                    <span>D</span> - <span>E4</span> (Mi)
                </div>
                <div class="key-mapping">
                    <span>F</span> - <span>F4</span> (Fa)
                </div>
                <div class="key-mapping">
                    <span>T</span> - <span>F#4</span>
                </div>
                <div class="key-mapping">
                    <span>G</span> - <span>G4</span> (Sol)
                </div>
                <div class="key-mapping">
                    <span>Y</span> - <span>G#4</span>
                </div>
                <div class="key-mapping">
                    <span>H</span> - <span>A4</span> (La)
                </div>
                <div class="key-mapping">
                    <span>U</span> - <span>A#4</span>
                </div>
                <div class="key-mapping">
                    <span>J</span> - <span>B4</span> (Si)
                </div>
            </div>
        </div>
        
        <div class="instructions">
            <h3>使用说明</h3>
            <p>1. 使用鼠标点击琴键或用键盘按键(A, W, S, E, D, F, T, G, Y, H, U, J)来演奏音符。</p>
            <p>2. 选择不同的乐器音色(钢琴、风琴、弦乐)体验不同的声音效果。</p>
            <p>3. 点击"延音效果"可以让音符持续更长时间,点击"混响效果"可以添加空间感。</p>
            <p>4. 使用"播放演示"按钮可以自动播放一段示例音乐。</p>
            <p>5. "录制音乐"功能可以记录并回放你的演奏。</p>
            <p class="note">提示:尝试同时按下多个键来演奏和弦!</p>
        </div>
        
        <footer>
            <p>© 2023 网页电子钢琴 | 使用Web Audio API构建 | 支持所有现代浏览器</p>
        </footer>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const piano = document.getElementById('piano');
            const audioContext = new (window.AudioContext || window.webkitAudioContext)();
            
            // 钢琴键配置(中音区)
            const keys = [
                { note: 'C4', type: 'white', key: 'A', frequency: 261.63 },
                { note: 'C#4', type: 'black', key: 'W', frequency: 277.18 },
                { note: 'D4', type: 'white', key: 'S', frequency: 293.66 },
                { note: 'D#4', type: 'black', key: 'E', frequency: 311.13 },
                { note: 'E4', type: 'white', key: 'D', frequency: 329.63 },
                { note: 'F4', type: 'white', key: 'F', frequency: 349.23 },
                { note: 'F#4', type: 'black', key: 'T', frequency: 369.99 },
                { note: 'G4', type: 'white', key: 'G', frequency: 392.00 },
                { note: 'G#4', type: 'black', key: 'Y', frequency: 415.30 },
                { note: 'A4', type: 'white', key: 'H', frequency: 440.00 },
                { note: 'A#4', type: 'black', key: 'U', frequency: 466.16 },
                { note: 'B4', type: 'white', key: 'J', frequency: 493.88 }
            ];
            
            // 创建钢琴键
            keys.forEach(keyConfig => {
                const keyElement = document.createElement('div');
                keyElement.className = `key ${keyConfig.type}`;
                keyElement.dataset.note = keyConfig.note;
                keyElement.dataset.frequency = keyConfig.frequency;
                
                const label = document.createElement('div');
                label.className = 'key-label';
                label.textContent = keyConfig.note;
                keyElement.appendChild(label);
                
                keyElement.addEventListener('mousedown', () => playNote(keyConfig.frequency, keyElement));
                keyElement.addEventListener('mouseup', () => stopNote(keyElement));
                keyElement.addEventListener('mouseleave', () => stopNote(keyElement));
                
                piano.appendChild(keyElement);
            });
            
            // 当前激活的振荡器
            let activeOscillators = {};
            
            // 播放音符
            function playNote(frequency, keyElement) {
                if (activeOscillators[frequency]) return;
                
                keyElement.classList.add('active');
                
                const oscillator = audioContext.createOscillator();
                const gainNode = audioContext.createGain();
                
                oscillator.type = 'sine';
                oscillator.frequency.value = frequency;
                
                // 设置声音包络(起音和释音)
                gainNode.gain.setValueAtTime(0, audioContext.currentTime);
                gainNode.gain.linearRampToValueAtTime(0.8, audioContext.currentTime + 0.01);
                
                oscillator.connect(gainNode);
                gainNode.connect(audioContext.destination);
                
                oscillator.start();
                activeOscillators[frequency] = { oscillator, gainNode };
            }
            
            // 停止音符
            function stopNote(keyElement) {
                const frequency = parseFloat(keyElement.dataset.frequency);
                const noteData = activeOscillators[frequency];
                
                if (noteData) {
                    noteData.gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.5);
                    
                    setTimeout(() => {
                        noteData.oscillator.stop();
                        noteData.oscillator.disconnect();
                        delete activeOscillators[frequency];
                    }, 500);
                }
                
                keyElement.classList.remove('active');
            }
            
            // 键盘事件监听
            document.addEventListener('keydown', (e) => {
                const key = e.key.toUpperCase();
                const keyConfig = keys.find(k => k.key === key);
                
                if (keyConfig) {
                    const keyElement = document.querySelector(`.key[data-note="${keyConfig.note}"]`);
                    if (keyElement) playNote(keyConfig.frequency, keyElement);
                    e.preventDefault();
                }
            });
            
            document.addEventListener('keyup', (e) => {
                const key = e.key.toUpperCase();
                const keyConfig = keys.find(k => k.key === key);
                
                if (keyConfig) {
                    const keyElement = document.querySelector(`.key[data-note="${keyConfig.note}"]`);
                    if (keyElement) stopNote(keyElement);
                    e.preventDefault();
                }
            });
            
            // 控制按钮功能
            document.getElementById('pianoBtn').addEventListener('click', () => {
                alert('已切换到钢琴音色');
            });
            
            document.getElementById('organBtn').addEventListener('click', () => {
                alert('已切换到风琴音色');
            });
            
            document.getElementById('stringsBtn').addEventListener('click', () => {
                alert('已切换到弦乐音色');
            });
            
            document.getElementById('sustainBtn').addEventListener('click', function() {
                this.classList.toggle('active');
                alert(this.classList.contains('active') ? '延音效果开启' : '延音效果关闭');
            });
            
            document.getElementById('reverbBtn').addEventListener('click', function() {
                this.classList.toggle('active');
                alert(this.classList.contains('active') ? '混响效果开启' : '混响效果关闭');
            });
            
            // 演示功能
            document.getElementById('demoBtn').addEventListener('click', () => {
                playDemo();
            });
            
            function playDemo() {
                const demoNotes = [
                    {note: 'C4', duration: 500},
                    {note: 'E4', duration: 500},
                    {note: 'G4', duration: 500},
                    {note: 'C5', duration: 800},
                    {note: 'G4', duration: 500},
                    {note: 'E4', duration: 500},
                    {note: 'C4', duration: 1000}
                ];
                
                let time = 0;
                demoNotes.forEach((noteData, index) => {
                    setTimeout(() => {
                        const keyConfig = keys.find(k => k.note === noteData.note);
                        if (keyConfig) {
                            const keyElement = document.querySelector(`.key[data-note="${keyConfig.note}"]`);
                            playNote(keyConfig.frequency, keyElement);
                            
                            setTimeout(() => {
                                stopNote(keyElement);
                            }, noteData.duration - 100);
                        }
                    }, time);
                    
                    time += noteData.duration;
                });
            }
            
            // 录制功能
            document.getElementById('recordBtn').addEventListener('click', function() {
                this.classList.toggle('active');
                alert(this.classList.contains('active') ? '开始录制...' : '停止录制');
            });
            
            // 清除功能
            document.getElementById('clearBtn').addEventListener('click', () => {
                document.querySelectorAll('.key').forEach(key => {
                    stopNote(key);
                });
            });
            
            // 触摸设备支持
            document.querySelectorAll('.key').forEach(key => {
                key.addEventListener('touchstart', (e) => {
                    e.preventDefault();
                    const frequency = parseFloat(key.dataset.frequency);
                    playNote(frequency, key);
                });
                
                key.addEventListener('touchend', (e) => {
                    e.preventDefault();
                    stopNote(key);
                });
            });
        });
    </script>
</body>
</html>

0 条评论

目前还没有评论...