aframe-mouse-manipulation
Version:
Enables the user to move objects in 3D space using a mouse on desktop.
1 lines • 19 kB
JavaScript
(()=>{var t={660:()=>{AFRAME.registerComponent("cursor-tracker",{schema:{cursor:{type:"selector",default:"#cursor"}},init(){this.cursor=this.data.cursor,this.raycaster=this.cursor.components.raycaster.raycaster,this.forward=new THREE.Vector3(0,0,-1),this.localRayVector=new THREE.Vector3},tick(){this.el.object3D.getWorldPosition(this.localRayVector),this.localRayVector.add(this.raycaster.ray.direction),this.el.object3D.parent.worldToLocal(this.localRayVector),this.localRayVector.normalize(),this.el.object3D.quaternion.setFromUnitVectors(this.forward,this.localRayVector)}})},711:()=>{AFRAME.registerComponent("label-anchor",{schema:{offsetVector:{type:"vec3"},showLine:{type:"boolean",default:!0},lineColor:{type:"color",default:"white"}},init(){this.label=this.el.querySelector("[label]"),this.cameraWorldPosition=new THREE.Vector3,this.objectWorldPosition=new THREE.Vector3},update(){this.data.showLine?this.el.setAttribute("line__label-anchor",`start: 0 0 0; end: 0 0 0; color: ${this.data.lineColor}`):this.el.removeAttribute("line__label-anchor")},tick(){const t=this.el.sceneEl.camera;var e=1;if(t.isPerspectiveCamera&&(this.cameraWorldPosition.setFromMatrixPosition(t.matrixWorld),this.el.object3D.getWorldPosition(this.objectWorldPosition),e=this.objectWorldPosition.distanceTo(this.cameraWorldPosition)),this.label.object3D.position.copy(this.data.offsetVector),this.label.object3D.position.multiplyScalar(e),this.data.showLine){const t=this.label.object3D.position,e=`${t.x} ${t.y} ${t.z}`;this.el.setAttribute("line__label-anchor",`end: ${e}`)}}}),AFRAME.registerComponent("label",{schema:{overwrite:{type:"boolean",default:!1},forceDesktopMode:{type:"boolean",default:!1}},init(){this.enterVR=this.enterVR.bind(this),this.exitVR=this.exitVR.bind(this),this.el.sceneEl.addEventListener("enter-vr",this.enterVR),this.el.sceneEl.addEventListener("exit-vr",this.exitVR)},update(){this.el.sceneEl.is("vr-mode")?this.enterVR():this.exitVR()},enterVR:function(){this.el.setAttribute("face-camera",{fixedSize:this.data.forceDesktopMode,spriteMode:this.data.forceDesktopMode,overwrite:this.data.overwrite})},exitVR:function(){this.el.setAttribute("face-camera",{fixedSize:!0,spriteMode:!0,overwrite:this.data.overwrite})}}),AFRAME.registerComponent("face-camera",{schema:{fixedSize:{type:"boolean",default:!1},spriteMode:{type:"boolean",default:!1},overwrite:{type:"boolean",default:!1}},init:function(){this.cameraWorldPosition=new THREE.Vector3,this.objectWorldPosition=new THREE.Vector3,this.cameraQuaternion=new THREE.Quaternion,this.spriteDistanceVector=new THREE.Vector3,this.cameraDirectionVector=new THREE.Vector3,this.parentInverseQuaternion=new THREE.Quaternion,this.object3DSet=this.object3DSet.bind(this),this.data.overwrite&&this.el.addEventListener("object3dset",this.object3DSet)},object3DSet(t){const e=t.target.getObject3D(t.detail.type);e.material.depthTest=!1,e.material.depthWrite=!1},tick:function(){const t=this.el.sceneEl.camera;if(this.data.spriteMode||t.isOrthographicCamera?(setWorldQuaternion=(t,e)=>{t.updateMatrixWorld(),t.parent.getWorldQuaternion(this.parentInverseQuaternion),this.parentInverseQuaternion.invert(),t.quaternion.copy(e),t.quaternion.premultiply(this.parentInverseQuaternion)},this.cameraQuaternion.setFromRotationMatrix(t.matrixWorld),setWorldQuaternion(this.el.object3D,this.cameraQuaternion)):(this.cameraWorldPosition.setFromMatrixPosition(t.matrixWorld),this.el.object3D.lookAt(this.cameraWorldPosition)),this.data.fixedSize)if(t.isPerspectiveCamera)if(this.data.spriteMode){this.cameraDirectionVector.set(0,0,-1),this.cameraDirectionVector.transformDirection(t.matrixWorld),this.cameraWorldPosition.setFromMatrixPosition(t.matrixWorld),this.el.object3D.getWorldPosition(this.objectWorldPosition),this.spriteDistanceVector.subVectors(this.objectWorldPosition,this.cameraWorldPosition),this.spriteDistanceVector.projectOnVector(this.cameraDirectionVector);const e=this.spriteDistanceVector.length();this.el.object3D.scale.set(e,e,e)}else{this.el.object3D.getWorldPosition(this.objectWorldPosition);const t=this.objectWorldPosition.distanceTo(this.cameraWorldPosition);this.el.object3D.scale.set(t,t,t)}else this.el.object3D.scale.set(1,1,1)}})},527:()=>{AFRAME.registerComponent("object-parent",{schema:{parent:{type:"selector"}},update(){document.querySelectorAll(`#${parent.id}`).length>1&&console.warn(`object-parent matches duplicate entities for new parent ${parent.id}`);const t=this.data.parent.object3D;this.reparent(t)},remove(){const t=this.el.parentEl;this.reparent(t.object3D)},reparent(t){const e=this.el.object3D,i=e.parent;e.parent!==t&&(objectEl=t=>"Scene"===t.type?this.el.sceneEl:t.el,console.log(`Reparenting ${e.el.id} from ${objectEl(i).id} to ${objectEl(t).id}`),t.attach(e))}})}},e={};function i(o){var s=e[o];if(void 0!==s)return s.exports;var n=e[o]={exports:{}};return t[o](n,n.exports,i),n.exports}AFRAME.components["object-parent"]||i(527),AFRAME.components["cursor-tracker"]||i(660),AFRAME.components.label||i(711),AFRAME.registerComponent("mouse-manipulation",{schema:{debug:{type:"boolean",default:!1},showHints:{type:"boolean",default:!0},grabEvents:{type:"boolean",default:!1},grabEvent:{type:"string",default:"mouseGrab"},releaseEvent:{type:"string",default:"mouseRelease"},controlMethod:{type:"string",default:"parent",oneOf:["parent","transform"]}},events:{mousedown:function(t){this.mouseDown(t)},mouseup:function(t){this.mouseUp(t)},mouseenter:function(t){this.mouseEnter(t)},mouseleave:function(t){this.mouseLeave(t)}},init(){this.el.id||(this.el.id="cursor-"+THREE.MathUtils.generateUUID()),this.moveSpeed=3,this.grabbedEl=null,this.activeContactPoint=null,this.activeControlMethod="",this.camera=document.querySelector("[camera]"),this.cursorTracker=document.createElement("a-entity"),this.cursorTracker.setAttribute("cursor-tracker",`cursor:#${this.el.id}`),this.camera.appendChild(this.cursorTracker),this.cursorContactPoint=document.createElement("a-entity"),this.cursorContactPoint.setAttribute("id",`${this.el.id}-cursor-contact-point`),this.data.debug&&(this.cursorContactPoint.setAttribute("geometry","primitive:box; height:0.1; width: 0.1; depth:0.1"),this.cursorContactPoint.setAttribute("material","color: blue")),this.cursorTracker.appendChild(this.cursorContactPoint),this.cameraContactPoint=document.createElement("a-entity"),this.cameraContactPoint.setAttribute("id",`${this.el.id}-camera-contact-point`),this.data.debug&&(this.cameraContactPoint.setAttribute("geometry","primitive:box; height:0.1; width: 0.1; depth:0.1"),this.cameraContactPoint.setAttribute("material","color: red")),this.camera.appendChild(this.cameraContactPoint),this.vector1=new THREE.Vector3,this.vector2=new THREE.Vector3,this.windowMouseUp=this.windowMouseUp.bind(this),this.windowMouseDown=this.windowMouseDown.bind(this),window.addEventListener("mouseup",this.windowMouseUp),window.addEventListener("mousedown",this.windowMouseDown),window.addEventListener("contextmenu",(t=>t.preventDefault())),this.lbDown=!1,this.mbDown=!1,this.rbDown=!1,this.radiansPerMousePixel=.01,this.lastContactPointTransform=new THREE.Object3D},update:function(){this.data.showHints?this.createHints():this.removeHints()},remove(){this.removeHints(),this.cursorTracker.parentNode.removeChild(this.cursorTracker),this.cameraContactPoint.parentNode.removeChild(this.cameraContactPoint),window.removeEventListener("mouseup",this.windowMouseUp),window.removeEventListener("mousedown",this.windowMouseDown)},windowMouseDown(t){void 0!==t.buttons&&this.grabbedEl&&(this.data.debug&&console.log("MouseDown:",t),this.recordMouseButtonsState(t),this.updateMouseControls(),this.updateHints(),this.lbDown?this.grabElToContactPoint(this.cursorContactPoint,`#${this.el.id}-cursor-contact-point`):this.grabElToContactPoint(this.cameraContactPoint,`#${this.el.id}-camera-contact-point`))},windowMouseUp(t){void 0!==t.buttons&&this.grabbedEl&&(this.data.debug&&console.log("MouseUp:",t),this.recordMouseButtonsState(t),this.updateMouseControls(),this.updateHints(),this.lbDown?this.data.debug&&console.log("Left button still down"):0===t.buttons?(this.data.debug&&console.log("No buttons down - releasing"),this.releaseEl()):0===t.button&&(this.data.debug&&console.log("Left button released, middle or right still down"),this.grabElToContactPoint(this.cameraContactPoint,`#${this.el.id}-camera-contact-point`)))},recordMouseButtonsState(t){this.lbDown=1&t.buttons,this.mbDown=4&t.buttons,this.rbDown=2&t.buttons,this.data.debug&&(console.log("this.lbDown:",this.lbDown),console.log("this.rbDown:",this.rbDown),console.log("this.mbDown:",this.mbDown))},updateMouseControls(){this.lbDown?this.cursorContactPoint.setAttribute("mouse-dolly",""):this.rbDown?(this.cursorContactPoint.removeAttribute("mouse-dolly"),this.cameraContactPoint.setAttribute("mouse-dolly","")):(this.cursorContactPoint.removeAttribute("mouse-dolly"),this.cameraContactPoint.removeAttribute("mouse-dolly")),this.rbDown?this.cameraContactPoint.setAttribute("mouse-pitch-yaw",""):this.cameraContactPoint.removeAttribute("mouse-pitch-yaw"),this.mbDown?this.cameraContactPoint.setAttribute("mouse-roll",""):this.cameraContactPoint.removeAttribute("mouse-roll")},createHints(){this.data.showHints&&(this.hints=document.createElement("a-entity"),this.hints.setAttribute("label","overwrite: true; forceDesktopMode: true"),this.hints.setAttribute("mouse-manipulation-hints",""),this.el.appendChild(this.hints),this.updateHints())},updateHints(){if(!this.data.showHints)return;const t=t=>{this.hints.setAttribute("mouse-manipulation-hints","view",t)};this.lbDown?t("left"):this.rbDown?t("right"):this.mbDown?t("middle"):this.hoverEl?t("hover"):t("none")},removeHints(){this.hints&&(this.hints.parentNode.removeChild(this.hints),this.hints=null)},mouseDown(t){const e=this.getIntersections(t.target);if(0===e.length)return;const i=e[0];var o=this.getRaycastTarget(i);this.grabbedEl&&this.grabbedEl!==o&&console.warn("Grabbed 2nd element without releasing the first:",o.id,this.grabbedEl.id),this.grabbedEl=o},assureUsableId(t){t.id?document.getElementById(t.id)!==t&&console.error(`Element ID for ${t.id} does not unambiguously identify it. Check for duplicate IDs.`):t.setAttribute("id",Math.random().toString(36).slice(2))},getParentEl(t){const e=t.object3D.parent;return"Scene"===e.type?this.el.sceneEl:e.el},grabElToContactPoint(t,e){this.originalParentEl||(this.originalParentEl=this.getParentEl(this.grabbedEl)),this.assureUsableId(this.originalParentEl);const i=t.object3D.position;this.grabbedEl.object3D.getWorldPosition(i),t.object3D.parent.worldToLocal(i),"parent"===this.data.controlMethod?(this.activeControlMethod="parent",this.grabbedEl.setAttribute("object-parent","parent",e)):(this.activeControlMethod="transform",this.saveContactPointTransform(t)),this.hints.object3D.position.set(0,0,0),t.object3D.add(this.hints.object3D),this.data.grabEvents&&this.grabbedEl.emit(this.data.grabEvent)},releaseEl(){if(this.grabbedEl.object3D.parent,"parent"===this.activeControlMethod&&this.grabbedEl.setAttribute("object-parent","parent",`#${this.originalParentEl.id}`),this.data.grabEvents){const t=this.grabbedEl;setTimeout((()=>{t.emit(this.data.releaseEvent)}))}if(this.grabbedEl=null,this.activeControlMethod="",this.originalParentEl=null,this.el.object3D.add(this.hints.object3D),this.hoverEl){const t=this.hints.object3D.position;this.hoverEl.object3D.getWorldPosition(t),this.hints.object3D.parent.worldToLocal(t)}},mouseUp(){},getRaycastTarget:t=>t.components["raycast-target"]?t.components["raycast-target"].target:t,mouseEnter(t){const e=this.getIntersections(t.target);if(0===e.length)return;const i=e[0];if(this.hoverEl=this.getRaycastTarget(i),this.data.debug&&console.log("HoverEl set:",this.hoverEl),this.grabbedEl)return;this.cursorContactPoint;const o=this.hints.object3D.position;this.hoverEl.object3D.getWorldPosition(o),this.hints.object3D.parent.worldToLocal(o),this.updateHints()},mouseLeave(t){this.hoverEl=null,this.data.debug&&console.log("HoverEl cleared"),this.updateHints()},getIntersections:t=>t.components.raycaster.intersectedEls,saveContactPointTransform(t){const e=this.lastContactPointTransform;e.quaternion.identity(),e.position.set(0,0,0),e.scale.set(1,1,1),t.object3D.add(e),this.el.sceneEl.object3D.attach(e),this.activeContactPoint=t},followContactPoint(){const t=this.grabbedEl.object3D;this.lastContactPointTransform.attach(t),this.saveContactPointTransform(this.activeContactPoint),this.originalParentEl.object3D.attach(t)},tick(){"transform"===this.activeControlMethod&&this.followContactPoint()}}),AFRAME.registerComponent("mouse-manipulation-hints",{schema:{view:{type:"string",oneOf:["none","hover","left","middle","right"],default:"none"}},init(){this.views={},this.views,this.createHoverView(),this.createLeftView(),this.createRightView(),this.createMiddleView()},createHoverView(){const t=this.views;t.hover=document.createElement("a-entity"),t.hover.setAttribute("id","hint-hover"),this.el.appendChild(t.hover),this.addRowsToView(t.hover,[["left-mouse","move-arrows","left-mouse","pitch-yaw-arrow"],["mouse-wheel","in-out-arrow","middle-mouse","roll"]],[[0,0,0,0],[0,0,0,0]],[[1,1,-1,1],[1,1,1,1]],"above")},createLeftView(){const t=this.views;t.left=document.createElement("a-entity"),t.left.setAttribute("id","hint-left"),this.el.appendChild(t.left),this.addRowsToView(t.left,[["mouse-wheel","in-out-arrow"]],[[0,0]],[[1,1]],"below"),this.addRowsToView(t.left,[["left-arrow"],["left-arrow"],["left-arrow"],["left-arrow"]],[[270],[90],[0],[180]],[[1],[1],[1],[1]],"compass")},createRightView(){const t=this.views;t.right=document.createElement("a-entity"),t.right.setAttribute("id","hint-right"),this.el.appendChild(t.right),this.addRowsToView(t.right,[["mouse-wheel","in-out-arrow"]],[[0,0]],[[1,1]],"below"),this.addRowsToView(t.right,[["yaw-arrow"],["yaw-arrow"],["yaw-arrow"],["yaw-arrow"]],[[90],[90],[0],[0]],[[1],[-1],[-1],[1]],"compass")},createMiddleView(){const t=this.views;t.middle=document.createElement("a-entity"),t.middle.setAttribute("id","hint-middle"),this.el.appendChild(t.middle);const e=[["roll"]],i=[[1]];this.addRowsToView(t.middle,e,[[0]],i,"above"),this.addRowsToView(t.middle,e,[[180]],i,"below")},addRowsToView(t,e,i,o,s){const n=.15;var r,a;function h(e,s,r,a){e.forEach(((e,h)=>{!function(e,i,o,s,n){const r=document.createElement("a-image"),a=`https://cdn.jsdelivr.net/gh/diarmidmackenzie/aframe-components@latest/assets/icons/${e}.svg`;r.setAttribute("src",a),r.object3D.position.set(i,o,0),r.object3D.rotation.set(0,0,THREE.MathUtils.degToRad(s)),r.object3D.scale.set(.1*n,.1,.1),t.appendChild(r)}(e,s+h*n,r,i[a][h],o[a][h])}))}if(r=-(e[0].length-1)*n/2,a=.2+e.length*n/2,"below"===s&&(a-=.5),"above"===s||"below"===s)e.forEach(((t,e)=>{h(t,r,a-e*n,e)}));else if("compass"===s){console.assert(4==e.length);const t=.4;h(e[0],0,t,0),h(e[1],0,-t,1),h(e[2],-t,0,2),h(e[3],t,0,3)}},update(){const t=t=>{t.object3D.visible=!1},e=this.views;t(e.hover),t(e.left),t(e.right),t(e.middle);const i=e[this.data.view];i&&(i.object3D.visible=!0)}}),AFRAME.registerComponent("mouse-pitch-yaw",{schema:{singleAxis:{type:"boolean",default:!1},threshold:{type:"number",default:5}},init:function(){this.axis=null,this.cumX=0,this.cumY=0,this.xQuaternion=new THREE.Quaternion,this.yQuaternion=new THREE.Quaternion,this.yAxis=new THREE.Vector3(0,1,0),this.xAxis=new THREE.Vector3(1,0,0),this.onMouseMove=this.onMouseMove.bind(this),document.addEventListener("mousemove",this.onMouseMove)},remove(){document.removeEventListener("mousemove",this.onMouseMove)},onMouseMove:function(t){this.rotateModel(t)},rotateModel:function(t){this.xAxis.copy(this.el.object3D.position),this.xAxis.normalize(),this.xAxis.cross(this.yAxis);var e=t.movementX,i=t.movementY;this.data.singleAxis&&(this.cumX+=e,this.cumY+=i,!this.axis&&(Math.abs(this.cumX)>this.data.threshold||Math.abs(this.cumY)>this.data.threshold)&&(this.axis=Math.abs(this.cumX)>Math.abs(this.cumY)?"x":"y"),"x"===this.axis?i=0:"y"===this.axis?e=0:(e=0,i=0)),this.xQuaternion.setFromAxisAngle(this.yAxis,e/200),this.yQuaternion.setFromAxisAngle(this.xAxis,i/200),this.el.object3D.quaternion.premultiply(this.xQuaternion),this.el.object3D.quaternion.premultiply(this.yQuaternion),this.el.object3D.quaternion.normalize()}}),AFRAME.registerComponent("mouse-roll",{schema:{slowdownRadius:{type:"number",default:50}},init:function(){this.zQuaternion=new THREE.Quaternion,this.zAxis=new THREE.Vector3(0,0,1),this.onMouseMove=this.onMouseMove.bind(this),document.addEventListener("mousemove",this.onMouseMove),this.currPointer=new THREE.Vector2,this.prevPointer=new THREE.Vector2,this.el.setAttribute("entity-screen-position",""),this.modelPos=new THREE.Vector2,this.el.components["entity-screen-position"].getEntityScreenPosition(this.modelPos)},remove(){this.el.removeAttribute("entity-screen-position"),document.removeEventListener("mousemove",this.onMouseMove)},onMouseMove:function(t){this.rotateModel(t)},rotateModel:function(t){this.zAxis.copy(this.el.object3D.position),this.zAxis.multiplyScalar(-1),this.zAxis.normalize(),this.el.components["entity-screen-position"].getEntityScreenPosition(this.modelPos);const e=t.movementX,i=t.movementY;this.currPointer.set(t.clientX,t.clientY),this.currPointer.sub(this.modelPos),this.prevPointer.set(t.clientX-e,t.clientY-i),this.prevPointer.sub(this.modelPos);let o=this.prevPointer.angle()-this.currPointer.angle();o<-Math.PI&&(o+=2*Math.PI),o>Math.PI&&(o-=2*Math.PI);const s=Math.min(this.currPointer.length(),this.prevPointer.length());s<this.data.slowdownRadius&&(o*=s/this.data.slowdownRadius),this.zQuaternion.setFromAxisAngle(this.zAxis,o),this.el.object3D.quaternion.premultiply(this.zQuaternion)}}),AFRAME.registerComponent("entity-screen-position",{init:function(){this.vector=new THREE.Vector3,this.canvasBounds=document.body.getBoundingClientRect(),this.updateCanvasBounds=AFRAME.utils.debounce((()=>{this.canvasBounds=this.el.sceneEl.canvas.getBoundingClientRect()}),500),window.addEventListener("resize",this.updateCanvasBounds),window.addEventListener("scroll",this.updateCanvasBounds),this.getEntityScreenPosition=this.getEntityScreenPosition.bind(this)},remove(){window.removeEventListener("resize",this.updateCanvasBounds),window.removeEventListener("scroll",this.updateCanvasBounds)},getEntityScreenPosition(t){this.el.object3D.getWorldPosition(this.vector),this.vector.project(this.el.sceneEl.camera);const e=this.canvasBounds;return t.set((this.vector.x+1)*e.width/2,e.height-(this.vector.y+1)*e.height/2),t}}),AFRAME.registerComponent("mouse-dolly",{init:function(){this.moveSpeed=1.3,this.zQuaternion=new THREE.Quaternion,this.zAxis=new THREE.Vector3(0,0,1),this.onMouseWheel=this.onMouseWheel.bind(this),document.addEventListener("mousewheel",this.onMouseWheel)},remove(){document.removeEventListener("mousewheel",this.onMouseWheel)},onMouseWheel:function(t){this.dollyModel(t)},dollyModel:function(t){const e=t.deltaY,i=Math.pow(this.moveSpeed,-e/400);this.el.object3D.position.multiplyScalar(i)}})})();