【问题标题】:Resizing canvas/webgl to fit screen width and heigh调整画布/webgl 的大小以适应屏幕宽度和高度
【发布时间】:2017-12-15 21:45:14
【问题描述】:

嗨,我正在尝试让我的 canvas/webgl 将窗口的高度和宽度调整为 100% 我已经这样做了,但是如果我将窗口的大小从小调整为大,它就不再“缩放/适合”窗口并且仍然很小有什么想法吗? JSfiddle:http://jsfiddle.net/jyr6y3fx/

代码:

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);

            }

提前谢谢你

【问题讨论】:

    标签: javascript canvas responsive-design three.js


    【解决方案1】:

    其他的答案都很悲伤。他们都在与浏览器抗争,而不是与之合作。

    可以说是调整three.js 大小的最佳方式,用于对其进行编码,因此它只接受CSS 设置的画布大小。这样,无论您如何使用画布,您的代码都可以正常工作,无需针对不同情况进行更改。

    首先在设置初始纵横比时没有理由设置它,因为我们要设置它以响应画布的大小不同,所以设置两次只是浪费代码

    // 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);
    

    然后我们需要一些代码来调整画布的大小以匹配其显示大小

    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
      }
    }
    

    在您的渲染循环中调用它,并在初始化时调用一次

    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);
    

    对于全屏,这就是所有需要的 css

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

    这里有 4 个示例,示例之间的唯一区别是 CSS 以及是我们制作画布还是 three.js 制作画布。没有其他代码更改。

    示例 1:全屏,我们制作画布

    "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>

    示例2:全屏画布,three.js制作画布

    "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; }
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"&gt;&lt;/script&gt;

    示例 3:内联画布

    "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>

    示例 4:50% 宽度的画布(如实时编辑器)

    "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>

    注意window.innerWidthwindow.innerHeight 从未在上面的代码中引用,但它适用于所有情况。

    为什么不使用resize 事件?因为在某些情况下,即使画布改变大小,您也不会收到调整大小事件。例如,如果您正在制作一个 2 列编辑器,并且您可以在 2 列之间绘制分隔线。或者,如果您的画布根据附近的内容进行缩放。无论哪种情况,都不会有resize 事件。

    【讨论】:

    • 为什么要给resizeCanvasToDisplaySize打两次电话?一次正常,一次与force
    【解决方案2】:

    这是因为,渲染器宽度和高度始终是固定的,不会在调整浏览器窗口大小时发生变化。

    要解决此问题,您需要在调整窗口大小时重置渲染器宽度和高度,您可以这样做...

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

    这里是working JSFiddle

    【讨论】:

    • 欢迎!
    • JSFiddle 链接已失效。
    【解决方案3】:

    整页响应式画布

    响应窗口调整大小调整大小的最佳方式是在动画循环中,而不是在窗口resize 事件上,该事件可以以超过 DOM 刷新率的 60FPS 的速率触发。这将使调整大小更有效,尤其是在通过鼠标调整窗口大小时。

    您可以将画布设置为绝对定位

    CSS

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

    代码

    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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-08
      相关资源
      最近更新 更多