Chapter III
Although the basic models have been created now, the main purpose of our 3D conference room is to place the items in the conference room and set the layout by ourselves. Therefore, we need to be able to drag the position and rotate the angle of the model in the conference room to achieve this highly free effect.
drag
DragControls
When it comes to drag effect, we must talk about three JS drag object DragControls, which is three JS provides convenient drag interaction
Explain the code directly
initDragControls(objects, info) { let that = this; // Initialize drag control this.dragControls = new DragControls( objects, this.camera, this.renderer.domElement ); this.dragControls.transformGroup = true; // Start dragging this.dragControls.addEventListener("dragstart", this.setDragstart); this.dragControls.addEventListener("drag", function (e) { if (e.object.position.y < -60) { e.object.position.y = -60; } }); // Drag end this.dragControls.addEventListener("dragend", this.setDragend); }, // Named drag start function setDragstart() { this.rotateBtnShow = false; this.editWellObject = false; this.controls.enabled = false; }, // Named drag end function setDragend() { this.controls.enabled = true; },
First, don't forget to import {dragcontrols} from "three / examples / JSM / controls / dragcontrols";
After registering the DragControls object, five events are provided for us
dragstart: triggered when the user starts dragging 3D Objects;
drag: triggered when the user drags 3D Objects;
Drawn: triggered when the user starts to complete 3D Objects;
hoveron: triggered when the pointer moves to a 3D Object or one of its children;
hoveroff: triggered when the pointer moves out of a 3D Object. Dran
It should be noted that when we register events, we can use named functions if we can, because this can remove the registered events when the page is destroyed and optimize the performance.
In this project, in order to achieve the gravity effect, that is, objects such as tables and chairs cannot leave the ground when dragging, so I added the restriction on the y-axis of the current operation model in the drag to achieve the following effects:
For multiple 3D models, we need to register multiple DragControls objects, because each drag object will correspond to the corresponding model. If only one DragControls object is registered, no matter which model is moved, it will only be the scene of the first model moving
rotate
Because three JS does not directly provide the object to rotate the 3D model, so we divided it into two parts in order to achieve the effect of rotation
Get the 3D model selected by the mouse
To select an object, we mainly rely on Raycaster, which is mainly used for mouse picking, that is, to calculate which objects the mouse touches when moving in three-dimensional space. The principle is that a ray will be emitted at the position pointed by the mouse, a vertical mouse, a ray perpendicular to the computer screen, three JS will get the objects that the ray passes through in turn.
In fact, we can understand this ray in another way, such as browser developer mode, as shown in the following figure
Click the element button in the page below to view the element code
getIntersects(event) { event.preventDefault(); let raycaster = new THREE.Raycaster(); let mouse = new THREE.Vector2(); mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; //The ray position is calculated through the mouse click position (two-dimensional coordinates) and the matrix of the current camera raycaster.setFromCamera(mouse, this.camera); // Gets the array of objects intersecting with the ray, in which the elements are sorted according to the distance, and the closer they are, the higher they are let moudle = this.scene.children.filter((item) => { if (item.type === "Group") { return item; } }); let intersects = raycaster.intersectObjects(moudle, true); if (intersects.length > 0) { let object = intersects[0].object; this.getGroup(object); return this.selectGroup; } return {}; }, // Get group getGroup(intersects) { let selectGroup = intersects.parent; if (selectGroup.type === "Group") { return (this.selectGroup = selectGroup); } else { selectGroup = selectGroup.parent; this.getGroup(selectGroup); } },
First, register the Raycaster object, obtain the coordinates of the mouse, and calculate the position of the ray according to the coordinates and the current camera (because the angle of view of the camera is also a very important factor) The intersectobjects () method obtains the 3D object intersecting with the ray, and then obtains its 3D group according to the 3D object. Because the externally loaded 3D model usually appears in the form of group, the ray may only touch an object of the group.
// Event triggered by mouse click mouseClick(event) { let intersects = this.getIntersects(event); if (intersects !== {} && intersects.type === "Group") { this.selectObject = intersects; if (this.selectObject.name === "Rotatable") { this.rotateBtnShow = true; this.addRotateClick(this.selectObject); } else { this.editWellObject = true; } } else { this.rotateBtnShow = false; this.selectObject = null; this.editWellObject = false; } },
Obviously, we need to register the mouse click event to obtain the group obtained after the trigger ray intersects with the 3D object. The mouse registration event is placed in mounted. I won't mention it specifically here. Here, we should also pay attention to one point. Because the mouse is registered globally, when clicking blank, it may cause an error because it can't obtain the relevant object, Moreover, not all 3D models need rotation effects. For example, walls and floors are not supported, so we need to add a filter condition, that is, intersects Type = = = "group" the rotation effect is triggered only when the obtained result is a group; (unregistered methods in the code will be found later in the code)
Add rotation button and rotation method
After obtaining the selected object, we will give it a rotatable method. In order to distinguish whether the current object is rotatable and highlight the currently selected object, first create a rotation button.
createRotateBtn() { let halWidth = window.innerWidth / 2; let halHeight = window.innerHeight / 2; let vector = this.selectObject.position.clone().project(this.camera); this.$refs.rotateBtn.style.left = `${ halWidth + vector.x * halWidth - 40 }px`; this.$refs.rotateBtn.style.top = `${ -vector.y * halHeight + halHeight - this.selectObject.position.y - 230 }px`; },
In fact, the position of the rotation button is mainly determined by the positioning of Css, so we need to calculate the left and top of the rotation button through the coordinates of the selected 3D model
addRotateClick() { if (this.addRotate) { this.addRotate = false; let that = this; let newX = Number(this.$refs.rotateBtn.style.left.replace("px", "")); // Data format conversion document.addEventListener("dragenter ", function (e) { e.preventDefault(); }); document.addEventListener("dragover", function (e) { e.preventDefault(); }); // Rotation event this.$refs.rotateBtn.addEventListener( "drag", function (event) { if (event.x < newX) { that.selectObject.rotateY(Math.PI / 120); } else if (event.x > newX) { that.selectObject.rotateY(Math.PI / -120); } newX = event.x; }, true ); }
If the rotation event is registered all the time, it will not only consume memory, but also make the rotation speed faster and faster. In order to avoid the rotation event being registered all the time, set addRotate as a switch. For the rotation event, I mainly assign it to the rotation button, and affect the rotation angle of the 3D model by dragging the rotation button.
Based on the coordinates of the current button, if you drag the button to the left, the corresponding 3D model will rotate counterclockwise. The rotation amplitude can be set by yourself. Use SelectObject The rotation (math. Pi / 120) method achieves the rotation of the selected object. Similarly, drag the button to the right and the corresponding 3D model rotates clockwise.
Upper effect:
Tips
When dragging, it may stop dragging because the mouse moves to the rotation button. Therefore, in the previous dragging code, the rotation button is hidden in the callback function that starts dragging.
Because three JS will count all objects that the ray emitted by the mouse passes through, so when dragging the object, the camera angle may also be dragged, so the track controller should also be disabled in the callback function to start dragging to prevent the camera from being rotated.