0

I have the following code, where I would like to replace all main references in animate to this references, to avoid using a global variable inside the Main class:

class Main {

    constructor(idDiv) {
        this.div = document.getElementById(idDiv);
        this.sceneManager = new SceneManager(this.div);
    }


    animate() {
        console.log('Main::animate::this', this);
        console.log('Main::animate::main', main);
        requestAnimationFrame(main.animate);
        main.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);
        console.log('Main::execute::main', main);
        this.animate(main.sceneManager);

    }
}


const main = new Main('original');
main.execute();

Currently the previous code creates an animation loop which uses THREEjs animate to render a scene.

In addition, currently the logs are:

In execute:

Main::execute::this Main {div: div#original.column, sceneManager: SceneManager}div: div#original.columnsceneManager: SceneManager {div: div#original.column, camera: PerspectiveCamera, scene: Scene, controls: T…E.TrackballControls, renderer: WebGLRenderer}__proto__: Object


Main::execute::main Main {div: div#original.column, sceneManager: SceneManager}

In animate, first call:

Main::animate::this Main {div: div#original.column, sceneManager: SceneManager}

Main.js:16 Main::animate::main Main {div: div#original.column, sceneManager: SceneManager}

In animate, others calls:

Main::animate::this null

Main.js:16 Main::animate::main Main {div: div#original.column, sceneManager: SceneManager}

So as we see in the first call both this and main are the same, in both execute and animate functions. Then in the others calls, the this in animate is null and the main stills being the instance of the Main class. I assumme it is because of the line:

requestAnimationFrame(main.animate);

Where we are losing the this.

In addittion, to replace the main references I tried:

class Main {

    constructor(idDiv) {
        this.div = document.getElementById(idDiv);
        this.sceneManager = new SceneManager(this.div);
    }


    animate(main) {
        console.log('Main::animate::this', this);
        console.log('Main::animate::main', main);
        requestAnimationFrame(main.animate);
        main.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);
        console.log('Main::execute::main', main);
        this.animate(this);

    }
}


const main = new Main('original');
main.execute();

To change the parameter being passed in to animate to reference the this, the current Main instance.

Its results are the following:

First call:

Main::execute::this Main {div: div#original.column, sceneManager: SceneManager}
Main.js:34 Main::execute::main Main {div: div#original.column, sceneManager: SceneManager}
Main.js:15 Main::animate::this Main {div: div#original.column, sceneManager: SceneManager}
Main.js:16 Main::animate::main Main {div: div#original.column, sceneManager: SceneManager}

Second call:

Main::animate::this null
Main.js:16 Main::animate::main 697.568

Main.js:17 Uncaught TypeError: Failed to execute 'requestAnimationFrame' on 'Window': The callback provided as parameter 1 is not a function.
    at animate (Main.js:17)

I understood that is is because of in the line:

    requestAnimationFrame(main.animate);

We are not passing in our main again so I did:

...
        requestAnimationFrame(main.animate(main));
...

However it generates an infinite loop where we cannot see the scene anymore. Also logs are:

Main::execute::this Main {div: div#original.column, sceneManager: SceneManager}
Main.js:34 Main::execute::main Main {div: div#original.column, sceneManager: SceneManager}
Main.js:15 Main::animate::this Main {div: div#original.column, sceneManager: SceneManager}
Main.js:16 Main::animate::main Main {div: div#original.column, sceneManager: SceneManager}
Main.js:15 Main::animate::this Main {div: div#original.column, sceneManager: SceneManager}
Main.js:16 Main::animate::main Main {div: div#original.column, sceneManager: SceneManager}
Main.js:15 Main::animate::this Main {div: div#original.column, sceneManager: SceneManager}
Main.js:16 Main::animate::main Main {div: div#original.column, sceneManager: SceneManager}
Main.js:15 Main::animate::this Main {div: div#original.column, sceneManager: SceneManager}
Main.js:16 Main::animate::main Main {div: div#original.column, sceneManager: SceneManager}

I have also read:

ES6 Class: access to 'this' with 'addEventListener' applied on method

How to call .render and .animate functions inside object class?

How to create closure (protect globals) in an animation loop?

What am I doing wrong?

Could you help me please?

Yone
  • 2,064
  • 5
  • 25
  • 56
  • 1
    Possible duplicate of [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – Andreas May 31 '18 at 07:23

1 Answers1

2

You can bind your animation function such that references to this will always reference your object, rather than requestAnimationFrame's default window context.

Here's a simple example:

class Test {
  constructor() {
    this.name = "Window doesn't have this name!"
    this.animate = this.animate.bind(this)
  }

  animate() {
    requestAnimationFrame(this.animate)
    console.log(this.name) // window's "name" property is usually empty or undefined
  }
}

let test = new Test()
test.animate() // look at the console
TheJim01
  • 8,411
  • 1
  • 30
  • 54