你完成了一个threejs的模型之后,里面有很多东西,你咋知道你点击的是哪个呢??如何触发你点击的事件呢?再canvas画布中可不能和html事件一样直接使用e.target来完成了哦。如何做到呢?
光线投射实现三维定位
方法思想
方法就是从点击的地方创造一条虚拟的从相机射入的射线,来计算这条射线是否穿过了什么几何体,穿过了几个,然后通过方法获取到穿过物体的属性。
1.创建三个小球,将其放入场景中
// 创建三个球
const sphere1 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0x00ff00,})
);
sphere1.position.x = -4;
scene.add(sphere1);
const sphere2 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0x0000ff,})
);
scene.add(sphere2);
const sphere3 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0xff00ff,})
);
sphere3.position.x = 4;
scene.add(sphere3);
如果觉得三个球离得太近 可以把相机的z轴位置调高一点 也就是我们的眼睛靠后一点
camera.position.z = 15;
// 为了看到z轴
camera.position.y = 2;
// 设置x轴
camera.position.x = 2;
//设置相机的焦点 (相机看向哪个点)
camera.lookAt(0, 0, 0);
2.新建射线,新建鼠标向量
// 创建射线
const raycaster = new THREE.Raycaster();//创建鼠标向量
const mouse = new THREE.Vector2();
使用THREE.Raycaster 方法创建一个射线,鼠标向量用来存储后面得到的坐标值
3. window事件 获取点击到的地方
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值
});
4.公式计算出对应的坐标值 传入鼠标向量中
//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);
});
公式是为什么可以用四个点测试一下,然后自己思考一下。或者直接套用。得到的x,y的值就是平面像素转换为三维坐标的x,y值。
5.通过鼠标向量更新射线坐标
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//通过摄像机和鼠标位置更新射线raycaster.setFromCamera(mouse, camera);
});
6. 使用raycaster.intersectObjects方法计算物体和射线的焦点
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//通过摄像机和鼠标位置更新射线raycaster.setFromCamera(mouse, camera);//计算物体和射线的焦点const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);console.log(intersects);
});
点击空的地方得到的就是一个空数组,点击有球的地方,得到的就是一个数组,数组值中有object属性,代表的就是这个小球的内容,可以通过object属性来设置小球的参数。
7.交互Demo实现
当然 也有可能出现两个的情况,所以说我们这时候只用取数组第一个值,用它来设置就好了。我们现在用他来做一个点击就变成红色的交互效果,要求是第二次点击的时候 要变回原有的颜色。
我们的思路就是需要再点击的时候判断他是否已经变过色了,如果没有变过色,就让他变色,并且将现在的颜色存起来。如果变过色,就让他的颜色变回来。
//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//通过摄像机和鼠标位置更新射线raycaster.setFromCamera(mouse, camera);//计算物体和射线的焦点const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);// console.log(intersects);if (intersects[0].object._isSelect) {//设定旧的颜色intersects[0].object.material.color.set(intersects[0].object._originColor);intersects[0].object._isSelect = false;return;}//添加一个属性 选中状态intersects[0].object._isSelect = true;//将之前的颜色记录下来intersects[0].object._originColor =intersects[0].object.material.color.getHex();//设定新的颜色intersects[0].object.material.color.set(0xff0000);
});
全部代码
//导入 threejs
import * as THREE from "three";
//导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(45, // 视角window.innerWidth / window.innerHeight, // 宽高比 窗口的宽高进行设置的0.1, // 近平面 相机最近最近能看到的物体1000 // 远平面 相机最远能看到的物体
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器的大小 (窗口大小)
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器的dom元素添加到body中
document.body.appendChild(renderer.domElement);
camera.position.z = 15;
// 为了看到z轴
camera.position.y = 2;
// 设置x轴
camera.position.x = 2;
//设置相机的焦点 (相机看向哪个点)
camera.lookAt(0, 0, 0);//添加世界坐标辅助器 (红色x轴,绿色y轴,蓝色z轴)一个线段 参数为 线段长度
const axesHelper = new THREE.AxesHelper(5);
//添加到场景之中
scene.add(axesHelper);// 添加轨道控制器 (修改侦听位置) 一般监听画布的事件 不监听document.body
const controls = new OrbitControls(camera, renderer.domElement);//渲染函数
function animate() {controls.update();//请求动画帧requestAnimationFrame(animate);//渲染renderer.render(scene, camera);
}
animate();
//渲染// 监听窗口的变化 重新设置渲染器的大小 画布自适应窗口
window.addEventListener("resize", () => {// 重新设置渲染器的大小renderer.setSize(window.innerWidth, window.innerHeight);// 重新设置相机的宽高比camera.aspect = window.innerWidth / window.innerHeight;// 重新计算相机的投影矩阵camera.updateProjectionMatrix();
});// 创建三个球
const sphere1 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0x00ff00,})
);
sphere1.position.x = -4;
scene.add(sphere1);
const sphere2 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0x0000ff,})
);
scene.add(sphere2);
const sphere3 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0xff00ff,})
);
sphere3.position.x = 4;
scene.add(sphere3);// 创建射线
const raycaster = new THREE.Raycaster();//创建鼠标向量
const mouse = new THREE.Vector2();//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//通过摄像机和鼠标位置更新射线raycaster.setFromCamera(mouse, camera);//计算物体和射线的焦点const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);// console.log(intersects);if (intersects[0].object._isSelect) {//设定旧的颜色intersects[0].object.material.color.set(intersects[0].object._originColor);intersects[0].object._isSelect = false;return;}//添加一个属性 选中状态intersects[0].object._isSelect = true;//将之前的颜色记录下来intersects[0].object._originColor =intersects[0].object.material.color.getHex();//设定新的颜色intersects[0].object.material.color.set(0xff0000);
});// //给window侦听点击事件 获取点击到的地方
// window.addEventListener("click", (e) => {
// // console.log(e.clientX, e.clientY);
// //设置鼠标向量的xy值
// //公式 可以用边框四个点做测试 或者暂时先记住
// mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
// mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);// //测试向量坐标是否正确
// // console.log(mouse.x, mouse.y);// //通过摄像机和鼠标位置更新射线
// raycaster.setFromCamera(mouse, camera);// //计算物体和射线的焦点
// const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);// if (intersects[0].object._isSelect) {
// //设定旧的颜色
// intersects[0].object.material.color.set(intersects[0].object._originColor);
// intersects[0].object._isSelect = false;
// return;
// }
// //添加一个属性 选中状态
// intersects[0].object._isSelect = true;
// //将之前的颜色记录下来
// intersects[0].object._originColor =
// intersects[0].object.material.color.getHex();
// //设定新的颜色
// intersects[0].object.material.color.set(0xff0000);
// // console.log(intersects);
// });
//3D 光线投射技术 完成3D三维场景中事件交互