Skip to content

Commit

Permalink
✨ feat(ims-view-pc): add Piano
Browse files Browse the repository at this point in the history
  • Loading branch information
eternallycyf committed Sep 6, 2024
1 parent ad8c476 commit 35320c4
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 2 deletions.
117 changes: 117 additions & 0 deletions packages/ims-view-pc/src/components/Piano/Piano.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React, {
FC,
forwardRef,
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState,
type ForwardRefRenderFunction,
} from 'react';
import { song1, song2 } from './constant';
import './index.css';
import type { PianoHandle, PianoProps } from './interface';

const Piano: ForwardRefRenderFunction<PianoHandle, PianoProps> = (props, ref) => {
const keys: Record<string, { frequency: number }> = {
A: { frequency: 196 },
S: { frequency: 220 },
D: { frequency: 246 },
F: { frequency: 261 },
G: { frequency: 293 },
H: { frequency: 329 },
J: { frequency: 349 },
K: { frequency: 392 },
L: { frequency: 440 },
Z: { frequency: 493 },
X: { frequency: 523 },
C: { frequency: 587 },
V: { frequency: 659 },
};

const context = useMemo(() => new AudioContext(), []);

const play = (key: string) => {
const frequency = keys[key]?.frequency;
if (!frequency) return;

const osc = context.createOscillator();
osc.type = 'sine';
osc.frequency.value = frequency;

const gain = context.createGain();
osc.connect(gain);
gain.connect(context.destination);

gain.gain.setValueAtTime(0, context.currentTime);
gain.gain.linearRampToValueAtTime(1, context.currentTime + 0.01);

osc.start(context.currentTime);

gain.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 1);
osc.stop(context.currentTime + 1);

const keyElement = document.getElementById(`key-${key}`);
keyElement?.classList.add('pressed');
setTimeout(() => {
keyElement?.classList.remove('pressed');
}, 100);
};

useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
play(e.key.toUpperCase());
};
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, []);

const map: Record<number, string> = {
1: 'A',
2: 'S',
3: 'D',
4: 'F',
5: 'G',
6: 'H',
7: 'J',
8: 'K',
9: 'L',
10: 'Z',
11: 'X',
12: 'C',
13: 'V',
};

useImperativeHandle(ref, () => ({
playMusic,
songs: {
1: song1,
2: song2,
},
}));

function playMusic(music: number[][]) {
let startTime = 0;
music.forEach((item) => {
setTimeout(() => {
play(map[item[0]]);
}, startTime * 0.5);
startTime += item[1];
});
}

return (
<div>
<section className="keys-container">
{Object.keys(keys).map((item) => (
<div className="key" key={item} id={`key-${item}`} onClick={() => play(item)}>
<span>{item}</span>
</div>
))}
</section>
</div>
);
};
export default forwardRef(Piano);
22 changes: 22 additions & 0 deletions packages/ims-view-pc/src/components/Piano/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const song1 = [
[6, 1000],
[5, 1000],
[3, 1000],
[5, 1000],
[8, 1000],
[6, 500],
[5, 500],
[6, 1000],
];

export const song2 = [
[6, 1000],
[6, 1000],
[6, 1000],
[3, 500],
[6, 500],
[5, 1000],
[3, 500],
[2, 500],
[3, 1000],
];
71 changes: 71 additions & 0 deletions packages/ims-view-pc/src/components/Piano/demo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Button, Spin } from 'antd';
import { Piano } from 'ims-view-pc';
import React, { useRef, useState, type ElementRef } from 'react';

const Demo = () => {
const PinaoRef = useRef<ElementRef<typeof Piano>>(null!);

const simplifiedSong = [
[8, 800],
[8, 800],
[8, 800],
[9, 400],
[9, 400],
[10, 200],
[10, 200],
[12, 200],
[12, 200],
[13, 200],
[13, 200],
[8, 800],
[9, 400],
[9, 400],
[10, 200],
[10, 200],
[12, 200],
[12, 200],
[8, 800],
[10, 200],
[9, 400],
[9, 400],
[8, 800],
[8, 800],
[8, 800],
[9, 400],
[9, 400],
[10, 200],
[10, 200],
[12, 200],
[12, 200],
[13, 200],
[13, 200],
[8, 800],
[9, 400],
[9, 400],
[10, 200],
[10, 200],
[12, 200],
[12, 200],
[8, 800],
[10, 200],
[9, 400],
[9, 400],
];

return (
<>
<Piano ref={PinaoRef} />

<button type="button" onClick={() => PinaoRef.current.playMusic(PinaoRef.current.songs[1])}>
世上只有妈妈好
</button>
<button type="button" onClick={() => PinaoRef.current.playMusic(PinaoRef.current.songs[2])}>
奢香夫人
</button>
<button type="button" onClick={() => PinaoRef.current.playMusic(simplifiedSong)}>
光年之外
</button>
</>
);
};
export default Demo;
29 changes: 29 additions & 0 deletions packages/ims-view-pc/src/components/Piano/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.pressed {
background: #aaa;
}

.keys-container {
display: flex;
overflow: hidden;
flex-direction: row;
justify-content: space-between;
margin: 40px auto;
width: 800px;
height: 400px;
}

.key {
display: flex;
align-items: center;
flex: 1;
justify-content: center;
border: 4px solid black;
background: #fff;
text-align: center;
font-size: 50px;
line-height: 500px;
}

.key:hover {
background: #aaa;
}
12 changes: 12 additions & 0 deletions packages/ims-view-pc/src/components/Piano/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Piano
description: 钢琴
order: 0
toc: content
demo:
cols: 2
---

## Piano 钢琴

<code src="./demo/index.tsx">钢琴</code>
2 changes: 2 additions & 0 deletions packages/ims-view-pc/src/components/Piano/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Piano from './Piano';
export default Piano;
9 changes: 9 additions & 0 deletions packages/ims-view-pc/src/components/Piano/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface PianoProps {}

export type PianoHandle = {
playMusic: (music: number[][]) => void;
songs: {
1: number[][];
2: number[][];
};
};
6 changes: 4 additions & 2 deletions packages/ims-view-pc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@ export { default as Marker } from './components/Marker';
export * from './components/Marker/interface';
export { default as MutateObserver } from './components/MutateObserver';
export * from './components/MutateObserver/interface';
export { default as Piano } from './components/Piano';
export * from './components/Piano/interface';
export { default as Portal } from './components/Portal';
export * from './components/Portal/interface';
export { default as SectionTitle } from './components/SectionTitle';
export * from './components/SectionTitle/interface';
export { default as Sticky } from './components/Sticky';
export * from './components/Sticky/interface';
export { default as Theme } from './components/theme';
export * from './components/theme/interface';
export { default as ToggleBtn } from './components/ToggleBtn';
export * from './components/ToggleBtn/interface';
export { default as TreeModal } from './components/TreeModal';
Expand All @@ -53,8 +57,6 @@ export { default as VideoViewer } from './components/VideoViewer';
export * from './components/VideoViewer/interface';
export { default as WaterMark } from './components/WaterMark';
export * from './components/WaterMark/interface';
export { default as Theme } from './components/theme';
export * from './components/theme/interface';
export { getFieldComp, renderFormItem } from './core/helpers';
export { CustomTheme, variables } from './styles/variables';
export * from './type/index';

0 comments on commit 35320c4

Please sign in to comment.