Aspect ratio not updated on maximize/minimize in resize event

Hi!

I’m trying to dynamically modify the size of my canvas and make the BabylonJS engine modify the aspect ratio in the process. In a regular window resize (dragging the sides of the window) the aspect ratio is updated to work fine with the current size of the canvas, but when I trigger a maximize/minimize the resize function does not modify the aspect ratio. My code:

export default ({antialias, engineOptions, adaptToDeviceRatio, sceneOptions, onSceneReady, onRender, ...rest}:BabylonjsProps & React.CanvasHTMLAttributes<HTMLCanvasElement>) => {
  const reactCanvas = useRef(null);
  const [canvasStyle, setCanvasStyle] = useState({
    width: '100%',
    height: '100%'
  });
  // set up basic engine and scene
  useEffect(() => {
    const { current: canvas } = reactCanvas;
    

    if (!canvas) return;

    const engine = new Engine(canvas, antialias, engineOptions, adaptToDeviceRatio);
    const scene = new Scene(engine, sceneOptions);
    if (scene.isReady()) {
      onSceneReady(scene);
    } else {
      scene.onReadyObservable.addOnce((scene) => onSceneReady(scene));
    }

    engine.runRenderLoop(() => {
      if (typeof onRender === "function") onRender(scene);
      scene.render();
    });

    
    const resize = () => {
      const canvasRect = document.getElementsByTagName('canvas')[0].getBoundingClientRect();
      const contentPos = document.getElementById('main-content').getBoundingClientRect();
      
      setCanvasStyle({
        width: contentPos.width + 'px',
        height: window.innerHeight - canvasRect.y + 'px'
      })
      scene.getEngine().resize();
    };

    if (window) {
      window.addEventListener("resize", resize);
    }

    return () => {
      scene.getEngine().dispose();

      if (window) {
        window.removeEventListener("resize", resize);
      }
    };
  }, [antialias, engineOptions, adaptToDeviceRatio, sceneOptions, onRender, onSceneReady]);
  return (
    <canvas ref={reactCanvas} {...rest} style={canvasStyle} className="p-5" />
  )
};

This is tested in Chrome and Edge, but doesn’t work for either browser. Does anyone have experience with this?

@brianzinn is the king of React :slight_smile:

1 Like

hi Kristian - welcome to the forum!

This question is not React specific, but I’ll try. It looks like you are taking the main-content (is this a parent div?) and canvas separately and switching from % to px… Additionally, your setState will trigger on the next “React” render, while you are triggering the Babylon Engine to resize (based on the size when the event triggers). I think those will be out of sync. If you are triggering a canvas resize outside of the window resize event then I would suggest connecting to a ResizeObserver on the canvas element itself and calling engine.resize(). You can copy the code from here:
babylonjs-hook/babylonjs-hook.tsx at master · brianzinn/babylonjs-hook (github.com)

I would also try to switch away from getElementsByTagName in case you ever have multiple canvases - you get get it directly from your useRef with reactCanvas.current, which is an HTMLCanvasElement.

I don’t see how your are using innerHeight could expect to maintain aspect ratio - you may have luck as well with FOV. If you are changing your canvas style like that then it will change the canvas aspect ratio - your width and height from different DOM elements. You may find this property useful:
Camera | Babylon.js Documentation (babylonjs.com)

You generally have better luck on the forum, if you can reproduce in the playground - it’s not 100% clear to me what the exact issue is. :smile:

2 Likes

Hi @brianzinn

Your code snippet within useEffect solved the issue for me, thanks! I guess the main difference was that ResizeObserver.

1 Like

glad you got it working - makes sense it wasn’t working since you were resizing the engine and then the element afterwards, the observer just catches the actual canvas resize :smile: