如何在Main类中用this替代全局变量main实现THREE.js动画循环?
main References with this and Fix requestAnimationFrame Binding Issues Let's break down how to fix your animation loop problems and cut the dependency on the global main variable:
Core Problem
The key issue here is two-fold:
- When you pass
main.animatedirectly torequestAnimationFrame, the function loses itsthisbinding—so subsequent calls end up withthisasnull(orwindowin non-strict mode). - Relying on the global
mainvariable makes your code tightly coupled and harder to maintain or reuse.
Step-by-Step Fixes
1. Swap all main references to this in the animate method
First, update every instance of main inside animate() to use this instead. This lets us reference the current class instance directly, no global variable required.
2. Fix this binding for the requestAnimationFrame callback
We need to make sure this stays tied to your Main instance when the browser calls animate for the animation frame. You have three reliable options here:
Option A: Wrap the callback in an arrow function
Arrow functions inherit the this context from their surrounding scope, so wrapping your callback will preserve the correct instance:
requestAnimationFrame(() => this.animate());
Option B: Bind the animate method in the constructor
Add this line to your constructor to permanently bind animate to the instance—this way, no matter how the function is called, this will always point to your Main object:
constructor(idDiv) { this.div = document.getElementById(idDiv); this.sceneManager = new SceneManager(this.div); // Bind animate to the current instance this.animate = this.animate.bind(this); }
With this, you can keep the callback as requestAnimationFrame(this.animate);
Option C: Define animate as a class arrow function
If you're using modern ES6+ syntax, define animate as an arrow function method. This automatically binds this to the instance:
animate = () => { console.log('Main::animate::this', this); requestAnimationFrame(this.animate); this.sceneManager.animate(); }
3. Clean up the execute method call
Your original execute method calls this.animate(main.sceneManager);—since we're now using this.sceneManager inside animate, you don't need to pass the scene manager as an argument. Just call this.animate(); instead.
Full Modified Code
Here's the complete fixed code using Option A (arrow function wrapper) for clarity:
class Main { constructor(idDiv) { this.div = document.getElementById(idDiv); this.sceneManager = new SceneManager(this.div); } animate() { console.log('Main::animate::this', this); // Use arrow function to preserve this binding requestAnimationFrame(() => this.animate()); // Replace main with this for sceneManager access this.sceneManager.animate(); } execute() { new ManageEvents(this.div, this.sceneManager); const atla = new Atla(); const repository = new Repository(); const loadData = new LoadData(atla, repository); loadData.load(this.sceneManager); console.log('Main::execute::this', this); // No need to pass sceneManager anymore this.animate(); } } const main = new Main('original'); main.execute();
Why Your Previous Attempts Failed
requestAnimationFrame(main.animate(main)): This immediately runsmain.animate(main)and passes its return value (which isundefined) torequestAnimationFrame—hence the "callback is not a function" error. It also causes an infinite loop because you're callinganimatesynchronously over and over.- Passing the instance as an argument: Without fixing the
thisbinding, even if you passmainas a parameter, the function's internalthiswould still benullin subsequent animation frame calls.
内容的提问来源于stack exchange,提问作者Yone

