繁体   English   中英

"<i>Why does changing state (useState) break my p5.js code?<\/i>为什么更改状态 (useState) 会破坏我的 p5.js 代码?<\/b> <i>(React + p5.js application)<\/i> (反应 + p5.js 应用程序)<\/b>"

[英]Why does changing state (useState) break my p5.js code? (React + p5.js application)

我使用 p5.js 和 React 创建了一个音频可视化器,我需要一些帮助。

我试图在歌曲开始播放时将按钮输入文本的状态从Play更改为Stop ”,并在用户决定在那一刻停止歌曲时将“ Stop ”更改为Play ”。

我正在使用react-p5库,并且在用户单击播放按钮时我setState之前一切正常。 由于这会导致重新渲染,它会破坏我的画布。

有没有办法只重新渲染按钮元素? 我不太确定如何在不更改状态的情况下更改按钮的内部文本?

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;

可悲的是没有很多可用的资源,包括 react + p5.js

如果有人想花时间克隆这个存储库以查看问题可能是什么,我将非常感激。

回购链接: https ://github.com/imperium11/audio-visualizer

  1. npm i
  2. npm run dev-build
  3. npm start

这里的问题是,每次更新功能组件中的状态时,都会再次调用该函数。 结果,每次状态更改时,您都重新声明preload<\/code> \/ setup<\/code> \/ draw<\/code> ,由于react-p5<\/code>的工作方式,正在运行的草图将开始使用您更新的draw<\/code>功能。 但是,更新后的绘图函数需要定义fft<\/code> ,但新draw<\/code>函数引用的fft<\/code>变量的版本未定义。

为了解决这个问题,您可以将草图使用的任何局部变量变成状态变量。 在本例中,我将所有本地人打包到一个对象中:

另一种方法是将使用这些局部变量的函数实际制作为状态变量。 这样,它们只会在第一次为它的每个实例调用组件函数时被声明。 这可能有点小技巧,但它的优点是可以快速更新局部变量(如果您在draw()<\/code>函数中更改局部变量,我的理解是您不希望将它们变成状态变量,因为高频状态更新可能会影响性能。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM