调整画布/ webgl的大小以适应屏幕宽度和高度

时间:2017-07-11 17:31:54

标签: javascript canvas responsive-design three.js

您好我正在尝试让我的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);

            }

提前谢谢

3 个答案:

答案 0 :(得分:2)

其他答案都非常难过。他们都在与浏览器作斗争,而不是与之合作。

可以说,调整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以及我们是否制作画布或者是三个.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>
&#13;
&#13;
&#13;

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

&#13;
&#13;
"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);
&#13;
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>
&#13;
&#13;
&#13;

示例3:内联画布

&#13;
&#13;
"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);
&#13;
body { font-size: x-large; }
.diagram { width: 150px; height: 150px; float: left; margin: 1em; }
canvas { width: 100%; height: 100%; }
&#13;
<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>
&#13;
&#13;
&#13;

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

&#13;
&#13;
"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);
&#13;
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%;
}
&#13;
<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>
&#13;
&#13;
&#13;

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

为什么不使用resize事件?因为在某些情况下,即使画布改变大小,您也不会获得调整大小事件。例如,如果您正在制作2列编辑器,则可以在2列之间绘制分隔符。或者,如果您的画布基于附近的内容进行缩放。在任何一种情况下都不会有resize事件。

答案 1 :(得分:1)

全页响应式画布

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

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

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

答案 2 :(得分:0)

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

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

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

以下是 working JSFiddle