如何在后台停止 react-p5-wrapper 运行

how to stop react-p5-wrapper running in background

我遇到了 react-p5-wrapper 的问题,它在后台 运行 尽管我已经在我的 React 应用程序中切换到另一条路线。

例如,我目前在/game,控制台正在记录"running draw",但是当我切换到/about-us时,它仍然在记录,这意味着它仍然是运行 绘制函数

Here is my code in sandbox

App.js

    import "./styles.css";
    import { Route, Switch, BrowserRouter as Router, Link } from "react-router-dom";
    export default function App() {
        return (
            <div className="App">
                <Router>
                    <Link to="/game">Game</Link> | <Link to="/about-us">About Us</Link>
                    <Switch>
                        <Route path="/about-us" component={require("./abtus").default} />
                        <Route path="/game" component={require("./game").default} />
                    </Switch>
                </Router>
            </div>
        );
    }

game.js

    import { useEffect } from "react";
    import { ReactP5Wrapper, P5Instance } from "react-p5-wrapper";

    // Sound
    let pointSound, endSound;
    let playEndSound = false;

    /**
    * @param {P5Instance} p
    */
    const sketch = (p) => {
        const MAX_SPEED = 15;

        const pickDirections = () => {
            return ((Math.floor(Math.random() * 3) % 2 === 0 ? 1 : -1) * (Math.floor(Math.random() * 2) + 1));
        };

        const randomXPos = () => {
            return p.random(30, p.width - 30);
        };

        const ramdomImgIndex = () => {
            return Math.floor(Math.random() * imgs.length);
        };

        const reset = () => {
            score = 0;
            speed = 2;
            falls = [
                {
                    y: -70,
                    x: randomXPos(),
                    rotation: 0,
                    direction: pickDirections(),
                    imgIndex: ramdomImgIndex()
                }
            ];
        };

        const rotate_n_draw_image = (image,img_x,img_y,img_width,img_height,img_angle) => {
            p.imageMode(p.CENTER);
            p.translate(img_x + img_width / 2, img_y + img_width / 2);
            p.rotate((Math.PI / 180) * img_angle);
            p.image(image, 0, 0, img_width, img_height);
            p.rotate((-Math.PI / 180) * img_angle);
            p.translate(-(img_x + img_width / 2), -(img_y + img_width / 2));
            p.imageMode(p.CORNER);
        };

        // Images
        let imgs = [],
            basket = { img: null, width: 150, height: 150 },
            imgSize = 50;

        let screen = 0,
            falls = [
                {
                    y: -70,
                    x: randomXPos(),
                    rotation: 0,
                    direction: pickDirections(),
                    imgIndex: ramdomImgIndex()
                }
            ],
            score = 0,
            speed = 2;

        const startScreen = () => {
            p.background(205, 165, 142);
            p.fill(255);
            p.textAlign(p.CENTER);
            p.text("WELCOME TO MY CATCHING GAME", p.width / 2, p.height / 2);
            p.text("click to start", p.width / 2, p.height / 2 + 20);
            reset();
        };

        const gameOn = () => {
            p.background(219, 178, 157);
            p.textAlign(p.LEFT);
            p.text("score = " + score, 30, 20);
    
            falls = falls.map(({ x, y, rotation, direction, imgIndex }) => {
                // rotate while dropping
                rotation += direction;

                // dropping
                y += speed;

                return { x, y, rotation, direction, imgIndex };
            });

            falls.forEach(({ x, y, rotation, imgIndex }, i) => {
                // when is lower than the border line
                if (y > p.height) {
                    screen = 2;
                    playEndSound = true;
                }

                // when reaching the border line and is within the range
                if (
                    y > p.height - 50 &&
                    x > p.mouseX - basket.width / 2 &&
                    x < p.mouseX + basket.width / 2
                ) {
                    // Play Sound
                    pointSound.currentTime = 0;
                    pointSound.play();
                    // Increase Score
                    score += 10;

                    // Increase Speed
                    if (speed < MAX_SPEED) {
                        speed += 0.1;
                        speed = parseFloat(speed.toFixed(2));
                    }
                    // Whether add new item into array or not
                    if (i === falls.length - 1 && falls.length < 3) {
                        falls.push({
                            x: randomXPos(),
                            y: -70 - p.height / 3,
                            rotation: 0,
                            direction: pickDirections(),
                            imgIndex: ramdomImgIndex()
                        });
                    }
                    falls[i].y = -70;
                    falls[i].x = randomXPos();
                    falls[i].imgIndex = ramdomImgIndex();
                }

                rotate_n_draw_image(imgs[imgIndex], x, y, imgSize, imgSize, rotation);
            });
            p.imageMode(p.CENTER);
            p.image(
                basket.img,
                p.mouseX,
                p.height - basket.height / 2,
                basket.width,
                basket.height
            );
        };

        const endScreen = () => {
            if (playEndSound) {
                endSound.play();
                playEndSound = false;
            }

            p.background(205, 165, 142);
            p.textAlign(p.CENTER);
            p.text("GAME OVER", p.width / 2, p.height / 2);
            p.text("SCORE = " + score, p.width / 2, p.height / 2 + 20);
            p.text("click to play again", p.width / 2, p.height / 2 + 60);
        };

        p.preload = () => {
            // Load Images
            imgs[0] = p.loadImage("https://dummyimage.com/400x400");
            imgs[1] = p.loadImage("https://dummyimage.com/400x400");
            imgs[2] = p.loadImage("https://dummyimage.com/401x401");
            basket.img = p.loadImage("https://dummyimage.com/500x500");
        };

        p.setup = () => {
            p.createCanvas(
                window.innerWidth > 400 ? 400 : window.innerWidth,
                window.innerHeight > 500 ? 500 : window.innerHeight
            );
        };

        p.draw = () => {
            console.log("running draw");
            switch (screen) {
                case 0:
                    startScreen();
                    break;
                case 1:
                    gameOn();
                    break;
                case 2:
                    endScreen();
                    break;
                default:
            }
        };

        p.mousePressed = () => {
            if (screen === 0) {
                screen = 1;
            } else if (screen === 2) {
                screen = 0;
            }
        };
    };
    const CatchingGmae = () => {
        useEffect(() => {
            // eslint-disable-next-line
            pointSound = new Audio("/game/points.wav");
            // eslint-disable-next-line
            endSound = new Audio("/game/end.wav");
            pointSound.volume = 0.3;
            return () => {
                pointSound.muted = true;
                endSound.muted = true;
            };
        });

        return (
            <div className="mx-auto flex justify-center items-center">
                <ReactP5Wrapper sketch={sketch} />
            </div>
        );
    };

    export default CatchingGame;

当用户切换路线时,是否可以在后台从 运行 停止它?

根据您的设置,我可以看到两种方法可以让草图在切换路线时停止并且不再渲染 Game React 组件。

Alt 1. You can make something similar to react-p5-wrapper documentation, reacting to props:

In CatchingGmae component:

const [lastRender, setLastRender] = useState(Date.now());

useEffect(() => {
 const interval = setInterval(() => setLastRender(Date.now()), 100);

 return () => {
   clearInterval(interval);
 };
}, []);

return (
  <>
    <div className="mx-auto flex justify-center items-center">
      <ReactP5Wrapper sketch={sketch} lastRender={lastRender} />

In sketch:

 let lastRender = 0;
 p.updateWithProps = (props) => {
   lastRender = props.lastRender;
 };

 p.draw = () => {
   if (!(Date.now() > lastRender + 100)) {
     console.log("running draw");

Alt 1 的问题是 React 会无缘无故地频繁地进行计算和重新渲染。

Alt 2. Use a state outside of React, a very simple side-effect for the component, for the sketch to poll on.

Add to CatchingGmae component:

useEffect(() => {
  window.noLoop = false;

  return () => {
    window.noLoop = true;
  };
}, []);

Inside p.draw:

 if (window.noLoop) return p.noLoop();

☝ 这无需计算即可工作,但您可能希望将全局范围限定在您自己的命名空间内或使用其他状态管理器。