kimhyunwoo commited on
Commit
0b313e4
·
verified ·
1 Parent(s): f5d0a5c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +32 -29
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
6
- <title>Refined Piano Visualizer with Keyboard Input</title>
7
  <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/MidiConvert.min.js"></script>
9
  <style>
@@ -12,13 +12,13 @@
12
  --piano-bg-start: #1c1c1c;
13
  --piano-bg-end: #0d0d0d;
14
  --piano-border: #2f2f2f;
15
- --white-key-bg-start: #fdfdfd; /* Slightly brighter white */
16
  --white-key-bg-end: #e4e4e4;
17
  --white-key-border: #9e9e9e;
18
  --black-key-bg-start: #353535;
19
  --black-key-bg-end: #181818;
20
  --black-key-border: #050505;
21
- --hit-line-color: rgba(255, 80, 180, 0.8);
22
  --key-active-glow: rgba(255, 80, 180, 0.9);
23
  --particle-base-color-h: 320;
24
  --default-note-color-h: 180;
@@ -149,31 +149,33 @@
149
  box-shadow: -1px 0 2px rgba(0,0,0,0.4), 1px 0 2px rgba(0,0,0,0.4), 0 2px 3px rgba(0,0,0,0.55), inset 0 -1px 1px rgba(60,60,60,0.3);
150
  }
151
 
152
- .white-key.active {
153
- background: linear-gradient(to bottom, #d2d2d2, #c2c2c2);
154
  transform: perspective(500px) rotateX(1.2deg) translateY(1.8px);
155
  border-bottom-width: 3.2px;
156
  box-shadow: 0 0 22px 6px var(--key-active-glow),
157
  inset 0 -1px 1px rgba(255,255,255,0.38);
 
158
  }
159
 
160
- .black-key.active {
161
  background: linear-gradient(to bottom, #222222, #020202);
162
  transform: perspective(500px) rotateX(0.6deg) translateY(1.2px);
163
  border-bottom-width: 2.8px;
164
  box-shadow: 0 0 22px 6px var(--key-active-glow),
165
  inset 0 -1px 1px rgba(60,60,60,0.18);
 
166
  }
167
 
168
  #note-fall-area {
169
- width: var(--piano-actual-width, 100%); /* Default to 100% if not set */
170
- height: calc(100vh - 28vh - 15px);
171
  position: absolute;
172
  top: 0;
173
  left: 50%;
174
  transform: translateX(-50%);
175
  pointer-events: none;
176
- /* border: 1px solid red; */ /* For debugging layout */
177
  }
178
 
179
  .note-bar {
@@ -204,18 +206,17 @@
204
  .particle-container {
205
  position: absolute;
206
  width: var(--piano-actual-width, 100%);
207
- height: 28vh; /* Height of the piano container */
208
- bottom: 5px; /* Align with bottom of piano container */
209
  left: 50%;
210
  transform: translateX(-50%);
211
  pointer-events: none;
212
  z-index: 5;
213
- /* border: 1px dashed lime; */ /* Debugging */
214
  }
215
  .particle {
216
  position: absolute;
217
- width: 8px; /* Increased size */
218
- height: 8px; /* Increased size */
219
  background-color: var(--particle-color);
220
  border-radius: 50%;
221
  opacity: 1;
@@ -225,9 +226,9 @@
225
  @keyframes particleAnim {
226
  0% { transform: translateY(0) translateX(0) scale(1.3); opacity: 0.95; }
227
  100% {
228
- transform: translateY(calc( (var(--random-y) - 0.7) * -90px - 40px)) /* Adjusted upward movement */
229
- translateX(calc( (var(--random-x) - 0.5) * 60px))
230
- scale(0.15);
231
  opacity: 0;
232
  }
233
  }
@@ -278,11 +279,9 @@
278
  'a': 'C4', 'w': 'C#4', 's': 'D4', 'e': 'D#4', 'd': 'E4', 'f': 'F4',
279
  't': 'F#4', 'g': 'G4', 'y': 'G#4', 'h': 'A4', 'u': 'A#4', 'j': 'B4',
280
  'k': 'C5', 'o': 'C#5', 'l': 'D5', 'p': 'D#5', ';': 'E5', "'": 'F5'
281
- // Add more mappings as needed
282
  };
283
  const activeKeyboardKeys = new Set();
284
 
285
-
286
  function createPianoKeys() {
287
  pianoElement.innerHTML = '';
288
  whiteKeyCount = 0;
@@ -314,7 +313,7 @@
314
  if(e && e.preventDefault) e.preventDefault();
315
  if(keyElement.classList.contains('active')) return;
316
  keyElement.classList.add('active');
317
- const hue = Math.random() * 360;
318
  keyElement.style.setProperty('--key-active-glow', `hsla(${hue}, 100%, 65%, 0.9)`);
319
  synth.triggerAttack(noteName, Tone.now());
320
  createKeyParticles(keyElement, `hsla(${hue}, 100%, 65%, 0.9)`);
@@ -350,8 +349,7 @@
350
  particle.style.setProperty('--random-y', Math.random());
351
 
352
  const xPos = (keyRect.left - particleContRect.left) + (keyRect.width / 2);
353
- // Particle origin slightly above the key
354
- const yPos = (keyRect.top - particleContRect.top) - 10; // 10px above the key's top
355
 
356
  particle.style.left = `${xPos}px`;
357
  particle.style.top = `${yPos}px`;
@@ -441,8 +439,7 @@
441
  noteElement.classList.add('note-bar');
442
 
443
  const keyRect = targetKeyElement.getBoundingClientRect();
444
- const pianoElementRect = pianoElement.getBoundingClientRect();
445
-
446
  const keyIsBlack = noteData.note.includes('#');
447
  noteElement.style.width = ((keyIsBlack ? BLACK_KEY_WIDTH_PX : WHITE_KEY_WIDTH_PX) - (keyIsBlack ? 1 : 2)) + 'px';
448
  noteElement.style.left = (keyRect.left - fallAreaRect.left + 1) + 'px';
@@ -460,13 +457,16 @@
460
  noteElement.style.top = `-${noteVisualHeight}px`;
461
  noteFallArea.appendChild(noteElement);
462
 
463
- const targetY = hitLineTopRelativeToFallArea - noteVisualHeight;
 
 
464
 
465
  noteElement.animate([
466
- { transform: `translateY(0px)` },
467
- { transform: `translateY(${targetY}px)` }
 
468
  ], {
469
- duration: NOTE_FALL_DURATION_MS,
470
  easing: 'linear'
471
  }).onfinish = () => {
472
  if (noteElement.parentNode) noteElement.remove();
@@ -476,10 +476,12 @@
476
  synth.triggerAttackRelease(noteData.note, noteData.duration, hitTime, noteData.velocity);
477
  if (targetKeyElement) {
478
  targetKeyElement.style.setProperty('--key-active-glow-hue', (colorIdx * 40) % 360);
479
- targetKeyElement.classList.add('active');
 
480
  createKeyParticles(targetKeyElement, noteMainColor);
481
  setTimeout(() => {
482
  targetKeyElement.classList.remove('active');
 
483
  }, noteData.duration * 1000);
484
  }
485
  }, time + (NOTE_FALL_DURATION_MS / 1000));
@@ -513,6 +515,7 @@
513
 
514
  window.addEventListener('resize', () => {
515
  createPianoKeys();
 
516
  });
517
 
518
  </script>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
6
+ <title>Enhanced Piano Visualizer</title>
7
  <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
8
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/MidiConvert.min.js"></script>
9
  <style>
 
12
  --piano-bg-start: #1c1c1c;
13
  --piano-bg-end: #0d0d0d;
14
  --piano-border: #2f2f2f;
15
+ --white-key-bg-start: #fdfdfd;
16
  --white-key-bg-end: #e4e4e4;
17
  --white-key-border: #9e9e9e;
18
  --black-key-bg-start: #353535;
19
  --black-key-bg-end: #181818;
20
  --black-key-border: #050505;
21
+ --hit-line-color: rgba(255, 80, 180, 0.85);
22
  --key-active-glow: rgba(255, 80, 180, 0.9);
23
  --particle-base-color-h: 320;
24
  --default-note-color-h: 180;
 
149
  box-shadow: -1px 0 2px rgba(0,0,0,0.4), 1px 0 2px rgba(0,0,0,0.4), 0 2px 3px rgba(0,0,0,0.55), inset 0 -1px 1px rgba(60,60,60,0.3);
150
  }
151
 
152
+ .white-key.active, .white-key.pressed-by-note {
153
+ background: linear-gradient(to bottom, #d5d5d5, #c2c2c2);
154
  transform: perspective(500px) rotateX(1.2deg) translateY(1.8px);
155
  border-bottom-width: 3.2px;
156
  box-shadow: 0 0 22px 6px var(--key-active-glow),
157
  inset 0 -1px 1px rgba(255,255,255,0.38);
158
+ z-index: 3 !important; /* Ensure active white key is on top */
159
  }
160
 
161
+ .black-key.active, .black-key.pressed-by-note {
162
  background: linear-gradient(to bottom, #222222, #020202);
163
  transform: perspective(500px) rotateX(0.6deg) translateY(1.2px);
164
  border-bottom-width: 2.8px;
165
  box-shadow: 0 0 22px 6px var(--key-active-glow),
166
  inset 0 -1px 1px rgba(60,60,60,0.18);
167
+ z-index: 4 !important; /* Ensure active black key is on top */
168
  }
169
 
170
  #note-fall-area {
171
+ width: var(--piano-actual-width, 100%);
172
+ height: calc(100vh - 28vh - 5px - 10px); /* Piano container bottom padding + body bottom padding */
173
  position: absolute;
174
  top: 0;
175
  left: 50%;
176
  transform: translateX(-50%);
177
  pointer-events: none;
178
+ /* border: 1px solid red; */
179
  }
180
 
181
  .note-bar {
 
206
  .particle-container {
207
  position: absolute;
208
  width: var(--piano-actual-width, 100%);
209
+ height: 28vh;
210
+ bottom: 5px;
211
  left: 50%;
212
  transform: translateX(-50%);
213
  pointer-events: none;
214
  z-index: 5;
 
215
  }
216
  .particle {
217
  position: absolute;
218
+ width: 8px;
219
+ height: 8px;
220
  background-color: var(--particle-color);
221
  border-radius: 50%;
222
  opacity: 1;
 
226
  @keyframes particleAnim {
227
  0% { transform: translateY(0) translateX(0) scale(1.3); opacity: 0.95; }
228
  100% {
229
+ transform: translateY(calc( (var(--random-y) - 0.7) * -100px - 50px))
230
+ translateX(calc( (var(--random-x) - 0.5) * 70px))
231
+ scale(0.1);
232
  opacity: 0;
233
  }
234
  }
 
279
  'a': 'C4', 'w': 'C#4', 's': 'D4', 'e': 'D#4', 'd': 'E4', 'f': 'F4',
280
  't': 'F#4', 'g': 'G4', 'y': 'G#4', 'h': 'A4', 'u': 'A#4', 'j': 'B4',
281
  'k': 'C5', 'o': 'C#5', 'l': 'D5', 'p': 'D#5', ';': 'E5', "'": 'F5'
 
282
  };
283
  const activeKeyboardKeys = new Set();
284
 
 
285
  function createPianoKeys() {
286
  pianoElement.innerHTML = '';
287
  whiteKeyCount = 0;
 
313
  if(e && e.preventDefault) e.preventDefault();
314
  if(keyElement.classList.contains('active')) return;
315
  keyElement.classList.add('active');
316
+ const hue = parseFloat(keyElement.style.getPropertyValue('--key-active-glow-hue') || Math.random() * 360);
317
  keyElement.style.setProperty('--key-active-glow', `hsla(${hue}, 100%, 65%, 0.9)`);
318
  synth.triggerAttack(noteName, Tone.now());
319
  createKeyParticles(keyElement, `hsla(${hue}, 100%, 65%, 0.9)`);
 
349
  particle.style.setProperty('--random-y', Math.random());
350
 
351
  const xPos = (keyRect.left - particleContRect.left) + (keyRect.width / 2);
352
+ const yPos = (keyRect.top - particleContRect.top) - 10;
 
353
 
354
  particle.style.left = `${xPos}px`;
355
  particle.style.top = `${yPos}px`;
 
439
  noteElement.classList.add('note-bar');
440
 
441
  const keyRect = targetKeyElement.getBoundingClientRect();
442
+
 
443
  const keyIsBlack = noteData.note.includes('#');
444
  noteElement.style.width = ((keyIsBlack ? BLACK_KEY_WIDTH_PX : WHITE_KEY_WIDTH_PX) - (keyIsBlack ? 1 : 2)) + 'px';
445
  noteElement.style.left = (keyRect.left - fallAreaRect.left + 1) + 'px';
 
457
  noteElement.style.top = `-${noteVisualHeight}px`;
458
  noteFallArea.appendChild(noteElement);
459
 
460
+ const targetY = hitLineTopRelativeToFallArea - noteVisualHeight; // Note's bottom should reach hit line's top
461
+
462
+ const noteFallAndPersistDuration = NOTE_FALL_DURATION_MS + (noteData.duration * 1000);
463
 
464
  noteElement.animate([
465
+ { transform: `translateY(0px)` }, // Start from above
466
+ { transform: `translateY(${targetY}px)`, offset: NOTE_FALL_DURATION_MS / noteFallAndPersistDuration }, // Arrive at hit line
467
+ { transform: `translateY(${targetY}px)` } // Stay at hit line for its duration
468
  ], {
469
+ duration: noteFallAndPersistDuration,
470
  easing: 'linear'
471
  }).onfinish = () => {
472
  if (noteElement.parentNode) noteElement.remove();
 
476
  synth.triggerAttackRelease(noteData.note, noteData.duration, hitTime, noteData.velocity);
477
  if (targetKeyElement) {
478
  targetKeyElement.style.setProperty('--key-active-glow-hue', (colorIdx * 40) % 360);
479
+ targetKeyElement.classList.add('active'); // General active class
480
+ targetKeyElement.classList.add('pressed-by-note'); // Specific class for note presses
481
  createKeyParticles(targetKeyElement, noteMainColor);
482
  setTimeout(() => {
483
  targetKeyElement.classList.remove('active');
484
+ targetKeyElement.classList.remove('pressed-by-note');
485
  }, noteData.duration * 1000);
486
  }
487
  }, time + (NOTE_FALL_DURATION_MS / 1000));
 
515
 
516
  window.addEventListener('resize', () => {
517
  createPianoKeys();
518
+ // Re-calculate dynamic positions if necessary
519
  });
520
 
521
  </script>