You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非全屏Canvas下ThreeJS Raycaster鼠标坐标校正方法问询

解决非全屏Canvas下ThreeJS鼠标射线检测坐标偏移问题

这个问题的核心原因很明确:你当前用的event.clientX和event.clientY是相对于浏览器视口的坐标,但你的Canvas元素并没有占满整个视口,它和文档边缘存在间距(也就是在页面里有偏移),直接用视口坐标计算会导致射线位置偏右,出现“点击右侧对象才会修改左侧目标”的错位情况。

要修正这个问题,我们需要先把鼠标的视口坐标转换成相对于Canvas元素本身的坐标,再进行ThreeJS需要的归一化处理。具体步骤如下:

  1. 获取Canvas元素在视口中的位置偏移:通过getBoundingClientRect()方法获取元素的边界信息,其中包含了left(元素左边缘到视口左边缘的距离)和top(元素上边缘到视口上边缘的距离)。
  2. 计算鼠标在Canvas内部的相对坐标:用视口坐标减去Canvas的偏移量,得到鼠标在Canvas元素内的真实位置。
  3. 将相对坐标转换成ThreeJS需要的归一化坐标(范围-1到1)。

修改后的onMouseDown方法代码如下:

public onMouseDown(event: MouseEvent) {
    console.log("onMouseDown");
    event.preventDefault();
    
    // 获取Canvas元素在视口中的边界信息
    const canvasRect = this.renderer.domElement.getBoundingClientRect();
    
    var rayCaster = new THREE.Raycaster();
    var mouse = new THREE.Vector2();
    
    // 计算鼠标相对于Canvas元素的坐标
    const mouseX = event.clientX - canvasRect.left;
    const mouseY = event.clientY - canvasRect.top;
    
    // 转换成ThreeJS需要的归一化坐标(范围-1到1)
    mouse.x = (mouseX / this.renderer.domElement.clientWidth) * 2 - 1;
    mouse.y = -(mouseY / this.renderer.domElement.clientHeight) * 2 + 1;
    
    rayCaster.setFromCamera(mouse, this.camera);
    var intersects = rayCaster.intersectObjects(this.scene.children);
    
    // 修正原代码中的笔误:obj应该指向scene的子元素集合
    console.log("Scene has " + this.scene.children.length + " objects");
    console.log(intersects.length + " intersected objects found");
    
    intersects.forEach((i) => {
        i.object.material = new THREE.MeshBasicMaterial({color: 0xf1f11f});
    });
    this.render();
}

另外我还修正了原代码里的一个小错误:原日志中的obj未定义,应该替换为this.scene.children,避免运行报错。

这样修改后,不管Canvas在页面中的位置如何,鼠标射线都能准确对应到场景内的对象,点击时就能正确修改目标对象的材质颜色了。

内容的提问来源于stack exchange,提问作者d4rty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:56:19