1.简介:在传统的web开发中,由于DOM树和事件捕获冒泡机制的存在,我们可以很方便的在某个DOM节点上注册事件。并进行父元素事件代理等一系列操作。但是在webgl的3D世界中,用户使用鼠标或者触摸事件,事件接收者是canvas容器。如何将这个点击行为映射到3D世界,需要3D世界的能力,建立一个平面容器从canvas到3D世界的桥梁,也就是所谓的objectpicking2.基础知识:DOM和NDC坐标变换,相机光线,观察者模式。熟悉这部分知识的同学可以直接跳到下面的代码实现。A。DOM坐标与NDC坐标转换:这里,我们知道DOM坐标的(0,0)点在容器的左上角,NDC坐标的(0,0)点在容器的中心容器,需要进行坐标转换。具体定义请参考以下两个链接3D坐标变换屏幕坐标定义b.Cameraray:根据Three.js的官方定义,ray是用于物体拾取的机制。与传统的颜色拾取相比,光线拾取可以识别多个物体并得到序列,更加方便,更符合人的直觉。?看一个官方的代码例子?javascriptconstraycaster=newTHREE.Raycaster();constmouse=newTHREE.Vector2();functiononMouseMove(event){//这里实现了DOM坐标系到NDC坐标系的转换mouse.x=(event.clientX/window.innerWidth)*2-1;mouse.y=-(event.clientY/window.innerHeight)*2+1;}functionrender(){//从相机位置发出光线raycaster.setFromCamera(mouse,camera);//检测与相机相交的光线obj列表constintersects=raycaster.intersectObjects(scene.children);for(leti=0;i0){if(clicked){obj=null;return;}clicked=true;obj=intersects[0].object;intersectPoint=intersects[0].point;}else{clicked=false;}}函数移动(e){event.preventDefault();//在这里做一些移动优化}functionup(e){event.preventDefault();if(clicked&&!!obj&&obj.callback){obj.callback(obj.object3d,intersectPoint);}clicked=false}consteventOption={passive:false};container.addEventListener('mousedown',down,{passive:false});container.addEventListener('mousemove',move,{passive:false});container.addEventListener('mouseup',up,{passive:false});container.addEventListener('touchstart',down,{passive:false});container.addEventListener('touchmove',move,{passive:false});container.addEventListener('touchend',up,{passive:false});}functiongetVisibleList(targetObj){constlist=[]for(constkeyintargetObj){consttarget=targetObj[key].object3d;if(target.visible)list.push(target);}returnlist}/*使用方法:直接在mesh上注册事件修改three.js中Object3D的原型链,基于观察者模式,增加点击事件绑定,支持用户点击画布容器,拾取场景中的物体在具体的生产环境中,还需要考虑如何实现事件冒泡、渲染层级、鼠标拖动和长按操作,这些不在主流程中,不再赘述。大家可以在调试代码的过程中逐步完善以上内容。作者:huoyunxieshen0007链接:https://juejin.cn/post/6951226769529634830来源:掘金版权归作者所有。商业转载请联系作者授权,非商业转载请注明出处。