响应式设计
响应式设计
让一个网页是响应式的通常是指让网页能在从桌面到平板再到手机不同尺寸的显示器上显示良好。
拉伸与块状化
一般来说,我们的响应式设计需要解决两个问题:
- 第一是我们的立方体被拉伸了。他们不是立方体了更像是个盒子太高或者太宽。在新标签中打开它然后改变尺寸你就能看到立方体是怎么在宽高上被拉伸的。
- 另一个问题是立方体看起来分辨率太低或者说块状化或者有点模糊,将窗口拉伸的非常大你就能看到问题。
我们先解决拉伸的问题。为此我们要将相机的 aspect 设置为 canvas 的宽高比。我们可以通过 canvas 的 clientWidth 和 clientHeight 属性来实现。
function render(time) {
time *= 0.001;
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
// ...
}
我们现在来解决块状化的问题,canvas 元素有两个尺寸。一个是 canvas 在页面上的显示尺寸,是我们用 CSS 来设置的。另一个尺寸是 canvas 本身像素的数量。这和图片一样。比如我们有一个 128x64 像素的图片然后我们可以通过 CSS 让它显示为 400x200 像素。
<img src="some128x64image.jpg" style="width:400px; height:200px" />
一个 canvas 的内部尺寸,它的分辨率,通常被叫做 drawingbuffer 尺寸。在 three.js 中我们可以通过调用 renderer.setSize 来设置 canvas 的 drawingbuffer,再一次,我们可以使用 canvas 的 clientWidth 和 clientHeight 属性。
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
一旦我们知道了是否需要调整大小我们就调用 renderer.setSize 然后 传入新的宽高。在末尾传入 false 很重要。render.setSize 默认会设置 canvas 的 CSS 尺寸但这并不是我们想要的。我们希望浏览器能继续工作就像其他使用 CSS 来定义尺寸的其他元素。我们不希望 three.js 使用 canvas 和其他元素不一样。注意如果我们的 canvas 大小被调整了那函数会返回 true。我们可以利用 这个来检查是否有其他的东西应该更新。我们修改渲染循环 来使用我们的新函数。
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
...
应对 HD-DPI 显示器
HD-DPI 代表每英寸高密度点显示器(视网膜显示器)。它指的是当今大多数的 Mac 和 windows 机器以及几乎所有的智能手机。浏览器中的工作方式是不管屏幕的分辨率有多高使用 CSS 像素设置尺寸会被认为是一样的。同样的物理尺寸浏览器会渲染出字体的更多细节。
使用 three.js 有多种方法来应对 HD-DPI。第一种就是不做任何特别的事情。这可以说是最常见的。渲染三维图形需要大量的 GPU 处理能力。移动端的 GPU 能力比桌面端的要弱。至少截止到 2018 年, 手机都有非常高的分辨率显示器。目前最好的手机的 HD-DPI 比例为 3x,意思是非高密度点显示器上的一个像素在高密度显示器上是 9 个像素。意味着需要 9 倍的渲染。计算 9 倍的像素是个大工程所以如果保持代码不变我们将计算一个像素然后浏览器将以三倍大小绘制(3x3=9 像素)。对于大型的 three.js 应用来说上面就是你想要的否侧你的帧速率会很低。
尽管如此如果你确实想用设备的分辨率来渲染,three.js 中有两种方法来实现:
- 一种是使用 renderer.setPixelRatio 来告诉 three.js 分辨率的倍数。访问浏览器从 CSS 像素到设备像素的倍数然后传给 three.js。
renderer.setPixelRatio(window.devicePixelRatio);
- 另一种方法是在调整 canvas 的大小时自己处理。
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const pixelRatio = window.devicePixelRatio;
const width = (canvas.clientWidth * pixelRatio) | 0;
const height = (canvas.clientHeight * pixelRatio) | 0;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
第二种方法从客观上来说更好,在使用 three.js 时有很多种情况下我们需要知道 canvas 的 drawingBuffer 的确切尺寸。比如制作后期处理滤镜或者我们在操作着色器需要访问 gl_FragCoord 变量,如果我们截屏或者给 GPU 读取像素,绘制到二维的 canvas 等等。通过我们自己处理我们会一直知道使用的尺寸是不是我们需要的。