I created an Audio visualizer using p5.js and React and i need some help.
I am trying to change the state of my button input text from Play
to Stop
when the song starts playing & from Stop
back to Play
when the user decides to stop the song at that moment.
I am using react-p5
library and everything works great until i setState
when a user clicks the play button. Since that causes a re render it breaks my canvas.
Is there a way to only re-render the button element? I am not too sure how i would go about changing the inner text of my button without a state change?
import React, { useState } from 'react';
import Sketch from 'react-p5';
import 'p5/lib/addons/p5.sound';
import Beat from '../../instrumental.mp3';
const App = () => {
let width = 900;
let height = 600;
let song;
let fft;
const [flag, setFlag] = useState(true);
const preload = (p) => {
p.soundFormats('mp3');
song = p.loadSound(Beat);
}
const setup = (p, canvasParentRef) => {
// react-p5 conveniently initializes window.p5 thats why all the other p5's
// have been changed to p in order to create an FFT object
fft = new p5.FFT();
// console.log(p.point)
// use parent to render the canvas in this ref
// without it p5 will render the canvas outside of this component
const canvas = p.createCanvas(width, height).parent(canvasParentRef);
}
const playStop = () => {
if (song.isPlaying()) {
song.pause();
setFlag(true);
//p.noLoop();
} else {
song.play();
setFlag(false);
//p.loop();
}
}
const draw = p => {
p.background(0);
// by default the stroke color is black
// we need to change this in order to see the wave
p.stroke(255, 204, 0);
// no fill in between waves
p.noFill();
// returns an array with 1024 elements
let wave = fft.waveform();
p.beginShape();
// By looping through the waveform data, we are able
// to draw the waveform across the canvas
for (let i = 0; i < width; i++) {
// create an index that maps the for loop variable
// to the index of the wave we want
// value must be integer thats we we use floor
let index = p.floor(p.map(i, 0, width, 0, wave.length));
let x = i;
let y = wave[index] * 100 + height / 2;
p.vertex(x, y);
}
p.endShape();
}
return (
<div className='outerbox'>
<h1>Audio Visualizer</h1>
<Sketch preload={preload} setup={setup} draw={draw}/>
{flag ? <button onClick={playStop}>Play</button> : <button onClick={playStop}>Stop</button>}
</div>
);
}
export default App;
The sad thing is there aren't many resources available that includes react + p5.js
If anyone would like to take the time and clone this repository down in order to see what the problem might be, i would very much appreciate that.
Repo link: https://github.com/imperium11/audio-visualizer
npm i
npm run dev-build
npm start
The issue here is that each time you update state in a functional component the function gets called again. As a result each time state changes you re-declare your preload<\/code> \/
setup<\/code> \/
draw<\/code> , because of the way
react-p5<\/code> work, the running sketch will start using your updated
draw<\/code> function.
However, the updated draw function expects
fft<\/code> to be defined, but the version of the
fft<\/code> variable referenced by the new
draw<\/code> function is undefined.
In this example I've packed all the locals into one object:
draw()<\/code> function, my understanding is that you would not want to make those into state variables, since high frequency state updates may impact performance.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.