Search code examples
angularthree.jsionic2requestanimationframe

requestAnimationFrame is being called only once


I'm trying to achieve a pretty basic animation using ThreeJS in my Ionic 2 application. Basically trying to rotate a cube. But the cube isn't rotating because requestAnimationFrame is being executed only once inside the render loop.

I'm able to see only this. enter image description here

No rotating animation. I'm sharing my code below.

home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>
  <div #webgloutput></div>
</ion-content>

home.ts

import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavController } from 'ionic-angular';

import * as THREE from 'three';


@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  @ViewChild('webgloutput') webgloutput: ElementRef;


  private renderer: any;
  private scene: any;
  private camera: any;
  private cube: any;

  constructor(public navCtrl: NavController) {
  }

  ngOnInit() {
    this.initThree();
  }

  initThree() {
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

    this.renderer = new THREE.WebGLRenderer();
    this.renderer.setSize( window.innerWidth, window.innerHeight );
    this.webgloutput.nativeElement.appendChild(this.renderer.domElement);

    let geometry = new THREE.BoxGeometry(1, 1, 1);

    let material = new THREE.MeshBasicMaterial({ color: 0x00ff00});
    this.cube = new THREE.Mesh(geometry, material);
    this.scene.add(this.cube);
    this.camera.position.z = 5;

    this.render();
  }


  render() {
    console.log("render called");
    requestAnimationFrame(() => this.render);

    this.cube.rotation.x += 0.5;
    this.cube.rotation.y += 0.5;
    this.renderer.render(this.scene, this.camera);
  }

}

Solution

  • The problem is that you are not calling your requestAnimationFrame correctly. You are not passing it the render function directly, but instead an arrow function that returns the render function.

    Change the line requestAnimationFrame(() => this.render); to requestAnimationFrame(this.render);

    However, when using class methods as callbacks (as an argument) it will no longer be called with the same this reference. Read more about this on MDN. In your case, "this" in the render method will be become undefined when called within requestAnimationFrame leading to the error message TypeError: Cannot read property 'render' of undefined. There are multiple ways to avoid this behaviour, but the simplest in my opinion is to explicitly bind the render method to the correct this:

    requestAnimationFrame(this.render.bind(this));