gpt4 book ai didi

javascript - Threejs Canvas 大小基于容器

转载 作者:数据小太阳 更新时间:2023-10-29 04:54:34 26 4
gpt4 key购买 nike

如何根据容器计算 Canvas 大小?避免滚动。

如果我根据窗口设置大小,则 Canvas 太大。

最佳答案

可以说,调整three.js 大小的最佳方法是对其进行编码,因此它只接受CSS 设置的 Canvas 大小。这样,无论您如何使用 Canvas ,您的代码都可以正常工作,无需针对不同情况进行更改。
首先,在设置初始纵横比时,没有理由设置它,因为我们将根据 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);
然后我们需要一些代码来调整 Canvas 的大小以匹配它的显示大小
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 以及我们制作 Canvas 还是 three.js 制作 Canvas
示例 1:全屏,我们制作 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; }
<canvas></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>

示例2:全屏 Canvas ,three.js制作 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; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>

示例 3:内联 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%; }
<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 didn'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% 宽度的 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);
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上面的代码中从未引用过,但它适用于所有情况。

更新:2021 年,没有“最好”的方式,只有多种方式需要权衡取舍。
有些人似乎关心性能,好像检查 2 个值是否不匹配 2 个其他值在任何需要性能的程序中都有任何可衡量的性能影响🙄
无论如何,我在上面发布的旧答案只是检查容器的大小。人们建议使用 window.addEventListener('resize', ...)但没有注意到它不处理 Canvas 改变大小但窗口本身不改变的情况(上面的示例 4)。
无论如何,在 2021 年我们可以使用 ResizeObserver而不是 window.addEventListener('resize', ...)对于那些认为每帧检查 2 个变量是性能开销的人。 ResizeObserver当 Canvas 改变大小时会触发,即使窗口本身没有改变大小。
function resizeCanvasToDisplaySize() {
const canvas = renderer.domElement;
// look up the size the canvas is being displayed
const width = canvas.clientWidth;
const height = canvas.clientHeight;

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

const resizeObserver = new ResizeObserver(resizeCanvasToDisplaySize);
resizeObserver.observe(canvas, {box: 'content-box'});
示例 1:全屏,我们制作 Canvas

body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/build/three.module.js';

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;

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

mesh.rotation.x = time * 0.5;
mesh.rotation.y = time * 1;

renderer.render(scene, camera);
requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

const resizeObserver = new ResizeObserver(resizeCanvasToDisplaySize);
resizeObserver.observe(renderer.domElement, {box: 'content-box'});
</script>

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

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%;
}
#ui {
position: absolute;
right: 0;
bottom: 0;
padding: 0.5em;
}
#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">
<p>no window resize</p>
<p>For example level editor might have the level
displayed on the left and a control panel on the right.</p>
<p>Click the buttons below. The only thing changing is CSS. The window size is not changing so using <code>window.addEventListener('resize', ...)</code> would fail</p>
<div id="ui">
<button type="button" data-size="25%">small</button>
<button type="button" data-size="50%">medium</button>
<button type="button" data-size="75%">large</button>
</div>
</div>
</div>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/build/three.module.js';

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;

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

mesh.rotation.x = time * 0.5;
mesh.rotation.y = time * 1;

renderer.render(scene, camera);
requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

const editorElem = document.querySelector("#editor");
document.querySelectorAll('button').forEach(elem => {
elem.addEventListener('click', () => {
editorElem.style.flexBasis = elem.dataset.size;
});
});


const resizeObserver = new ResizeObserver(resizeCanvasToDisplaySize);
resizeObserver.observe(renderer.domElement, {box: 'content-box'});
</script>

为什么不使用 ResizeObserver ?
  • 如果您仍然需要支持它,它在旧浏览器(如 IE)中不可用
  • 不能保证尽快更新。
    换句话说,使用 rAF 并检查每一帧,理想情况下,您的内容将始终保持正确的大小,因为浏览器要求您准备内容。相反,ResizeObserver只是说它最终会给你一个事件。在您的内容实际需要调整大小之后,这可能会出现 5-10 帧。

  • 更多的是处理 devicePixelRatio。对于这个答案来说,这个话题太大了,但你可以 see more here

    关于javascript - Threejs Canvas 大小基于容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29884485/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com