Skip to content

Instantly share code, notes, and snippets.

@walemust
Created September 13, 2020 12:03
Show Gist options
  • Save walemust/e446ca453c02523d77def8630c7d9098 to your computer and use it in GitHub Desktop.
Save walemust/e446ca453c02523d77def8630c7d9098 to your computer and use it in GitHub Desktop.
Fork Me! FCC: Drum Machine
const projectName = 'drum-machine';
localStorage.setItem('example_project', 'Drum Machine');
const bankOne = [{
keyCode: 81,
keyTrigger: 'Q',
id: 'Heater-1',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3'
}, {
keyCode: 87,
keyTrigger: 'W',
id: 'Heater-2',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-2.mp3'
}, {
keyCode: 69,
keyTrigger: 'E',
id: 'Heater-3',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-3.mp3'
}, {
keyCode: 65,
keyTrigger: 'A',
id: 'Heater-4',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-4_1.mp3'
}, {
keyCode: 83,
keyTrigger: 'S',
id: 'Clap',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Heater-6.mp3'
}, {
keyCode: 68,
keyTrigger: 'D',
id: 'Open-HH',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Dsc_Oh.mp3'
}, {
keyCode: 90,
keyTrigger: 'Z',
id: "Kick-n'-Hat",
url: 'https://s3.amazonaws.com/freecodecamp/drums/Kick_n_Hat.mp3'
}, {
keyCode: 88,
keyTrigger: 'X',
id: 'Kick',
url: 'https://s3.amazonaws.com/freecodecamp/drums/RP4_KICK_1.mp3'
}, {
keyCode: 67,
keyTrigger: 'C',
id: 'Closed-HH',
url: 'https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3'
},
];
const 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'
}];
const activeStyle = {
backgroundColor: 'orange',
boxShadow: "0 3px orange",
height: 77,
marginTop: 13
}
const inactiveStyle = {
backgroundColor: 'grey',
marginTop: 10,
boxShadow: "3px 3px 5px black"
}
class DrumPad extends React.Component {
constructor(props) {
super(props);
this.state = {
padStyle: inactiveStyle
}
this.playSound = this.playSound.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.activatePad = this.activatePad.bind(this);
}
componentDidMount() {
document.addEventListener('keydown', this.handleKeyPress);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeyPress);
}
handleKeyPress(e) {
if (e.keyCode === this.props.keyCode) {
this.playSound();
}
}
activatePad() {
if (this.props.power) {
this.state.padStyle.backgroundColor === 'orange' ?
this.setState({
padStyle: inactiveStyle
}) :
this.setState({
padStyle: activeStyle
});
} else {
this.state.padStyle.marginTop === 13 ?
this.setState({
padStyle: inactiveStyle
}) :
this.setState({
padStyle: {
height: 77,
marginTop: 13,
backgroundColor: 'grey',
boxShadow: "0 3px grey"
}
});
}
}
playSound(e) {
const sound = document.getElementById(this.props.keyTrigger);
sound.currentTime = 0;
sound.play();
this.activatePad();
setTimeout(() => this.activatePad(), 100);
this.props.updateDisplay(this.props.clipId.replace(/-/g, ' '));
}
render() {
return (
<div id={this.props.clipId}
onClick={this.playSound}
className="drum-pad"
style={this.state.padStyle} >
<audio className='clip' id={this.props.keyTrigger} src={this.props.clip}></audio>
{this.props.keyTrigger}
</div>
)
}
}
class PadBank extends React.Component {
constructor(props) {
super(props);
}
render() {
let padBank;
this.props.power ?
padBank = this.props.currentPadBank.map((drumObj, i, padBankArr) => {
return (
<DrumPad
clipId={padBankArr[i].id}
clip={padBankArr[i].url}
keyTrigger={padBankArr[i].keyTrigger}
keyCode={padBankArr[i].keyCode}
updateDisplay={this.props.updateDisplay}
power={this.props.power} />
)
}) :
padBank = this.props.currentPadBank.map((drumObj, i, padBankArr) => {
return (
<DrumPad
clipId={padBankArr[i].id}
clip="#"
keyTrigger={padBankArr[i].keyTrigger}
keyCode={padBankArr[i].keyCode}
updateDisplay={this.props.updateDisplay}
power={this.props.power} />
)
});
return (
<div className="pad-bank" >
{padBank}
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
power: true,
display: String.fromCharCode(160),
currentPadBank: bankOne,
currentPadBankId: 'Heater Kit',
sliderVal: 0.3
}
this.displayClipName = this.displayClipName.bind(this);
this.selectBank = this.selectBank.bind(this);
this.adjustVolume = this.adjustVolume.bind(this);
this.powerControl = this.powerControl.bind(this);
this.clearDisplay = this.clearDisplay.bind(this);
}
powerControl() {
this.setState({
power: !this.state.power,
display: String.fromCharCode(160)
});
}
selectBank() {
if (this.state.power) {
this.state.currentPadBankId === 'Heater Kit' ?
this.setState({
currentPadBank: bankTwo,
display: 'Smooth Piano Kit',
currentPadBankId: 'Smooth Piano Kit',
}) :
this.setState({
currentPadBank: bankOne,
display: 'Heater Kit',
currentPadBankId: 'Heater Kit',
});
}
}
displayClipName(name) {
if (this.state.power) {
this.setState({
display: name
});
}
}
adjustVolume(e) {
if (this.state.power) {
this.setState({
sliderVal: e.target.value,
display: "Volume: " + Math.round(e.target.value * 100)
});
setTimeout(() => this.clearDisplay(), 1000);
}
}
clearDisplay() {
this.setState({
display: String.fromCharCode(160)
});
}
render() {
const powerSlider = this.state.power ? {
float: 'right'
} : {
float: 'left'
};
const bankSlider = this.state.currentPadBank === bankOne ? {
float: 'left'
} : {
float: 'right'
}; {
const clips = [].slice.call(document.getElementsByClassName('clip'));
clips.forEach(sound => {
sound.volume = this.state.sliderVal
});
}
return (
<div id="drum-machine" className="inner-container">
<PadBank
power={this.state.power}
updateDisplay={this.displayClipName}
clipVolume={this.state.sliderVal}
currentPadBank={this.state.currentPadBank} />
<div className="logo">
<div className="inner-logo ">{'FCC' + String.fromCharCode(160)}</div>
<i className="inner-logo fa fa-free-code-camp" />
</div>
<div className="controls-container">
<div className="control">
<p>Power</p>
<div onClick={this.powerControl} className="select">
<div style={powerSlider} className="inner" />
</div>
</div>
<p id="display">
{this.state.display}
</p>
<div className="volume-slider">
<input type="range" min="0" max="1" step="0.01" value={this.state.sliderVal} onChange={this.adjustVolume} />
</div>
<div className="control">
<p>Bank</p>
<div onClick={this.selectBank} className="select">
<div style={bankSlider} className="inner" />
</div>
</div>
</div>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
$padWidth: 100px;
$padHeight: 80px;
@import url('https://fonts.googleapis.com/css?family=Russo+One');
body {
font-family: Russo One;
}
body {
user-select: none;
background-color: lighten(grey, 5%);
}
#root {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.inner-container {
outline: 5px solid orange;
position: relative;
width: 660px;
text-align: center;
background-color: lighten(grey, 20%);
.pad-bank {
width: $padWidth * 3 + 32;
height: $padHeight * 3 + 32;
display: inline-block;
margin: 20px;
.drum-pad {
position: relative;
float: left;
width: $padWidth;
height: $padHeight;
margin-right: 10px;
border-radius: 5px;
padding-top: 35px;
box-sizing: border-box;
cursor: pointer;
}
}
.logo {
position: absolute;
top: 5px;
right: 10px;
.inner-logo {
display: inline-block;
font-style: italic;
font-weight: bold;
font-size: 20px;
}
}
.controls-container {
width: 240px;
height: $padHeight * 3 + 32;
display: inline-block;
margin: 40px 20px 0 10px;
vertical-align: top;
.control {
width: 100px;
margin: auto;
p {
margin-bottom: 0;
}
.select {
@extend .outer-select;
.inner {
@extend .inner-select;
}
}
}
#display {
width: 200px;
background-color: gray;
margin: 15px auto;
padding: 15px;
box-sizing: border-box;
}
}
}
.outer-select {
margin: auto;
border: 1px solid black;
width: 50px;
height: 20px;
padding: 1px;
background-color: black;
}
.inner-select {
width: 23px;
height: 19px;
background: blue;
border: 1px solid black;
box-sizing: border-box;
cursor: pointer;
}
@media screen and (max-width: 650px) {
.inner-container {
transform: scale(0.7);
}
}
// CUSTOM RANGE INPUT : Credit : https://github.com/darlanrod/input-range-scss/blob/master/_inputrange.scss
$track-color: #424242 !default;
$thumb-color: blue !default;
$thumb-radius: 0 !default;
$thumb-height: 25px !default;
$thumb-width: 8px !default;
$thumb-shadow-size: 1px !default;
$thumb-shadow-blur: 1px !default;
$thumb-shadow-color: #111 !default;
$thumb-border-width: 0 !default;
$thumb-border-color: #fff !default;
$track-width: 100% !default;
$track-height: 5px !default;
$track-shadow-size: 2px !default;
$track-shadow-blur: 2px !default;
$track-shadow-color: #222 !default;
$track-border-width: 1px !default;
$track-border-color: #000 !default;
$track-radius: 0 !default;
$contrast: 5% !default;
@mixin shadow($shadow-size, $shadow-blur, $shadow-color) {
box-shadow: $shadow-size $shadow-size $shadow-blur $shadow-color, 0 0 $shadow-size lighten($shadow-color, 5%);
}
@mixin track() {
width: $track-width;
height: $track-height;
cursor: pointer;
transition: all .2s ease;
}
@mixin thumb() {
@include shadow($thumb-shadow-size, $thumb-shadow-blur, $thumb-shadow-color);
border: $thumb-border-width solid $thumb-border-color;
height: $thumb-height;
width: $thumb-width;
border-radius: $thumb-radius;
background: $thumb-color;
cursor: pointer;
}
[type=range] {
-webkit-appearance: none;
margin: $thumb-height / 2 0;
width: $track-width;
&:focus {
outline: none;
}
&::-webkit-slider-runnable-track {
@include track();
@include shadow($track-shadow-size, $track-shadow-blur, $track-shadow-color);
background: $track-color;
border: $track-border-width solid $track-border-color;
border-radius: $track-radius;
}
&::-webkit-slider-thumb {
@include thumb();
-webkit-appearance: none;
margin-top: ((-$track-border-width * 2 + $track-height) / 2) - ($thumb-height / 2);
}
&:focus::-webkit-slider-runnable-track {
background: lighten($track-color, $contrast);
}
&::-moz-range-track {
@include track();
@include shadow($track-shadow-size, $track-shadow-blur, $track-shadow-color);
background: $track-color;
border: $track-border-width solid $track-border-color;
border-radius: $track-radius;
}
&::-moz-range-thumb {
@include thumb();
}
&::-ms-track {
@include track();
background: transparent;
border-color: transparent;
border-width: $thumb-width 0;
color: transparent;
}
&::-ms-fill-lower {
@include shadow($track-shadow-size, $track-shadow-blur, $track-shadow-color);
background: darken($track-color, $contrast);
border: $track-border-width solid $track-border-color;
border-radius: $track-radius * 2;
}
&::-ms-fill-upper {
@include shadow($track-shadow-size, $track-shadow-blur, $track-shadow-color);
background: $track-color;
border: $track-border-width solid $track-border-color;
border-radius: $track-radius * 2;
}
&::-ms-thumb {
@include thumb();
}
&:focus::-ms-fill-lower {
background: $track-color;
}
&:focus::-ms-fill-upper {
background: lighten($track-color, $contrast);
}
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment