非全屏Canvas下ThreeJS Raycaster鼠标坐标校正方法问询
解决非全屏Canvas下ThreeJS鼠标射线检测坐标偏移问题
这个问题的核心原因很明确:你当前用的event.clientX和event.clientY是相对于浏览器视口的坐标,但你的Canvas元素并没有占满整个视口,它和文档边缘存在间距(也就是在页面里有偏移),直接用视口坐标计算会导致射线位置偏右,出现“点击右侧对象才会修改左侧目标”的错位情况。
要修正这个问题,我们需要先把鼠标的视口坐标转换成相对于Canvas元素本身的坐标,再进行ThreeJS需要的归一化处理。具体步骤如下:
- 获取Canvas元素在视口中的位置偏移:通过
getBoundingClientRect()方法获取元素的边界信息,其中包含了left(元素左边缘到视口左边缘的距离)和top(元素上边缘到视口上边缘的距离)。 - 计算鼠标在Canvas内部的相对坐标:用视口坐标减去Canvas的偏移量,得到鼠标在Canvas元素内的真实位置。
- 将相对坐标转换成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
相关产品推荐
相关产品推荐

