15

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);
  }

}
somnathbm
  • 649
  • 1
  • 9
  • 19

2 Answers2

38

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

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

Edit:

When using ES2015 classes like you are, it is important to remember that class methods are functions that are declared as object properties. The context (this) will be the object that the method is attached to. So when passing the method to the requestAnimationFrame(...) method, it will no longer be called with the same object reference. Because of this, we need to bind the context of the render method before passing it to the requestAnimationFrame(...):

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

This is expained well in this blog post. (don't mind that it is focused on React, the principles and examples are ES2015 specific).

micnil
  • 4,705
  • 2
  • 28
  • 39
  • Yes I have tried this on the first place. But doing so compiler throws this error **TypeError: Cannot read property 'render' of undefined**. It cannot identify that render is the method. Possibly here somehow **this** context is screwed up. – somnathbm Apr 19 '17 at 20:32
  • 3
    @somnathbm does ```requestAnimationFrame(this.render.bind(this));``` work? – micnil Apr 19 '17 at 20:54
  • @somnathbm Updated my answer, please mark as correct if you found it useful :). – micnil Apr 19 '17 at 21:21
  • why it is slowing down my machine? is it the correct way? or is there any way to cancel animationframes? – Umesh Patadiya Feb 05 '19 at 08:28
  • @micnil second part helped. I read the blog post you are linking, but after reading it I feel more confused than before. Can I use other classes than ES2015 so the js is going to behave properly? Because current behavior in comparison with other programming languages, doesnt make sense to me. – suchoss Oct 04 '19 at 20:32
  • 1
    @suchoss I think by now I should rephrase this to just say "classes". There is no other variant. I understand your confusion coming from other languages, it is not the same thing. In other languages the class methods always belong to the created instance, `this` will always be the same. In JavaScript the method is shared among all instances. `this` will be whatever the method is attached to when called. Before ES2015 declaring classes was a lot more verbose, but it was the same thing. I recommend reading more about JS classes and the prototype chain if you are to continue developing JS. – micnil Oct 15 '19 at 20:53
1

The purpose of requestAnimationFrame is not to do animations, its purpose is to call the function provided on the next frame so basically we are calling the same function on each frame.

requestAnimationFrame(justNameofFun);

i.e.

const clock = new THREE.Clock();
const tick = () => {
  const elapsedTime = clock.getElapsedTime()
  cube1.rotation.y = elapsedTime * Math.PI * 1;
  renderer.render(scene, camera);
  window.requestAnimationFrame(tick);
};

// Call at least once to perform requestanimationframe

tick();
Dpk
  • 567
  • 5
  • 16