Skip to content

Add piano key customization and improved styling by CSS variables #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 72 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ An interactive piano keyboard for React. Supports custom sounds, touch/click/key
## Installing

```
yarn add react-piano
npm install react-piano

# pnpm add react-piano
```

Alternatively, you can download the UMD build from [unpkg](https://unpkg.com/react-piano).
Expand Down Expand Up @@ -63,49 +65,96 @@ react-piano does not implement audio playback of each note, so you have to imple

## Props

| Name | Type | Description |
| ---- | ---- | ----------- |
| `noteRange` | **Required** object | An object with format `{ first: 48, last: 77 }` where first and last are MIDI numbers that correspond to natural notes. You can use `MidiNumbers.NATURAL_MIDI_NUMBERS` to identify whether a number is a natural note or not. |
| `playNote` | **Required** function | `(midiNumber) => void` function to play a note specified by MIDI number. |
| `stopNote` | **Required** function | `(midiNumber) => void` function to stop playing a note. |
| `width` | **Conditionally required** number | Width in pixels of the component. While this is not strictly required, if you omit it, the container around the `<Piano>` will need to have an explicit width and height in order to render correctly. |
| `activeNotes` | Array of numbers | An array of MIDI numbers, e.g. `[44, 47, 54]`, which allows you to programmatically play notes on the piano. |
| `keyWidthToHeight` | Number | Ratio of key width to height. Used to specify the dimensions of the piano key. |
| `renderNoteLabel` | Function | `({ keyboardShortcut, midiNumber, isActive, isAccidental }) => node` function to render a label on piano keys that have keyboard shortcuts |
| `className` | String | A className to add to the component. |
| `disabled` | Boolean | Whether to show disabled state. Useful when audio sounds need to be asynchronously loaded. |
| `keyboardShortcuts` | Array of object | An array of form `[{ key: 'a', midiNumber: 48 }, ...]`, where `key` is a `keyEvent.key` value. You can generate this using `KeyboardShortcuts.create`, or use your own method to generate it. You can omit it if you don't want to use keyboard shortcuts. **Note:** this shouldn't be generated inline in JSX because it can cause problems when diffing for shortcut changes. |
| `onPlayNoteInput` | Function | `(midiNumber, { prevActiveNotes }) => void` function that fires whenever a play-note event is fired. Can use `prevActiveNotes` to record notes. |
| `onStopNoteInput` | Function | `(midiNumber, { prevActiveNotes }) => void` function that fires whenever a stop-note event is fired. Can use `prevActiveNotes` to record notes. |
| Name | Type | Description |
| ------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `noteRange` | **Required** object | An object with format `{ first: 48, last: 77 }` where first and last are MIDI numbers that correspond to natural notes. You can use `MidiNumbers.NATURAL_MIDI_NUMBERS` to identify whether a number is a natural note or not. |
| `playNote` | **Required** function | `(midiNumber) => void` function to play a note specified by MIDI number. |
| `stopNote` | **Required** function | `(midiNumber) => void` function to stop playing a note. |
| `width` | **Conditionally required** number | Width in pixels of the component. While this is not strictly required, if you omit it, the container around the `<Piano>` will need to have an explicit width and height in order to render correctly. |
| `activeNotes` | Array of numbers | An array of MIDI numbers, e.g. `[44, 47, 54]`, which allows you to programmatically play notes on the piano. |
| `keyWidthToHeight` | Number | Ratio of key width to height. Used to specify the dimensions of the piano key. |
| `renderNoteLabel` | Function | `({ keyboardShortcut, midiNumber, isActive, isAccidental }) => node` function to render a label on piano keys that have keyboard shortcuts |
| `renderKey` | Function | `({ KeyComponent, key, keyProps, midiNumber, isActive, isAccidental }) => node` function to render a customized piano key. Please note that you should manually add key prop to KeyComponent for React working properly. |
| `className` | String | A className to add to the component. |
| `disabled` | Boolean | Whether to show disabled state. Useful when audio sounds need to be asynchronously loaded. |
| `keyboardShortcuts` | Array of object | An array of form `[{ key: 'a', midiNumber: 48 }, ...]`, where `key` is a `keyEvent.key` value. You can generate this using `KeyboardShortcuts.create`, or use your own method to generate it. You can omit it if you don't want to use keyboard shortcuts. **Note:** this shouldn't be generated inline in JSX because it can cause problems when diffing for shortcut changes. |
| `onPlayNoteInput` | Function | `(midiNumber, { prevActiveNotes }) => void` function that fires whenever a play-note event is fired. Can use `prevActiveNotes` to record notes. |
| `onStopNoteInput` | Function | `(midiNumber, { prevActiveNotes }) => void` function that fires whenever a stop-note event is fired. Can use `prevActiveNotes` to record notes. |

## Recording/saving notes

You can "record" notes that are played on a `<Piano>` by using `onPlayNoteInput` or `onStopNoteInput`, and you can then play back the recording by using `activeNotes`. See [this CodeSandbox](https://codesandbox.io/s/l4jjvzmp47) which demonstrates how to set that up.

<a href="https://codesandbox.io/s/l4jjvzmp47"><img width="300" src="/demo/public/images/recording-demo.gif" alt="demo of recording" /></a>

## Customizing Key

You can customize the key component by passing a render function to the `renderKey` prop. Pass your own props after spreading the `keyProps` object:

```javascript
renderKey={({ KeyComponent, key, keyProps }) => (
<KeyComponent
key={key}
{...keyProps}
{/* your own props here */}
/>
)}
```

Here are the types of `keyProps` that you can overwrite:

```
naturalKeyWidth: number;
midiNumber: number;
noteRange: { first: number; last: number };
active: boolean;
accidental: boolean;
disabled: boolean;
onPlayNoteInput: (midiNumber: number) => void;
onStopNoteInput: (midiNumber: number) => void;
gliss: boolean;
useTouchEvents: boolean;
children: React.ReactNode;
```

Please note that the default `children` is the result of `renderNoteLabel`.

## Customizing styles

You can customize many aspects of the piano using CSS. In javascript, you can override the base styles by creating your own set of overrides:
You can customize the look and feel of the piano by overriding CSS variables. In your own CSS file, you can override the base styles by creating your own set of overrides:

```javascript
import 'react-piano/dist/styles.css';
import './customPianoStyles.css'; // import a set of overrides
import './customPianoStyles.css'; // import a set of overrides
```

In the CSS file you can do things like:

```css
.ReactPiano__Key--active {
background: #f00; /* Change the default active key color to bright red */
:root {
--ReactPiano__Key--active-bg: #f00; /* Change the default active key color to bright red */
--ReactPiano__Key--accidental-bg: #000; /* Change accidental keys to be completely black */
}

.ReactPiano__Key--accidental {
background: #000; /* Change accidental keys to be completely black */
}
```

See [styles.css](/src/styles.css) for more detail on what styles can be customized.
Here are the CSS variables for your customization:

```css
--ReactPiano__Key--accidental-bg: #555;
--ReactPiano__Key--natural-bg: #f6f5f3;
--ReactPiano__Key--natural-border: #888;
--ReactPiano__Key--active-bg: #3ac8da;
--ReactPiano__Key--disabled-accidental-bg: #ddd;
--ReactPiano__Key--disabled-accidental-border: #999;
--ReactPiano__Key--disabled-natural-bg: #eee;
--ReactPiano__Key--disabled-natural-border: #aaa;
--ReactPiano__NoteLabel--accidental-color: #f8e8d5;
--ReactPiano__NoteLabel--natural-color: #888;
--ReactPiano__NoteLabel--natural-active-color: #f8e8d5;
```

See the [styles.css](/src/styles.css) for a list of CSS variables that you can override.

## Upgrading versions

Expand Down
Loading