Three.js组内对象射线检测触发多动画问题求助
解决Three.js Raycaster点击触发多个对象动画的问题
嘿,我看你遇到了点击被遮挡对象时,Raycaster同时检测到多个对象导致动画串扰的问题。这其实是因为当前的检测逻辑没有考虑对象的距离优先级——射线会穿过前面的对象,检测到所有相交的物体,而你现在是分别判断每个对象是否被射线命中,不管它们离相机的远近,所以就会同时触发多个动画。咱们来一步步修复这个问题:
问题根源分析
你当前的点击事件里,分别调用了raycaster.intersectObject(fnup)、raycaster.intersectObject(old)等方法,只要某对象和射线相交,就会执行对应的动画。但当目标被其他对象遮挡时,射线会同时命中遮挡物和目标物,两个条件都满足,自然就会同时触发两组动画。
而raycaster.intersectObjects(group.children)返回的数组是按距离相机由近到远排序的,第一个元素就是用户实际点击到的最上层对象——这才是我们需要处理的目标。
解决方案:只处理最近的相交对象
我们可以重构点击事件的逻辑,优先取intersections数组的第一个元素,然后根据这个对象的标识(比如userData里的属性)来执行对应的动画,这样就不会触发被遮挡对象的动画了。
优化后的代码示例
首先,确保你给每个对象的userData添加了唯一标识(比如在创建对象时):
// 示例:给各个对象添加标识 fnup.userData.key = 'fnup'; old.userData.key = 'old'; cam.userData.key = 'cam'; alex.userData.key = 'alex';
然后修改onDocumentMouseDown函数:
function onDocumentMouseDown(event) { event.preventDefault(); mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersections = raycaster.intersectObjects(group.children); // 只处理最近的那个相交对象 if (intersections.length > 0) { const clickedObj = intersections[0].object; const objKey = clickedObj.userData.key; // 封装统一隐藏逻辑,避免重复代码 function hideAllObjects() { TweenMax.to(fnup, 1, {three:{y:-4000, opacity: 0 }, ease:Power2.easeInOut}); TweenMax.to(old, 1, {three:{y:-4000, opacity: 0 }, ease:Power2.easeInOut}); TweenMax.to(alex, 1, {three:{y:-4000, opacity: 0 }, ease:Power2.easeInOut}); TweenMax.to(cam, 1, {three:{y:-4000, opacity: 0 }, ease:Power2.easeInOut}); TweenMax.to(mirrorCube, 1, {three:{y:-400, opacity: 0 }, ease:Power2.easeInOut}); oldRotate = false; groupRotate = false; fnupPosition = false; } hideAllObjects(); // 根据对象标识执行对应的放大动画 switch(objKey) { case 'old': TweenMax.to(old, 1.5, {three:{scaleX: 2.5, scaleY: 2.5, x:0, y:0, z:0, rotationX: 0 }, ease:Power2.easeInOut}); break; case 'fnup': TweenMax.to(fnup, 1.5, {three:{scaleX: 2.5, scaleY: 2.5, x:0, y:0, z:0 }, ease:Power2.easeInOut}); break; case 'cam': TweenMax.to(cam, 1.5, {three:{scaleX: 2.5, scaleY: 2.5, x:0, y:0, z:0 , rotationX: 0}, ease:Power2.easeInOut}); break; case 'alex': TweenMax.to(alex, 1.5, {three:{scaleX: 2.5, scaleY: 2.5, x:0, y:0, z:0, rotationX: 0 }, ease:Power2.easeInOut}); break; } } };
额外优化建议
- 防止重复触发动画:可以在执行动画前判断对象是否正在被动画,比如用
TweenMax.isTweening(old)来检查,避免重复点击导致的动画混乱。 - 统一管理对象:把
fnup、old这些对象放到一个数组里,遍历执行隐藏动画,进一步减少重复代码。 - 射线检测优化:如果你的
group里有很多子对象,可以考虑给不需要检测的对象设置raycastable = false,减少射线检测的计算量。
这样修改后,点击时只会触发最上层(离相机最近)的对象动画,就不会出现被遮挡对象也触发动画的问题啦~
内容的提问来源于stack exchange,提问作者Jacob Truax




