Javascript 基于容器的Threejs画布大小

Javascript 基于容器的Threejs画布大小,javascript,three.js,Javascript,Three.js,如何根据画布的容器计算画布大小?避免滚动 如果我根据窗口设置大小,画布就太大了。好吧,这并不难。设置渲染的大小就行了 container = document.getElementById('container'); renderer.setSize($(container).width(), $(container).height()); container.appendChild(renderer.domElement); 可以说,调整three.js大小的最佳方法是使用代码对其进行编码,

如何根据画布的容器计算画布大小?避免滚动


如果我根据窗口设置大小,画布就太大了。

好吧,这并不难。设置渲染的大小就行了

container = document.getElementById('container');
renderer.setSize($(container).width(), $(container).height());
container.appendChild(renderer.domElement);

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

首先,当设置初始纵横比时,没有理由设置它,因为我们将根据画布的大小不同来设置它,所以设置两次只会浪费代码

// 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);
然后我们需要一些代码来调整画布的大小以匹配其显示大小

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
  }
}
在渲染之前在渲染循环中调用此函数

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);
这里有3个示例,这3个示例之间的唯一区别是CSS,以及是我们制作画布还是三个.js制作画布

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

“严格使用”;
const renderer=new THREE.WebGLRenderer({canvas:document.querySelector(“canvas”)});
//没有理由在这里设置相位,因为我们要
//要在每帧设置它,我们将从2开始将其设置为2
//画布默认大小的外观(300w/150h=2)
常数照相机=新的三个透视照相机(70,2,1,1000);
摄像机位置z=400;
const scene=new THREE.scene();
const geometry=新的3.BoxGeometry(200200200200);
const material=新的3.0网格材质({
颜色:0x555555,
镜面反射:0xffffff,
光泽度:50,,
明暗处理:三点明暗处理
});
常量网格=新的三个网格(几何体、材质);
场景。添加(网格);
常量灯1=新的三点灯(0xff80C0,2,0);
灯1.位置。设置(200100300);
场景。添加(light1);
函数resizeCanvasToDisplaySize(){
const canvas=renderer.domeElement;
const width=canvas.clientWidth;
常数高度=canvas.clientHeight;
if(canvas.width!==宽度| | canvas.height!==高度){
//必须在此处传递false或在浏览器中传递three.js
设置大小(宽度、高度、假);
camera.aspect=宽度/高度;
camera.updateProjectMatrix();
//在此处设置渲染目标大小
}
}
函数动画(时间){
时间*=0.001;//秒
resizeCanvasToDisplaySize();
网格旋转.x=时间*0.5;
网格旋转y=时间*1;
渲染器。渲染(场景、摄影机);
请求动画帧(动画);
}
请求动画帧(动画)
body{margin:0;}
画布{宽度:100vw;高度:100vh;显示:块;}

我认为调整画布大小的最佳方法不是上面公认的答案。 每个animationframe@gman都将运行resizeCanvasToDisplaySize函数,进行多次计算

我认为最好的方法是创建一个窗口事件列表器“resize”和一个布尔值,说明用户是否已调整大小。在动画帧中,可以检查用户是否已调整大小。如果他调整了大小,您可以在animationframe中调整画布的大小

let resized = false

// resize event listener
window.addEventListener('resize', function() {
    resized = true
})

function animate(time) {
    time *= 0.001

    if (resized) resize()

    // rotate the cube
    cube.rotation.x += 0.01
    cube.rotation.y += 0.01

    // render the view
    renderer.render(scene, camera)

    // animate
    requestAnimationFrame(animate)
}

function resize() {
    resized = false

    // update the size
    renderer.setSize(window.innerWidth, window.innerHeight)

    // update the camera
    const canvas = renderer.domElement
    camera.aspect = canvas.clientWidth/canvas.clientHeight
    camera.updateProjectionMatrix()
}

也许我忽略了这一点——但为什么现有的建议涉及从动画循环中调整画布的大小

您希望动画循环尽可能少地执行,因为它在理想情况下每秒重复30多次,并且应该进行优化以尽可能高效地运行-为运行它的最慢系统提供最大fps

我认为从resize事件监听器中调用resize函数没有什么坏处,就像下面建议的Meindert Stijfhals一样

大概是这样的:

var container = renderer.domElement.parentElement;
container.addEventListener('resize', onContainerResize);

function onContainerResize() {
    var box = container.getBoundingClientRect();
    renderer.setSize(box.width, box.height);

    camera.aspect = box.width/box.height
    camera.updateProjectionMatrix()
    // optional animate/renderloop call put here for render-on-changes
}

如果设置了某种“仅在更改时渲染”,则可以在“调整大小”函数末尾调用“渲染”函数。否则下次渲染循环触发时,它应该使用新设置进行渲染。

当然,@gman提供的答案是完整的答案。然而,为了重述可能的情景,我们必须假设这两种可能性,这就是答案截然不同的原因

第一种可能是当容器是全局(head)对象时:
window
,或者
或者
,在这种情况下,最方便的方法是使用
window.addEventListener('resize',onResize()){…}


第二种可能性是最初提出的问题:Three.js WebGL输出的容器何时应该响应。在这种情况下,无论容器是canvas、section还是div,处理大小调整的最佳方法是在DOM中以容器为目标,使用
renderer.setsize()
方法,将容器的
clientWidth
clientHeight
传递给它,然后调用
onResize()
渲染循环函数中的函数,@gman前面提到了它的完整实现。在这种特殊情况下,无需使用
addEventListener

以下是允许我获得画布渲染大小句柄的代码:

首先,场景在具有您选择的尺寸的
中渲染:

<div class="relative h-512 w-512">
    <div id="your_scene"></div>
</div>
然后,获取它的尺寸:

this.mount = document.querySelector('#your_scene');
this.width = document.querySelector('#your_scene').offsetWidth;
this.height = document.querySelector('#your_scene').offsetHeight;
然后,您(在我的例子中)将渲染器背景设置为透明,设置像素比率,然后将容器的大小应用于渲染器。Three.js的工作原理是将渲染器
元素附加到
场景
容器中,因此这里的最后一步是
appendChild

this.renderer = new THREE.WebGLRenderer({ alpha: true });
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(this.width, this.height);
this.mount.appendChild(this.renderer.domElement);
希望这足以让有人在他们的现场进行法医鉴定。工作最多的部分是
setSize
调用,但您需要有正确的尺寸,此外,您的相机必须指向正确的位置


如果您的代码与我的代码类似,但仍然不起作用,请查看您的相机透视图,并确保您的场景对象确实在视图中。

我不确定上面的评论是否对每个人都是显而易见的
this.renderer = new THREE.WebGLRenderer({ alpha: true });
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(this.width, this.height);
this.mount.appendChild(this.renderer.domElement);
camera.aspect = gifWidth / gifHeight;
camera.updateProjectionMatrix();
var scale = window.devicePixelRatio;
renderer.setSize( gifWidth / scale, gifHeight / scale, true );