Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resize viewport/canvas according to browser window size

I want my webgl app viewport and canvas to react to browser window size changes (that includes F11 button). I.e. I want the canvas to be always 100% width and height and I want the viewport size and aspect ratio to be appropriate. The former I achieve with simple:

<canvas style="width: 100%; height:100%;">

although I'm not sure if that's the way to go. The latter I was trying to achieve with some information I found here: Webgl gl.viewport change but obviously I'm doing something wrong. I do realize:

canvas.onresize = resizeViewport;

is wrong as canvas's size doesn't change very often (it's always 100% width and height) but I don't know how I should do it.

Here's an online version of the app: http://nps.netarteria.pl/gallery.

like image 776
NPS Avatar asked Dec 13 '12 23:12

NPS


People also ask

How do I resize a canvas to resize a window?

Resizing the canvas on the fly is quite easy. To do it, simply set the width and height properties of the Canvas object, and then redraw the canvas contents: Canvas . width = 600 ; Canvas .

How do you make a resizable canvas in HTML?

Resize it using CSS every time you resize with canvas. width, canvas. height the canvas is fully cleared. if you're building a drawing app you have probably defined a function to redraw the canvas every time a change is applied so use the same function to redraw the canvas after it changed.


1 Answers

I think this works:

var canvas;
var gl;

function render() {
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    
  // Draw a 1 pixel border around the edge using 
  // the scissor test since it's easier than setting up
  // a lot of stuff
  gl.clearColor(1, 0, 0, 1);  // red
  gl.disable(gl.SCISSOR_TEST);
  gl.clear(gl.COLOR_BUFFER_BIT);
    
  gl.enable(gl.SCISSOR_TEST);
  gl.scissor(1, 1, gl.canvas.width - 2, gl.canvas.height - 2);
  gl.clearColor(0, 0, 1, 1);  // blue
  gl.clear(gl.COLOR_BUFFER_BIT);
};

function resizeCanvas() {
  var width = canvas.clientWidth;
  var height = canvas.clientHeight;
  if (canvas.width != width ||
      canvas.height != height) {
    canvas.width = width;
    canvas.height = height;
          
    // in this case just render when the window is resized.
    render();
  }
}

function main() {
  canvas = document.getElementById("c");
  gl = canvas.getContext("webgl");
  resizeCanvas();
}

window.addEventListener('resize', resizeCanvas);
main();
body { 
  margin: 0;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
<canvas id="c"></canvas>

As far as I know, resize only works on the window. The HTML5 spec has unfortunately not added resize events to other elements.

Note: If you are always rendering (like a game for example) then you don't need to listen for the resize event. Just call resizeCanvas at the beginning of your render loop. If the browser has resized the canvas, regardless of its container, the code will see the size no longer matches and update the size of the canvas's drawingbuffer.

The proper viewport size for rendering the the canvas is almost always:

// Set the viewport to be the size of the canvas's drawingBuffer.

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

Whereas if you are using a typical 3D math library, you also have a projection matrix and a function—usually called perspective which takes a fieidOfView, aspect, zNear and zFar parameters. The correct aspect for nearly all WebGL programs is:

// Set the aspect of our perspective matrix to match the size
// the canvas is displayed at.

var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight
???.perspective(fieldOfView, aspect, zNear, zFar);

Update

Apparently you need to cache the clientWidth and clientHeight. The reason is setting canvas.width will change the CSS layout which can change clientHeight

Updated the code above.

Update2

Switched from using 100% to 100vw and 100vw as these are arguably more correct. They describe what you want which is to make the canvas the size of the viewport where as the previous solution made the canvas the size of the document and then tried for force the document to be the size of the window. Since the content of the document could potentially be larger than the window that was arguably the wrong solution.

like image 126
gman Avatar answered Sep 19 '22 10:09

gman