简体   繁体   中英

Resizing canvas/webgl to fit screen width and heigh

Hi I am trying to get my canvas/webgl to fit the windows height and width to a 100% I have done this but if I resize the window from small to large it does not "scale/fit" the window anymore and remains small any idea's ? JSfiddle: http://jsfiddle.net/jyr6y3fx/

Code:

var  scene, renderer;
    var glitchPass;

            init();
            animate();


            function init() {

        renderer = new THREE.WebGLRenderer( { alpha: true } );  

                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                document.body.appendChild( renderer.domElement );

                //

                camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
                camera.position.z = 400;

                scene = new THREE.Scene();



  // postprocessing

                composer = new THREE.EffectComposer( renderer );
                composer.addPass( new THREE.RenderPass( scene, camera ) );

                glitchPass = new THREE.GlitchPass();
                glitchPass.renderToScreen = true;
                composer.addPass( glitchPass );

            }



            function animate() {

                requestAnimationFrame( animate );


                composer.render();

                //renderer.render(scene, camera);

            }

Thank you in advance

The other answers are all very sad. They are all fighting the browser instead of cooperating with it.

Arguably the best way to resize three.js use to code it so it just accepts whatever size the canvas is as set by CSS. That way, no matter how you use the canvas your code will work, no need to change it for different situations.

First off when setting the initial aspect ratio there's no reason to set it because we're going to set it in response to the size of the canvas being different so it's just a waste of code to set it twice

// There's no reason to set the aspect here because we're going
// to set on resize anyway
const camera = new THREE.PerspectiveCamera(70, 1, 1, 1000);

Then we need some code that will resize the canvas to match its display size

function resizeCanvasToDisplaySize(force) {
  const canvas = renderer.domElement;
  // look up the size the canvas is being displayed
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;

  // adjust displayBuffer size to match
  if (force || canvas.width !== width || canvas.height !== height) {
    // you must pass false here or three.js sadly fights the browser
    renderer.setSize(width, height, false);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();

    // update any render target sizes here
  }
}

Call this in your render loop and once at init time

function animate(time) {
  time *= 0.001;  // seconds

  resizeCanvasToDisplaySize();

  mesh.rotation.x = time * 0.5;
  mesh.rotation.y = time * 1;

  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

resizeCanvasToDisplaySize(true);
requestAnimationFrame(animate);

For fullscreen this is all the css needed

body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }

Here's 4 examples, the only difference between the examples is the CSS and whether we make the canvas or three.js makes the canvas. No other code changes.

Example 1: fullscreen, We make the canvas

 "use strict"; const renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas")}); // There's no reason to set the aspect here because we're going // to set it every frame any const camera = new THREE.PerspectiveCamera(70, 1, 1, 1000); camera.position.z = 400; const scene = new THREE.Scene(); const geometry = new THREE.BoxGeometry(200, 200, 200); const material = new THREE.MeshPhongMaterial({ color: 0x555555, specular: 0xffffff, shininess: 50, shading: THREE.SmoothShading }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); const light1 = new THREE.PointLight(0xff80C0, 2, 0); light1.position.set(200, 100, 300); scene.add(light1); function resizeCanvasToDisplaySize(force) { const canvas = renderer.domElement; const width = canvas.clientWidth; const height = canvas.clientHeight; if (force || canvas.width !== width ||canvas.height !== height) { // you must pass false here or three.js sadly fights the browser renderer.setSize(width, height, false); camera.aspect = width / height; camera.updateProjectionMatrix(); // set render target sizes here } } function animate(time) { time *= 0.001; // seconds resizeCanvasToDisplaySize(); mesh.rotation.x = time * 0.5; mesh.rotation.y = time * 1; renderer.render(scene, camera); requestAnimationFrame(animate); } resizeCanvasToDisplaySize(true); requestAnimationFrame(animate);
 body { margin: 0; } canvas { width: 100vw; height: 100vh; display: block; }
 <canvas></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>

Example 2: fullscreen canvas, three.js makes the canvas

 "use strict"; const renderer = new THREE.WebGLRenderer(); document.body.appendChild(renderer.domElement); // There's no reason to set the aspect here because we're going // to set it every frame any const camera = new THREE.PerspectiveCamera(70, 1, 1, 1000); camera.position.z = 400; const scene = new THREE.Scene(); const geometry = new THREE.BoxGeometry(200, 200, 200); const material = new THREE.MeshPhongMaterial({ color: 0x555555, specular: 0xffffff, shininess: 50, shading: THREE.SmoothShading }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); const light1 = new THREE.PointLight(0xff80C0, 2, 0); light1.position.set(200, 100, 300); scene.add(light1); function resizeCanvasToDisplaySize(force) { const canvas = renderer.domElement; const width = canvas.clientWidth; const height = canvas.clientHeight; if (force || canvas.width !== width ||canvas.height !== height) { // you must pass false here or three.js sadly fights the browser renderer.setSize(width, height, false); camera.aspect = width / height; camera.updateProjectionMatrix(); // set render target sizes here } } function animate(time) { time *= 0.001; // seconds resizeCanvasToDisplaySize(); mesh.rotation.x = time * 0.5; mesh.rotation.y = time * 1; renderer.render(scene, camera); requestAnimationFrame(animate); } resizeCanvasToDisplaySize(true); requestAnimationFrame(animate);
 body { margin: 0; } canvas { width: 100vw; height: 100vh; display: block; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>

Example 3: inline canvas

 "use strict"; const renderer = new THREE.WebGLRenderer({canvas: document.querySelector(".diagram canvas")}); // There's no reason to set the aspect here because we're going // to set it every frame any const camera = new THREE.PerspectiveCamera(70, 1, 1, 1000); camera.position.z = 400; const scene = new THREE.Scene(); const geometry = new THREE.BoxGeometry(200, 200, 200); const material = new THREE.MeshPhongMaterial({ color: 0x555555, specular: 0xffffff, shininess: 50, shading: THREE.SmoothShading }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); const light1 = new THREE.PointLight(0xff80C0, 2, 0); light1.position.set(200, 100, 300); scene.add(light1); function resizeCanvasToDisplaySize(force) { const canvas = renderer.domElement; const width = canvas.clientWidth; const height = canvas.clientHeight; if (force || canvas.width !== width ||canvas.height !== height) { // you must pass false here or three.js sadly fights the browser renderer.setSize(width, height, false); camera.aspect = width / height; camera.updateProjectionMatrix(); // set render target sizes here } } function animate(time) { time *= 0.001; // seconds resizeCanvasToDisplaySize(); mesh.rotation.x = time * 0.5; mesh.rotation.y = time * 1; renderer.render(scene, camera); requestAnimationFrame(animate); } resizeCanvasToDisplaySize(true); requestAnimationFrame(animate);
 body { font-size: x-large; } .diagram { width: 150px; height: 150px; float: left; margin: 1em; } canvas { width: 100%; height: 100%; }
 <p> Pretend this is a diagram in a physics lesson and it's inline. Notice we didn't have to change the code to handle this case. <span class="diagram"><canvas></canvas></span> The same code that handles fullscreen handles this case as well. The only difference is the CSS and how we look up the canvas. Otherwise it just works. We didnt't have to change the code because we cooperated with the browser instead of fighting it. </p> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>

Example 4: 50% width canvas (like a live editor)

 "use strict"; const renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas")}); // There's no reason to set the aspect here because we're going // to set it every frame any const camera = new THREE.PerspectiveCamera(70, 1, 1, 1000); camera.position.z = 400; const scene = new THREE.Scene(); const geometry = new THREE.BoxGeometry(200, 200, 200); const material = new THREE.MeshPhongMaterial({ color: 0x555555, specular: 0xffffff, shininess: 50, shading: THREE.SmoothShading }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); const light1 = new THREE.PointLight(0xff80C0, 2, 0); light1.position.set(200, 100, 300); scene.add(light1); function resizeCanvasToDisplaySize(force) { const canvas = renderer.domElement; const width = canvas.clientWidth; const height = canvas.clientHeight; if (force || canvas.width !== width ||canvas.height !== height) { // you must pass false here or three.js sadly fights the browser renderer.setSize(width, height, false); camera.aspect = width / height; camera.updateProjectionMatrix(); // set render target sizes here } } function animate(time) { time *= 0.001; // seconds resizeCanvasToDisplaySize(); mesh.rotation.x = time * 0.5; mesh.rotation.y = time * 1; renderer.render(scene, camera); requestAnimationFrame(animate); } requestAnimationFrame(animate);
 html { box-sizing: border-box; } *, *:before, *:after { box-sizing: inherit; } body { margin: 0; } .outer { } .frame { display: flex; width: 100vw; height: 100vh; } .frame>* { flex: 1 1 50%; } #editor { font-family: monospace; padding: .5em; background: #444; color: white; } canvas { width: 100%; height: 100%; }
 <div class="frame"> <div id="result"> <canvas></canvas> </div> <div id="editor"> explaintion of example on left or the code for it would go here </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>

notice window.innerWidth and window.innerHeight are never referenced in the code above and yet it works for all cases.

Why not use the resize event? Because there are situations where you'll get no resize event even though the canvas changes size. For example if you're making a 2 column editor and you can draw the divider between the 2 columns. Or if your canvas is scaling based on content near by. In either case there will be no resize event.

This is because, the renderer­ 's width and height are always being fixed, and are not changing when the browser window is resized.

To resolve this, you need to reset renderer­ 's width and height on window resize, which you could do like this ...

window.addEventListener('resize', function() {
   renderer.setSize(window.innerWidth, window.innerHeight);
});

Here is the working JSFiddle

Fullpage responsive canvas

The best way to resize in response to a window resize is in the animation loop rather than on the window resize event that can fire at a rates over the 60FPS of the DOMs refresh rate. This will make the resize more efficient especially if the window is being resized by a mouse.

You can set the canvas to be positioned absolutely

CSS

canvas { position : absolute; top : 0px; left : 0px; }

Code

function animate(){
    if(renderer.domElement.width !== innerWidth || renderer.domElement.height !== innerHeight) {
        // last arg {false} stops Three from setting canvas.style width and height properties
        renderer.setSize(innerWidth,innerHeight,false); 
        camera.aspect = innerWidth / innerHeight;
        camera.updateProjectionMatrix();

    }

    // render your scene

    // next frame
    requestAnimationFrame(animate);
}

I had similar problems with resizing, eg due to orientation change of device and followed all the basic instructions which are most often like in https://jsfiddle.net/Q4Jpu/

But what solved it finally was to remove the initial

renderer.setSize(window.innerWidth, window.innerHeight)

before appending the canvas element in container.appendChild(renderer.domElement) , which caused the canvas element to have a fixed style attribute with this initial height and width, which never changed on resize.

I now call the onWindowResize() function after all other initialization is done beside having it listening to the resize event.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM