Threejs canvas size based on container

前端 未结 6 426
借酒劲吻你
借酒劲吻你 2021-02-01 20:58

How can I calculate canvas size based on its container? To avoid scrolling.

If I set the size based on window the canvas is too big.

6条回答
  •  离开以前
    2021-02-01 21:21

    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 it every frame anyway so we'll set it to 2 since 2
    // is the the aspect for the canvas default size (300w/150h = 2)
    const camera = new THREE.PerspectiveCamera(70, 2, 1, 1000);
    

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

    function resizeCanvasToDisplaySize() {
      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 (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 before rendering

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

    Here's 3 examples, the only difference between the 3 examples is the CSS and whether we make the canvas or three.js makes the canvas

    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 anyway so we'll set it to 2 since 2
    // is the the aspect for the canvas default size (300w/150h = 2)
    const  camera = new THREE.PerspectiveCamera(70, 2, 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() {
      const canvas = renderer.domElement;
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      if (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);
    body { margin: 0; }
    canvas { width: 100vw; height: 100vh; display: block; }
    
    

    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 anyway so we'll set it to 2 since 2
    // is the the aspect for the canvas default size (300w/150h = 2)
    const  camera = new THREE.PerspectiveCamera(70, 2, 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() {
      const canvas = renderer.domElement;
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      if (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);
    body { margin: 0; }
    canvas { width: 100vw; height: 100vh; display: block; }

    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 anyway so we'll set it to 2 since 2
    // is the the aspect for the canvas default size (300w/150h = 2)
    const  camera = new THREE.PerspectiveCamera(70, 2, 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() {
      const canvas = renderer.domElement;
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      if (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);
    body { font-size: x-large; }
    .diagram { width: 150px; height: 150px; float: left; margin: 1em; }
    canvas { width: 100%; height: 100%; }

    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. 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 didn't have to change the code because we cooperated with the browser instead of fighting it.

    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 anyway so we'll set it to 2 since 2
    // is the the aspect for the canvas default size (300w/150h = 2)
    const  camera = new THREE.PerspectiveCamera(70, 2, 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() {
      const canvas = renderer.domElement;
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      if (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%;
    }
    explaintion of example on left or the code for it would go here

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

提交回复
热议问题