import * as THREE from 'three'; // 视图旋转控件 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; // 可视化平移控件 import { TransformControls } from 'three/examples/jsm/controls/TransformControls'; /** * 3d 鼠标拖拽,测试光源 */ export class ThreeDragLight { constructor(canvasId) { this.work(canvasId); } work(canvasId) { // 创建 3d 场景 let scene = new THREE.Scene(); let width = window.innerWidth; let height = window.innerHeight; let renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(width, height); // 最后一步很重要,我们将renderer(渲染器)的dom元素(renderer.domElement)添加到我们的HTML文档中。这就是渲染器用来显示场景给我们看的<canvas>元素。 document.getElementById(canvasId).appendChild(renderer.domElement); scene.background = new THREE.Color(0x9e9e9e); let camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000); // 3d 视图(主视角) camera.position.set(240, 50, 240); //设置照相机的位置 camera.lookAt(new THREE.Vector3(0, 0, 0)); //设置照相机面向(0,0,0)坐标观察 照相机默认坐标为(0,0,0); 默认面向为沿z轴向里观察; // 聚光灯 let spotLight = new THREE.SpotLight(0xffffff, 100, 0); //创建光源 // 光源移动 spotLight.position.set(0, 100, 0); scene.add(spotLight); //在场景中添加光源 // 网格辅助线 let gridHelper = new THREE.GridHelper(200, 10, 0x444444, 0xffffff); scene.add(gridHelper); // 三维坐标轴参考线 let axes = new THREE.AxesHelper(100); scene.add(axes); // 添加物体 let geo = this.addBox(scene); // 渲染 renderer.render(scene, camera); // 添加鼠标操作视图 let tackBallC, orbC; orbC = this.initMouseControl(scene, camera, renderer); // 添加拖动事件,返回平移控件对象 let transformControls = this.initDragControl(scene, camera, renderer, orbC); // 添加操作面板,绑定按钮事件,可切换光源对象 this.addControlPanel(renderer, scene, camera, transformControls, spotLight); /** * 使用官方的THREE.Raycaster * THREE.Raycaster是three.js中的射线类,其实现监听的原理是由相机位置为射线起点,由鼠标位置为射线方向发射射线, * 其穿过的所有几何体都会被监测到。 * @type {*[]} */ let intersects = []; //几何体合集 const pointer = new THREE.Vector2(); // 给 canvas 加监听点击事件 document.getElementsByTagName('canvas')[0].addEventListener('click', meshOnClick); let raycaster = new THREE.Raycaster(); function meshOnClick(event) { //geometrys 为需要监听的Mesh合集,可以通过这个集合来过滤掉不需要监听的元素例如地面天空 let geometrys = []; for (let i = 0; i < scene.children.length; i++) { console.log(scene.children[i].isLight); // 物体 和 光源都监听点击 if (scene.children[i].isMesh || scene.children[i].isLight) { geometrys.push(scene.children[i]); } } console.log(geometrys); pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(pointer, camera); // true为不拾取子对象 intersects = raycaster.intersectObjects(geometrys, true); // 被射线穿过的几何体为一个集合,越排在前面说明其位置离端点越近,所以直接取[0] console.log(intersects); if (intersects.length > 0) { transformControls.attach(intersects[0].object); } else { //若没有几何体被监听到,可以做一些取消操作,判断不到光源点击,直接移除控制不可行 // transformControls.detach(); // 当前控制器操控的对象 // console.log(transformControls.object); console.log(1); } renderer.render(scene, camera); } } /** * 鼠标操作 */ initMouseControl(scene, camera, renderer) { // 添加鼠标操作 let controls = new OrbitControls(camera, renderer.domElement); controls.addEventListener('change', () => { renderer.render(scene, camera); }); return controls; } // 添加拖拽控件 initDragControl(scene, camera, renderer, orbC) { /** * 添加平移控件,可视化操作,默认显示三维坐标系(因为是一个三维模型,所以需要添加到场景中) * * 变换控制器(TransformControls) * 该类可提供一种类似于在数字内容创建工具(例如Blender)中对模型进行交互的方式,来在3D空间中变换物体。 和其他控制器不同的是,变换控制器不倾向于对场景摄像机的变换进行改变。 * TransformControls 期望其所附加的3D对象是场景图的一部分。 * * TransformControls( camera : Camera, domElement : HTMLDOMElement ) * camera: 被控制的摄像机。 * domElement: 用于事件监听的HTML元素。 * 创建一个新的 TransformControls 实例。 * * 文档:https://threejs.org/docs/index.html#examples/zh/controls/TransformControls.detach * * .attach ( object : Object3D ) 设置应当变换的3D对象,并确保控制器UI是可见的。 * .detach () 从控制器中移除当前3D对象,并确保控制器UI是不可见的。 * @type {TransformControls} */ let transformControls = new TransformControls(camera, renderer.domElement); scene.add(transformControls); // 监听改变,则更新界面 transformControls.addEventListener("change", () => { renderer.render(scene, camera); }); // 变换控制器监听 mousedown,禁用 鼠标拖拽 transformControls.addEventListener("mouseDown", () => { orbC.enabled = false; }); // transformControls.addEventListener("mouseUp", () => { orbC.enabled = true; }); return transformControls; } /** * 添加物体,此为十二面体,可观察光影效果 * @param scene * @return {Mesh} */ addBox(scene) { // 添加 十二面体 const geometry = new THREE.DodecahedronGeometry(20, 0); const material = new THREE.MeshStandardMaterial({ color: 0x049EF4 }); const dodecahedron = new THREE.Mesh(geometry, material); scene.add(dodecahedron); // 添加边缘辅助线 let edges = new THREE.EdgesHelper(dodecahedron, 0x00ff00); scene.add(edges); const box = new THREE.BoxHelper(dodecahedron, 0xffff00); scene.add(box); // 后续增加点击事件,抛出物体对象 return dodecahedron; } /** * 添加操作面板,按钮控制光源对象切换,添加平移控件关联 * @param renderer * @param scene * @param camera * @param transformControls 平移控制器对象 * @param spotLight 聚光灯对象 */ addControlPanel(renderer, scene, camera, transformControls, spotLight) { let ele = ` <div class="control-panel"> <button class="spot-light">聚光灯</button> </div> `; $('body').append(ele); // 绑定按钮事件 $('.spot-light').on('click', function (e) { // 标记光源控制器,测试光源拖拽效果,后续单独搞个 demo transformControls.attach(spotLight); renderer.render(scene, camera); }); } }