Properly fix resizing

This commit is contained in:
Tristan Daniël Maat 2022-08-13 01:07:15 +01:00
parent 81e7bd98ff
commit 034c2a2c3f
Signed by: tlater
GPG key ID: 49670FD774E43268
2 changed files with 85 additions and 11 deletions

View file

@ -19,6 +19,7 @@ class Renderer {
private lastFrameTime: number; private lastFrameTime: number;
private dTime: number; private dTime: number;
private nextAnimationFrame?: number;
private rotation: number; private rotation: number;
@ -53,13 +54,63 @@ class Renderer {
this.buffers = {}; this.buffers = {};
} }
resize() { resizeAndDraw(
this.canvas.width = this.canvas.parentElement!.clientWidth; gl: WebGL2RenderingContext,
this.canvas.height = this.canvas.parentElement!.clientHeight; 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() { initializeScene() {
this.resize(); if (this.canvas.parentElement === null) {
throw new Error("canvas was not added to page");
}
const gl = this.canvas.getContext("webgl2"); const gl = this.canvas.getContext("webgl2");
if (gl === null) { if (gl === null) {
@ -78,12 +129,30 @@ class Renderer {
.build(); .build();
this.initGL(gl, shader); this.initGL(gl, shader);
this.initMatrices(gl, shader); this.updateProjection(gl, shader);
this.initBuffers(gl); 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(); const projectionMatrix = mat4.create();
mat4.perspective( mat4.perspective(
projectionMatrix, projectionMatrix,
@ -264,9 +333,8 @@ class Renderer {
this.overlay.innerText = `${dTime}ms (${cpuTime}ms / ${gpuTime}ms)`; this.overlay.innerText = `${dTime}ms (${cpuTime}ms / ${gpuTime}ms)`;
} }
requestAnimationFrame((time) => { this.nextAnimationFrame = requestAnimationFrame((time) => {
this.dTime = time - this.lastFrameTime; this.updateTime(time);
this.lastFrameTime = time;
this.drawScene(gl, shader); this.drawScene(gl, shader);
}); });

View file

@ -101,7 +101,13 @@ function Visualizer({
ref={visualizer} ref={visualizer}
className="is-flex-grow-1 is-clipped is-relative" className="is-flex-grow-1 is-clipped is-relative"
> >
<canvas style={{ display: "block" }}></canvas> <canvas
style={{
display: "block",
position: "absolute",
boxSizing: "border-box",
}}
></canvas>
<span <span
className="is-bottom-left" className="is-bottom-left"
style={{ display: "relative" }} style={{ display: "relative" }}