Engine resize does't work in react project

I am develop babylonjs with react,and write a react class component for babylonjs scene,
It render correctly but engine.resize function does’t work!

import * as BABYLON from 'babylonjs';
import { GridMaterial } from 'babylonjs-materials';
import { Component } from 'react';
import { AuxiliaryEntity, AuxiliaryFrame, AuxiliaryLine } from './SunnyHouse/Core';
import { DragPolygonArcOperator, IOperator, SelectOperator } from './Operator'
import { UndoRedoManager } from './SunnyHouse/Utils';

import * as React from "react";
import { ConnectOperator } from './Operator/ConnectOperator';
import { DragExtendOperator } from './Operator/DragExtendOperator';
import { DragLineArcOperator } from './Operator/DragLineArcOperator';
import { DragMoveOperator } from './Operator/DragMoveOperator';
import { DrawPolygonOperator } from './Operator/DrawPolygonOperator';

export class DesignScene extends Component {
  canvas: HTMLCanvasElement;
  scene: BABYLON.Scene;
  engine: BABYLON.Engine;
  camera: BABYLON.ArcRotateCamera;
  lights: BABYLON.Light[];
  ground: BABYLON.Mesh;
  operators: IOperator[];

  public select: AuxiliaryEntity;
  public frame: AuxiliaryFrame;
  public undoRedo: UndoRedoManager;

  constructor(props) {
    super(props);
  }

  componentDidMount = () => {
    this.canvas = document.getElementById( 'SceneCanvas') as HTMLCanvasElement;
    this.engine = new BABYLON.Engine(this.canvas, true, { preserveDrawingBuffer: true, stencil: true });
    this.scene = new BABYLON.Scene(this.engine);
    this.camera = new BABYLON.ArcRotateCamera("camera1", Math.PI / 2, Math.PI / 4, 3, new BABYLON.Vector3(0, 5, 0), this.scene);
    this.camera.attachControl(this.canvas, true);

    this.camera.inertia = 0.5;
    this.camera.panningInertia = 0.5;
    this.camera.lowerRadiusLimit = 0;
    this.camera.upperRadiusLimit = 100;
    this.camera.wheelDeltaPercentage = 0.02;
    this.camera.speed = 50;
    this.camera.panningSensibility = 500;

    this.undoRedo = new UndoRedoManager("");

    this.operators = [];
    this.operators.push(DragMoveOperator.instance);
    this.operators.push(DragExtendOperator.instance);
    this.operators.push(DrawPolygonOperator.instance);
    this.operators.push(ConnectOperator.instance);
    this.operators.push(DragLineArcOperator.instance);
    this.operators.push(DragPolygonArcOperator.instance);
    this.operators.push(SelectOperator.instance);

    for (let i = 0; i < this.operators.length; ++i) {
      this.operators[i].designScene = this;
    }
    this.init();
  }

  private init() {
    window.addEventListener('resize', 
      () => {
         this.engine.resize();
         console.log('resize')
    }, false);
    this.engine.runRenderLoop(() => {
      this.scene.render();
    });

    let light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), this.scene);
    light.intensity = 1.0;

    let skybox = BABYLON.Mesh.CreateBox("skyBox", 5000.0, this.scene);
    let skyboxMaterial = new BABYLON.StandardMaterial("skyBox", this.scene);
    skyboxMaterial.backFaceCulling = false;
    skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("//www.babylonjs.com/assets/skybox/TropicalSunnyDay", this.scene);
    skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
    skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
    skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
    skyboxMaterial.disableLighting = true;
    skybox.material = skyboxMaterial;

    this.ground = BABYLON.GroundBuilder.CreateGround("ground", { width: 10000, height: 10000 }, this.scene);
    let gridMaterial = new GridMaterial("gridMaterial", this.scene);
    gridMaterial.majorUnitFrequency = 10;
    gridMaterial.minorUnitVisibility = 0.45;
    gridMaterial.gridRatio = 2;
    gridMaterial.backFaceCulling = false;
    gridMaterial.mainColor = new BABYLON.Color3(1, 1, 1);
    gridMaterial.lineColor = new BABYLON.Color3(1.0, 1.0, 1.0);
    gridMaterial.opacity = 0.98;
    this.ground.material = gridMaterial;
  }

  render() {
    return (
      <canvas
        id = 'SceneCanvas'
        style={{ width: window.innerWidth, height: window.innerHeight }}
      />
    );
  }
}

export default DesignScene;

I have search the same issue in stackoverflow but it has not any answer.

Hi. maybe try use method instead of function? and i dont known about private methods private init() maybe you must use simple method init() instead private init()?. maybe you loss context? and I see for the first time such use componentDidMount as function)) componentDidMount its react method https://reactjs.org/docs/react-component.html#componentdidmount )
and why you export this class twice as class and by default 1)export class DesignScene extends Component 2)export default DesignScene; ? im not very familiar with react but i think you going the wrong way;

Maybe this helps you?

Thanks for your suggestions, I’m not familiar with react either.

I use the React mainly because it can provide many excellent components and run my typescript code directly. In fact, I didn’t even know Babylonjs and React were compatible in a typescript project.
I search some doc of dev babylonjs with react, but it just mention a way for turning babylon class to react component in javascript, but I just want use babylonjs in an react project with typescript.

Export twice class DesignScene is my wrong typing, the fisrt export shouldn’t exist, thanks for your mention.

I have tried use method instead of function, but it didn’t change anything!

Your guide of the Primax Studio is help me a lot, but It seem can not sovles my problem.

It is worth mentioning that when I change the part of the canvas size setting, It brings diffrent effect,


//It in correct resolution but cannot listen resize event
 this.canvas.width = window.innerWidth;
 this.canvas.height = window.innerHeight;

// This can resize the canvas by css style, but got a wrong resolution,
// maybe get a wrong device px from css px! 
 this.canvas.style.width = '100%';
 this.canvas.style.height ='100%';

I have solved this problem, By reading this doc,
By comparing my project with this repository, I found two diffrence,

// I didn't write forceUpdate in my resize callback function
  onResizeWindow = () => {
    if (this.engine) {
      this.engine.resize();
      this.forceUpdate()
    }
  }
// give size in react component render function but not in the componentDidMount function

export default class DesignScene extends React.Component{

  canvas: HTMLCanvasElement;
...

...
componentDidMount () {

//fault
  this.canvas.width = window.innerWidth;
  this.canvas.height = window.innerHeight;

}
render(){

//right
  const opts: any = {};
  opts.width = window.innerWidth;
  opts.height = window.innerHeight;
  return(<canvas
         {...opts}
         ref={this.canvas = canvas}
  />)
}

I think the reason I need to do this is because the render behaviour of react Component is different than use HTML directly. Anyway, So far everything is normal.

1 Like