I’m trying to create a web 3D scene using Three.js, in the hope of better future maintainability I opted to code it as a class
export class RenderCanvas {
#canvas; // document.querySelector(idOfCanvas)
#renderer; // WebgL renderer
#scene;
#controls // Trackball control
#camera
// ... other properties
constructor(){ /* ... */ }
startup() {
this.#renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: this.#canvas
});
// Other initializations
}
loadGeometries() { /* add models and lights into the scene */ }
update(time) {
const canvas = this.#renderer.domElement;
this.#camera.aspect = canvas.clientWidth / canvas.clientHeight;
this.#camera.updateProjectionMatrix();
// This is for resizing the render area and fit the resolution
requestAnimationFrame(this.update);
this.#controls.update();
this.#renderer.render(this.#scene, this.#camera);
}
main() {
this.startup();
this.addLights();
this.loadGeometries();
requestAnimationFrame(this.update);
}
}
And in another script I can just do let rc = new RenderCanvas(); and rc.main();.
But in the update method, on line const canvas = this.#renderer.domElement;, I received a console error saying Uncaught TypeError: Cannot read properties of undefined (reading '#renderer').
I thought that by the time update gets called, the renderer is already initiated. But the error seems to indicate otherwise. Am I misunderstanding the execution order? Or is there something about scope in Javascript at play here?
>Solution :
the issue seems to be with the update function you have .. you should do it like on of those two ways :
bind:
update(time) {
const canvas = this.#renderer.domElement;
this.#camera.aspect = canvas.clientWidth / canvas.clientHeight;
this.#camera.updateProjectionMatrix();
requestAnimationFrame(this.update.bind(this)); // Bind the context here
this.#controls.update();
this.#renderer.render(this.#scene, this.#camera);
}
OR – using arrow function:
update = (time) => {
const canvas = this.#renderer.domElement;
this.#camera.aspect = canvas.clientWidth / canvas.clientHeight;
this.#camera.updateProjectionMatrix();
requestAnimationFrame(this.update); // Context is automatically preserved
this.#controls.update();
this.#renderer.render(this.#scene, this.#camera);
}