Three.js "Uncaught TypeError: Cannot read property 'render' of undefined" error with object literal

ozgrozer picture ozgrozer · Sep 9, 2017 · Viewed 7.2k times · Source

I'm trying to convert my codes to object literal style. I can create the scene but I got problems with animation.

In this line I'm getting the "Uncaught TypeError: Cannot read property 'render' of undefined" error.

this.renderer.render(this.scene, this.camera);

This is my object:

var three = {
    objects: function() {
        /*objects*/
    },
    createScene: function() {
        this.container = document.getElementById("container");

        this.scene = new THREE.Scene();

        this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.001, (26 * 10));
        this.camera.position.set(26, (26 / 2), 26);

        window.addEventListener("resize", function() {
            this.camera.aspect = window.innerWidth / window.innerHeight;
            this.camera.updateProjectionMatrix();
            this.renderer.setSize(window.innerWidth, window.innerHeight);
        });

        this.objects();

        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.container.appendChild(this.renderer.domElement);

        this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
    },
    animate: function() {
        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.animate);
    },
    render: function() {
        this.createScene();
        this.animate();
    }
};

three.render();

Answer

Ivan Sivak picture Ivan Sivak · Sep 9, 2017

Check my modified example (I borrowed your objects function to render a cube.. just for fun of it:) )

Basically, you need to pass the context along with your animate method using the Function.prototype.bind

requestAnimationFrame(this.animate.bind(this));

..what happens behind the curtain is that the first call of this.renderer.render(this.scene, this.camera); happens just fine without issues because the context is passed along with this.animate(); method. However, the second time the animate is called, by requestAnimationFrame method, the context is not there. Hence you need to pass it manually.

var three = {
    objects: function() {
        /*objects*/
        var geometry = new THREE.BoxBufferGeometry( 3, 3, 3 );
				var material = new THREE.MeshBasicMaterial( { color: 0xffaa00 } );
				this.mesh = new THREE.Mesh( geometry, material );
        this.mesh.position.set(24, 14, 12);
				this.scene.add( this.mesh );
    },
    createScene: function() {
        this.container = document.getElementById("container");

        this.scene = new THREE.Scene();

        this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.001, (26 * 10));
        this.camera.position.set(26, (26 / 2), 26);

        window.addEventListener("resize", function() {
            this.camera.aspect = window.innerWidth / window.innerHeight;
            this.camera.updateProjectionMatrix();
            this.renderer.setSize(window.innerWidth, window.innerHeight);
        }.bind(this));

        this.objects();

        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.container.appendChild(this.renderer.domElement);
    },
    animate: function() {
        this.mesh.rotation.x += 0.005;
				this.mesh.rotation.y += 0.01;

        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.animate.bind(this));
    },
    render: function() {
        this.createScene();
        this.animate();
    }
};

three.render();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>

<div id="container"></div>