首页/ 学科题库/ webgl/ WebGL 鼠标与触摸交互映射
webgl 实战教程

WebGL 鼠标与触摸交互映射

2026-09-13 约 7 分钟 juanwangdev

WebGL 鼠标与触摸交互映射

一句话结论:交互的关键是坐标换算,把屏幕像素坐标正确映射到裁剪坐标,拖拽与缩放才有正确的手感。

鼠标与触摸交互映射

是什么

鼠标与触摸交互映射,是把浏览器事件的屏幕像素坐标转换为 WebGL 裁剪坐标(x 与 y 均在 -1 到 1 之间)、再据此驱动相机或物体参数的过程。

解决什么问题

它解决点击位置与绘制位置对不上的问题。浏览器事件的原点在左上角、Y 轴向下、单位是 CSS 像素;WebGL 裁剪坐标原点在中心、Y 轴向上、范围只有 -1 到 1,两者不一致时必须显式换算。

为什么这样做

事件系统属于 DOM,渲染属于 GPU,二者坐标系天然不同。换算只需一次减偏移与两次线性变换,成本可忽略;把它做成独立函数还能同时服务鼠标、触摸与指针事件,避免多处重复实现。

怎么用

JavaScript
function toClipX(canvas, clientX, clientY) {
  const rect = canvas.getBoundingClientRect();
  // 相对画布的 CSS 像素坐标
  const px = clientX - rect.left;
  const py = clientY - rect.top;
  return {
    x: (px / rect.width) * 2 - 1,
    y: 1 - (py / rect.height) * 2,   // Y 轴翻转
  };
}

// 拖拽旋转:位移映射为角度增量
let last = null, rotX = 0.6, rotY = 0.8;
canvas.addEventListener('pointerdown', (e) => { last = { x: e.clientX, y: e.clientY }; });
canvas.addEventListener('pointermove', (e) => {
  if (!last) return;
  rotY += (e.clientX - last.x) * 0.01;
  rotX += (e.clientY - last.y) * 0.01;
  rotX = Math.max(-1.5, Math.min(1.5, rotX)); // 限制俯仰,避免翻转
  last = { x: e.clientX, y: e.clientY };
  render();
});
canvas.addEventListener('pointerup', () => { last = null; });

// 滚轮缩放:改变相机距离
canvas.addEventListener('wheel', (e) => {
  e.preventDefault();
  distance = Math.max(1.2, Math.min(20, distance + e.deltaY * 0.01));
  render();
}, { passive: false });

常见坑

  • 直接用 clientX 减 canvas.width 计算 → 原因:画布宽高是渲染缓冲尺寸,不是页面位置 → 处理:用 getBoundingClientRect 求偏移与 CSS 尺寸
  • 忘记翻转 Y 轴 → 原因:两套坐标系 Y 方向相反 → 处理:用 1 减去归一化后的值
  • 拖拽时画面翻转 → 原因:俯仰角越界 → 处理:把角度限制在正负 90 度以内
  • 移动端页面跟着滑动 → 原因:未阻止默认行为 → 处理:监听时设置 passive 为 false 并调用 preventDefault
  • 滚轮事件报错 → 原因:被动监听器不允许 preventDefault → 处理:显式传 passive false

要点总结

  • 坐标换算三步:减去画布偏移、按 CSS 尺寸归一化、翻转 Y 轴
  • 用 getBoundingClientRect 取偏移与尺寸,不要用 canvas.width/height
  • 拖拽旋转把屏幕位移按比例映射为角度增量,并限制俯仰角
  • 滚轮缩放用 passive false 的监听器才能阻止页面滚动
  • 指针事件可统一鼠标与触摸,减少分支代码

常见问题

屏幕坐标怎么转成 WebGL 坐标?

先用 getBoundingClientRect 求出相对画布的像素坐标,再按 x 除以宽度乘 2 减 1 转成裁剪 x,y 需要用 1 减去 y 除以高度乘 2,因为 WebGL 的 Y 轴向上。

为什么点击位置总是偏移?

常见原因有三个:没有减去画布在页面中的偏移、用 CSS 尺寸代替了渲染缓冲尺寸、以及忽略了设备像素比。三者都修正后偏移就会消失。

拖拽旋转应该怎么实现?

记录上一帧与当前帧的屏幕位移,把横纵位移分别映射为绕 Y 轴与 X 轴的旋转角增量,累加到相机角度上,每帧再用 lookAt 重算视图矩阵。

移动端触摸事件要注意什么?

要处理多指缩放、阻止默认滚动行为,并使用 passive 为 false 的监听方式才能调用 preventDefault。指针事件 pointerdown 与 pointermove 可以统一鼠标与触摸的处理。

想在手机上练这篇的配套题目?
使用微信 卷王开发者小程序/App,首页顶部扫一扫识别下方二维码,即可解锁本讲对应真题与错题本。
了解产品 App
← 上一篇 WebGL 索引组网与面剔除 下一篇 → WebGL 上下文丢失恢复与错误码定位