其他的答案都很悲伤。他们都在与浏览器抗争,而不是与之合作。
可以说是调整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; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>
示例 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.innerWidth 和window.innerHeight 从未在上面的代码中引用,但它适用于所有情况。
为什么不使用resize 事件?因为在某些情况下,即使画布改变大小,您也不会收到调整大小事件。例如,如果您正在制作一个 2 列编辑器,并且您可以在 2 列之间绘制分隔线。或者,如果您的画布根据附近的内容进行缩放。无论哪种情况,都不会有resize 事件。