Reputation: 842
I have main component App, a drum with 9 buttons with letters, on mouse click on each button must be started different sound file. With onClick event and function soundDetectionHandler I am detecting which letter was pressed. When detected, I am setting the state in currentKey variable to that letter/object with keyCode, keyTrigger, id, url. In child component Keypress for each letter I have div in which is embedded audio tag, with no showed own controls. In it there is a button which is onClick calling a audioActivationHandler function in main component, which should start playing of sound file(url) based on detection of class name of audio tag. document.getElementsByClassName("player").play(); Unfortunately it's not working on mouse click, can somebody check, and if this is not the best solution, has anybody to suggest a better solution. Here it is the main component:
import React, { Component } from 'react';
import './App.css';
import Keypress from '.././components/Keypress/Keypress';
class App extends Component {
constructor(props) {
super(props)
this.state = {
currentKey: [],
bankTwo : [{
keyCode: 81,
keyTrigger: 'Q',
id: 'Chord-1',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Chord_1.mp3'
}, {
keyCode: 87,
keyTrigger: 'W',
id: 'Chord-2',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Chord_2.mp3'
}, {
keyCode: 69,
keyTrigger: 'E',
id: 'Chord-3',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Chord_3.mp3'
}, {
keyCode: 65,
keyTrigger: 'A',
id: 'Shaker',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Give_us_a_light.mp3'
}, {
keyCode: 83,
keyTrigger: 'S',
id: 'Open-HH',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Dry_Ohh.mp3'
}, {
keyCode: 68,
keyTrigger: 'D',
id: 'Closed-HH',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Bld_H1.mp3'
}, {
keyCode: 90,
keyTrigger: 'Z',
id: 'Punchy-Kick',
url: 'https://s3.amazonaws.com/freecodecamp/drums/punchy_kick_1.mp3'
}, {
keyCode: 88,
keyTrigger: 'X',
id: 'Side-Stick',
url: 'https://s3.amazonaws.com/freecodecamp/drums/side_stick_1.mp3'
}, {
keyCode: 67,
keyTrigger: 'C',
id: 'Snare',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Brk_Snr.mp3'
}],
isChecked: false
}
}
soundDetectionHandler = (event) => {
let currentKey = this.state.bankTwo.filter((el, key) => {
return el.id === event.target.id
})
this.setState ({
currentKey: currentKey
})
}
audioActivationHandler = () => {
document.getElementsByClassName("player").play();
}
checkBoxHandler = () => {
this.setState({
isChecked: !this.state.isChecked
})
}
render () {
const drum = this.state.bankTwo.map((keypress, key) => {
return <Keypress
key={keypress.keyCode}
keypress={keypress}
soundDetection={this.soundDetectionHandler}
activated={this.audioActivationHandler}
/>
}
)
return (
<div className="App">
<div className="container-fluid">
<div className="container">
<div className="DrumWrapper">
<div className="Drum">
{drum}
</div>
<div className="Controlls">
<label className="switch">
<input type="checkbox"
onChange={this.toggleChange}
/>
<span className="slider"></span>
</label>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default App;
Here it is a child stateless component Keypress:
import React from 'react';
const keypress = (props) => {
return (
<div id={props.keypress.id} onClick={props.soundDetection}>
{props.keypress.keyTrigger}
<audio className="player" src={props.keypress.url}>
<button onClick={props.activated}>Play</button>
</audio>
</div>
);
}
export default keypress;
Upvotes: 0
Views: 2124
Reputation: 1114
This is what you're after. I refactored your code quite a bit. Making this work in react think was a bit mind-bending I must confess. There are a few comments. Hope this helps :)
app.css:
.player {
width:100px;
height: 100px;
border: 1px solid black;
color: black;
display: flex;
align-items: center;
justify-content:center;
cursor: pointer;
box-sizing: border-box;
}
player.js:
import React from 'react'
import './app.css'
export default class Player extends React.Component {
constructor(props) {
super(props)
this.state = {
active: false,
}
this.audio = React.createRef()
}
playAudio = () => {
this.audio.current.play()
}
toggleActiveState = () => {
this.setState({
active: !this.state.active,
})
}
render() {
return (
<div
className="player"
style={
this.state.active
? {background: 'black', color: 'white'}
: {backgroung: 'white', color: 'black'}
}
ref={this.ref}
id={this.props.id}
onClick={this.props.clicked}>
<audio
ref={this.audio}
src={this.props.url}
onEnded={this.toggleActiveState}
/>
{this.props.keyTrigger}
</div>
)
}
}
index.js:
import React from 'react'
import Player from './player'
const page = {
width: '100vw',
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}
const keyboard = {
width: '300px',
height: '300px',
display: 'flex',
flexWrap: 'wrap',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid black',
}
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
currentKey: [],
bankTwo: [
{
keyCode: 81,
keyTrigger: 'Q',
id: 'Chord-1',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Chord_1.mp3',
},
{
keyCode: 87,
keyTrigger: 'W',
id: 'Chord-2',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Chord_2.mp3',
},
{
keyCode: 69,
keyTrigger: 'E',
id: 'Chord-3',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Chord_3.mp3',
},
{
keyCode: 65,
keyTrigger: 'A',
id: 'Shaker',
url:
'https://s3.amazonaws.com/freecodecamp/drums/Give_us_a_light.mp3',
},
{
keyCode: 83,
keyTrigger: 'S',
id: 'Open-HH',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Dry_Ohh.mp3',
},
{
keyCode: 68,
keyTrigger: 'D',
id: 'Closed-HH',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Bld_H1.mp3',
},
{
keyCode: 90,
keyTrigger: 'Z',
id: 'Punchy-Kick',
url: 'https://s3.amazonaws.com/freecodecamp/drums/punchy_kick_1.mp3',
},
{
keyCode: 88,
keyTrigger: 'X',
id: 'Side-Stick',
url: 'https://s3.amazonaws.com/freecodecamp/drums/side_stick_1.mp3',
},
{
keyCode: 67,
keyTrigger: 'C',
id: 'Snare',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Brk_Snr.mp3',
},
],
isChecked: false,
bgColor: 'white',
}
this.handleKeyPress = this.handleKeyPress.bind(this)
this.Q = React.createRef()
this.W = React.createRef()
this.E = React.createRef()
this.A = React.createRef()
this.D = React.createRef()
this.S = React.createRef()
this.Z = React.createRef()
this.X = React.createRef()
this.C = React.createRef()
}
componentDidMount() {
document.addEventListener('keydown', this.handleKeyPress)
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeyPress)
}
playAudioOnClick = e => {
e.target.children[0].play()
this[e.target.id].current.toggleActiveState()
}
handleKeyPress = e => {
if (this.state.isChecked) {
// in any case uppercase
let which = e.key.toUpperCase()
// check if the pressed key is defined as a keyTrigger in the state else do nothing
if (
this.state.bankTwo.some(key => {
return key.keyTrigger === which
})
) {
// trigger functions in child
this[which].current.playAudio()
this[which].current.toggleActiveState()
}
}
}
handleCheck = () => {
this.setState({
isChecked: !this.state.isChecked,
})
}
render() {
const drum = this.state.bankTwo.map(player => {
return (
<Player
id={player.keyTrigger}
ref={this[player.keyTrigger]}
key={player.keyCode}
keyTrigger={player.keyTrigger}
url={player.url}
clicked={this.playAudioOnClick}
/>
)
})
return (
<section style={page}>
<main style={keyboard}>{drum}</main>
<section>
<label>play with keyboard</label>
<input type="checkbox" onChange={this.handleCheck} />
</section>
</section>
)
}
}
It works generally but playing too fast will leave some fields highlighted even if not active. I would love to see a better solution. For a real instrument though I would suggest using the Web Audio API.
Upvotes: 1