requestAnimationFrame is being called only once
Asked Answered
B

2

16

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

}
Browbeat answered 18/4, 2017 at 7:32 Comment(1)
try this...when you call this.render(); put that in a setInterval(function(){that.render}, 50)?Sincerity
A
44

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));
Ambuscade answered 19/4, 2017 at 20:13 Comment(6)
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.Browbeat
@Browbeat does requestAnimationFrame(this.render.bind(this)); work?Ambuscade
@Browbeat Updated my answer, please mark as correct if you found it useful :).Ambuscade
why it is slowing down my machine? is it the correct way? or is there any way to cancel animationframes?Incretion
@Ambuscade 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.Rexfourd
@Rexfourd 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.Ambuscade
T
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();
Thalweg answered 25/2, 2021 at 6:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.