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);
});
}
}