画布、尺寸与设备像素比
画布有两套尺寸:CSS 尺寸决定它在页面里占多大,绘制缓冲尺寸决定真正画多少像素。 两者之比就是设备像素比。luma.gl 的设备把这两件事绑在一起, 弄错任何一侧都会得到模糊或错位的画面。
画布上下文的三个参数
createCanvasContext 在创建设备时传入,负责绑定画布、
设置像素比与自动 resize。
const device = await luma.luma.createDevice({
type: 'webgl',
adapters: [luma.webgl2Adapter],
createCanvasContext: {
canvas: document.getElementById('cv'),
useDevicePixels: Math.min(window.devicePixelRatio, 2),
autoResize: true
}
});
| 参数 | 取值 | 说明 |
|---|---|---|
canvas | 元素或 id | 省略时由 true 自动新建一张 |
useDevicePixels | true / false / 数字 | 数字即上限,常用 Math.min(dpr, 2) |
autoResize | true / false | 容器尺寸变化时自动同步绘制缓冲 |
useDevicePixels: true 在高分屏上会把绘制缓冲放大到 3 倍甚至 4 倍,
像素量随之翻几倍。视觉收益在 2 倍之后基本饱和,性能却持续下降,
所以截到 2 是通用做法。
CSS 尺寸必须自己写
luma.gl 只调整绘制缓冲,不会给画布加样式。画布的 CSS 尺寸为 0 时, 无论像素比是多少,画面上都看不到东西。
html, body { height: 100%; margin: 0; }
#cv {
display: block;
width: 100%;
height: 100%;
touch-action: none; /* 触摸设备上不让页面抢走手势 */
}
常见错误是只给 width 属性、不给 CSS 宽度:浏览器按
width 的像素值显示,容器一变宽,画布就被拉伸变形。
逐帧拿到真实尺寸
AnimationLoop 的回调里,width 与
height 是已经按像素比换算后的绘制尺寸,可以直接用来构造投影矩阵。
const loop = new luma.AnimationLoop({
device: device,
onRender: function (props) {
const aspect = props.width / props.height;
const pass = props.device.beginRenderPass({
clearColor: [0.05, 0.07, 0.10, 1]
});
model.draw(pass);
pass.end();
if (props.tick % 60 === 0) {
console.log('绘制尺寸', props.width, props.height, '宽高比', aspect.toFixed(3));
}
}
});
await loop.start();
用 canvas.clientWidth 直接算投影,会得到 CSS 像素而不是设备像素,
在高分屏上投影比例与实际视口差一倍以上,物体会被压扁。
尺寸变化时的处理
autoResize: true 覆盖了容器尺寸变化的情况,但离屏渲染目标
不会跟着变。窗口改变后,需要重建与尺寸相关的纹理与帧缓冲。
let offscreenSize = 0;
let fbo = null;
function ensureOffscreen(props) {
const size = Math.max(1, Math.floor(props.height * 0.5));
if (size === offscreenSize) return;
offscreenSize = size;
if (fbo) fbo.destroy();
const color = device.createTexture({
width: size,
height: size,
format: 'rgba8unorm',
usage: luma.Texture.SAMPLE | luma.Texture.RENDER
});
fbo = device.createFramebuffer({
width: size,
height: size,
colorAttachments: [color]
});
}
先销毁旧帧缓冲再建新的,否则每次窗口拖动都会泄漏一组纹理。 尺寸取整是必要的:纹理宽高必须是整数。
注意事项
把绘制缓冲尺寸当 CSS 尺寸用
鼠标事件给的是 CSS 像素坐标,拾取时却要在绘制缓冲里定位。
两者相差一个像素比,未换算时点击位置与命中结果偏到画面外。
用 event.clientX * devicePixelRatio 一类换算补齐。
WebGL 后端只挂一个画布
同一台设备再调用 createCanvasContext 会抛错。
需要第二个视图时,用帧缓冲渲染再合成,或者复制像素到另一张 2D 画布;
WebGPU 后端才支持同时挂多个画布。
容器隐藏时尺寸为 0
标签页切换、抽屉收起时容器宽高变成 0,绘制缓冲被压到 1×1。 恢复显示后必须再触发一次 resize,否则画面停在 1 像素的状态。
经纬志(2026)。《画布、尺寸与设备像素比》。https://www.jingweizhi.com/lumagl/tutorial/canvas-sizing.html(访问日期 2026-11-03)。