簡體   English   中英

如何在多個 React Redux 組件中使用 requestAnimationFrame 實現游戲循環?

[英]How to implement a gameloop with requestAnimationFrame across multiple React Redux components?

努力想出最好的方法來解決它。 我可以使用帶有requestAnimationFrame的遞歸調用來進行游戲循環:

export interface Props {
    name: string;
    points: number;
    onIncrement?: () => void;
    onDecrement?: () => void;
}

class Hello extends React.Component<Props, object> {

    constructor(props: Props) {
        super(props);
    }

    render() {
        const { name, points, onIncrement, onDecrement } = this.props;

        return (
            <div className="hello">
                <div className="greeting">
                    Hello {name + points}
                </div>
                <button onClick={onDecrement}>-</button>
                <button onClick={onIncrement}>+</button>
            </div>
        );
    }

    componentDidMount() {
        this.tick();
    }

    tick = () => {
        this.props.onIncrement();
        requestAnimationFrame(this.tick)
    }

}

但是如果我想要在每一幀上怎么辦:

  • Component1 做 X
  • Component2 做 Y
  • Component3做Z

我可以在每個組件中再有一個循環,但是我的理解是讓多個requestAnimationFrame循環運行是不好的做法,這是一個重大的性能損失。

所以我在這里迷路了。 如何讓另一個組件使用相同的循環? (如果這是最好的方法的話!)

您需要一個父組件,它調用requestAnimationFrame並迭代需要在每個循環中更新的子組件的refs數組,調用其update (或您想調用的任何方式)方法:

 class ProgressBar extends React.Component { constructor(props) { super(props); this.state = { progress: 0, }; } update() { this.setState((state) => ({ progress: (state.progress + 0.5) % 100, })); } render() { const { color } = this.props; const { progress } = this.state; const style = { background: color, width: `${ progress }%`, }; return( <div className="progressBarWrapper"> <div className="progressBarProgress" style={ style }></div> </div> ); } } class Main extends React.Component { constructor(props) { super(props); const progress1 = this.progress1 = React.createRef(); const progress2 = this.progress2 = React.createRef(); const progress3 = this.progress3 = React.createRef(); this.componentsToUpdate = [progress1, progress2, progress3]; this.animationID = null; } componentDidMount() { this.animationID = window.requestAnimationFrame(() => this.update()); } componentWillUnmount() { window.cancelAnimationFrame(this.animationID); } update() { this.componentsToUpdate.map(component => component.current.update()); this.animationID = window.requestAnimationFrame(() => this.update()); } render() { return( <div> <ProgressBar ref={ this.progress1 } color="magenta" /> <ProgressBar ref={ this.progress2 } color="blue" /> <ProgressBar ref={ this.progress3 } color="yellow" /> </div> ); } } ReactDOM.render(<Main />, document.getElementById('app'));
 body { margin: 0; padding: 16px; } .progressBarWrapper { position: relative; width: 100%; border: 3px solid black; height: 32px; box-sizing: border-box; margin-bottom: 16px; } .progressBarProgress { position: absolute; top: 0; left: 0; height: 100%; }
 <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="app"></div>

但是請記住,如果您嘗試做一些過於復雜的事情並希望達到60 fps ,那么 React 可能不是合適的工具。

此外, setState是異步的,因此在調用它時,您只是將更新推送到 React 將在某個時間處理的隊列,這實際上可能發生在下一幀或之后。

我描述了這個簡單的例子,看看是否是這種情況,實際上並非如此。 更新被添加到隊列 ( enqueueSetState ),但工作會立即完成:

示例應用分析結果

但是,我懷疑在 React 有更多更新要處理的實際應用程序中,或者在具有時間切片、具有優先級的異步更新等功能的 React 的未來版本中……渲染實際上可能發生在不同的幀中。

一種解決方案是將諸如callbacks的數組定義為狀態的一部分。 在每個組件生命周期的開始,向該數組添加一個函數,該函數在每個循環中執行您想要的操作。 然后像這樣調用 rAF 循環中的每個函數:

update( performance.now())

// Update loop
function update( timestamp ) {
    // Execute callback
    state.callbacks.forEach( cb => cb( ...args )) // Pass frame delta, etc.

    requestAnimationFrame( update )
  }

通過一些工作,您可以調整這個簡單的示例,以提供一種從callbacks刪除函數的方法,以允許按名稱或簽名從游戲循環中動態添加/減去例程。

您還可以傳遞一個包裝函數的對象,該對象還包含一個整數,您可以使用該整數按優先級對回調進行排序。

您應該創建一個運行循環的父組件,然后將其傳遞給其他組件,它應該如下所示:

<Loop>
    <ComponentX loop={loop} />
    <ComponentY loop={loop} />
    <ComponentZ loop={loop} />
</Loop>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM