From 034c2a2c3f1196f806cf42ae7a85e5b5d1d90476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= Date: Sat, 13 Aug 2022 01:07:15 +0100 Subject: [PATCH] Properly fix resizing --- src/music/features/visualizer/Renderer.ts | 88 +++++++++++++++++--- src/music/features/visualizer/Visualizer.tsx | 8 +- 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/src/music/features/visualizer/Renderer.ts b/src/music/features/visualizer/Renderer.ts index f5648c9..3597b59 100644 --- a/src/music/features/visualizer/Renderer.ts +++ b/src/music/features/visualizer/Renderer.ts @@ -19,6 +19,7 @@ class Renderer { private lastFrameTime: number; private dTime: number; + private nextAnimationFrame?: number; private rotation: number; @@ -53,13 +54,63 @@ class Renderer { this.buffers = {}; } - resize() { - this.canvas.width = this.canvas.parentElement!.clientWidth; - this.canvas.height = this.canvas.parentElement!.clientHeight; + resizeAndDraw( + gl: WebGL2RenderingContext, + shader: Shader, + observerData: ResizeObserverEntry | null + ) { + if (this.canvas.parentElement === null) { + throw new Error("renderer has been removed from dom"); + } + + if (this.nextAnimationFrame) { + cancelAnimationFrame(this.nextAnimationFrame); + } + + // Note: For this to work, it's *incredibly important* for the + // canvas to be overflowable by its parent, and its parent to + // have `overflow: hidden` set. If using a flexbox, this means + // that the canvas has to be `position: absolute`. + let width: number; + let height: number; + + if (observerData !== null && observerData.devicePixelContentBoxSize) { + width = observerData.devicePixelContentBoxSize[0].inlineSize; + height = observerData.devicePixelContentBoxSize[0].blockSize; + } else { + // Fallback; the above API is even newer than + // ResizeObserver, and by setting the observerData to null + // we can manually resize at least once without going + // through the API. + if (this.canvas.parentElement === null) { + throw new Error("canvas parent disappeared"); + } + // Note: This *requires* `box-sizing: border-box` + ({ width, height } = + this.canvas.parentElement.getBoundingClientRect()); + } + + this.canvas.width = width; + this.canvas.height = height; + + gl.viewport(0, 0, this.canvas.clientWidth, this.canvas.clientHeight); + this.updateProjection(gl, shader); + + // ResizeObserver will call when we should draw, so do our own + // time calculation and draw the scene. + this.updateTime(performance.now()); + this.drawScene(gl, shader); + } + + updateTime(time: number) { + this.dTime = time - this.lastFrameTime; + this.lastFrameTime = time; } initializeScene() { - this.resize(); + if (this.canvas.parentElement === null) { + throw new Error("canvas was not added to page"); + } const gl = this.canvas.getContext("webgl2"); if (gl === null) { @@ -78,12 +129,30 @@ class Renderer { .build(); this.initGL(gl, shader); - this.initMatrices(gl, shader); + this.updateProjection(gl, shader); this.initBuffers(gl); - this.drawScene(gl, shader); + + try { + const observer = new ResizeObserver((elements) => { + // We only observe one element + const element = elements[0]; + this.resizeAndDraw(gl, shader, element); + }); + observer.observe(this.canvas.parentElement); + } catch (error) { + // If the browser does not support ResizeObserver, we + // simply don't resize. Resizing is hard enough, just use + // a modern browser. + if (error instanceof ReferenceError) { + console.warn( + "Browser does not support `ResizeObserver`. Canvas resizing will be disabled." + ); + } else throw error; + } + this.resizeAndDraw(gl, shader, null); } - initMatrices(gl: WebGLRenderingContext, shader: Shader) { + updateProjection(gl: WebGLRenderingContext, shader: Shader) { const projectionMatrix = mat4.create(); mat4.perspective( projectionMatrix, @@ -264,9 +333,8 @@ class Renderer { this.overlay.innerText = `${dTime}ms (${cpuTime}ms / ${gpuTime}ms)`; } - requestAnimationFrame((time) => { - this.dTime = time - this.lastFrameTime; - this.lastFrameTime = time; + this.nextAnimationFrame = requestAnimationFrame((time) => { + this.updateTime(time); this.drawScene(gl, shader); }); diff --git a/src/music/features/visualizer/Visualizer.tsx b/src/music/features/visualizer/Visualizer.tsx index c45228e..8081d6b 100644 --- a/src/music/features/visualizer/Visualizer.tsx +++ b/src/music/features/visualizer/Visualizer.tsx @@ -101,7 +101,13 @@ function Visualizer({ ref={visualizer} className="is-flex-grow-1 is-clipped is-relative" > - +