以上一篇入门篇为例来简单的设置下 3d 模型当中的交互事件, 上一篇我们已经完成了在 3d 页面中添加了一个红色球, 下面我们给这个球一个点击事件让它 Y 轴位置上升, 再设置一个鼠标移入到球上时让其变色.
1. 其实 three.JS 当中没有事件可以直接选中物体的, 我们需要监听 Windows 对象来完成与 3d 页面的交互, 通过使用到 three.JS 当中 RayCaster 对象, 用于在三维空间中进行鼠标拾取, 原理是: 相机与鼠标所在的设备坐标之间的连线与哪些物体相交. 相交的物体离屏幕越近的越靠前, 所以第一个物体就是我们选中的对象.
第一步: 给 Windows 对象添加点击事件监听器;
Windows.addEventListener('mousedown', mouseDownFuc);
第二步: 在事件监听函数里拾取到点击的对象集合;
- function mouseDownFuc(){
- let raycaster = new THREE.Raycaster();// 创建光线投射对象
- let mouse = new THREE.Vector2();// 创建二维平面
- let intersectsObjArr = getSelsectOBj(mouse,raycaster, e);// 通过封装的 getSelsectOBj 函数获取鼠标选中对象集合, e 是点击事件对象
- }
- // 获取事件操作对象
- function getSelsectOBj(mouse,raycaster, e) {
- // 将 html 坐标系转化为 webgl 坐标系, 并确定鼠标点击位置
- mouse.x = e.clientX / renderer.domElement.clientWidth*2-1;
- mouse.y = -(e.clientY / renderer.domElement.clientHeight*2)+1;
- raycaster.setFromCamera(mouse,camera);// 以 camera 为 z 坐标, 确定所点击物体的 3D 空间位置
- let intersects = raycaster.intersectObjects(scene.children, true);// 确定所点击位置上的物体数量集合
- return intersects;// 返回连线经过的物体集合
- }
第三步: 判断 intersectsObjArr 集合长度不为零, 确认我们鼠标点击的地方有物体, 如果有物体那么 intersectsObjArr[0] 就是我们选中的对象, 通过页面 console.log(intersectsObjArr[0]) 发现 intersectsObjArr[0] 包含了相交点的许多信息, 而我们只需要交点的对象, 所以我们需要取到 intersectsObjArr[0].object 对象; 然后我们还需要进行一次判断当前对象就是我们点击的对象, 在这里我们还得回到创建物体时给它的 name 属性一个值进行区分物体. 最后给它 position 属性值的 Y 轴坐标设为 50;
- myBall.name = 'redBall';// 创建物体时给它 name 属性一个名称
- if(intersectsObjArr.length> 0){
- if(intersectsObjArr[0].object.name == 'redBall'){
- intersectsObjArr[0].object.position.y = 50;
- }
- }
以上就是我们完成了 3d 页面物体点击事件, 然后就是鼠标的移入到物体变色, 移出时颜色还原, 这和 2d 页面的移入移出不太一样, 因为我们是给 Windows 对象加的监听器, 然后通过 RayCaster 对象来拾取到物体的, 所以我们这里需要给 Windows 对象加鼠标移动事件来判断鼠标是否移动我们的物体上. 其步骤同上点击事件一样.
- Windows.addEventListener('mousemove', mouseMoveFuc);
- function mouseMoveFuc(){
- let raycaster = new THREE.Raycaster();// 创建光线投射对象
- let mouse = new THREE.Vector2();// 创建二维平面
- let intersectsObjArr = getSelsectOBj(mouse,raycaster, e);// 通过封装的 getSelsectOBj 函数获取鼠标选中对象集合, e 是点击事件对象
- if(intersectsObjArr.length> 0){
- if(intersectsObjArr[0].object.name == 'redBall'){
- intersectsObjArr[0].object.material = new THREE.MeshPhongMaterial( { color: 'orange'});// 移到物体上时颜色变成橘色
- document.getElementsByTagName('body')[0].style.cursor = 'pointer';// 移到物体上时鼠标显示为手
- }
- }else{
- myBall.material = new THREE.MeshPhongMaterial( { color: 0xff0000});// 移出物体时颜色变成原来的红色
- document.getElementsByTagName('body')[0].style.cursor = 'default';// 移出物体时鼠标显示为默认
- }
- }
贴上最终代码: 运行一下, 看下效果 https://lookuptothesky.coding.me/blog-event
- <!DOCTYPE HTML>
- <HTML>
- <head>
- <meta charset=utf-8>
- <title>
- event
- </title>
- <style>
- body { margin: 0; padding: 0; height: 100vh; width: 100vw; overflow: hidden;
- background: url('img/bgImg.jpg') no-repeat fixed; background-size: 100%
- 100%; }
- </style>
- </head>
- <body>
- <script src="lib/three.min.js">
- </script>
- <script src="lib/OrbitControls.js">
- </script>
- <script>
- Windows.onload = function() {
- let scene,
- camera,
- renderer,
- myBall;
- initThreeScene();
- // 物体的事件交互
- Windows.addEventListener('mousedown', mouseDownFuc);
- Windows.addEventListener('mousemove', mouseMoveFuc);
- function mouseDownFuc(e) {
- let raycaster = new THREE.Raycaster(); // 光线投射, 用于确定鼠标点击位置
- let mouse = new THREE.Vector2(); // 创建二维平面
- let intersects = getSelsectOBj(mouse, raycaster, e);
- if (intersects.length > 0) {
- console.log(intersects[0]) if (intersects[0].object.name == 'myBall') {
- myBall.position.y = 50;
- }
- }
- }
- function mouseMoveFuc(e) {
- let raycaster = new THREE.Raycaster(); // 光线投射, 用于确定鼠标点击位置
- let mouse = new THREE.Vector2(); // 创建二维平面
- let intersects = getSelsectOBj(mouse, raycaster, e);
- if (intersects.length > 0) {
- if (intersects[0].object.name == 'myBall') {
- myBall.material = new THREE.MeshPhongMaterial({
- color: 'orange'
- });
- document.getElementsByTagName('body')[0].style.cursor = 'pointer';
- }
- } else {
- myBall.material = new THREE.MeshPhongMaterial({
- color: 0xff0000
- });
- document.getElementsByTagName('body')[0].style.cursor = 'default';
- }
- }
- // 获取事件操作对象
- function getSelsectOBj(mouse, raycaster, e) {
- // 将 HTML 坐标系转化为 webgl 坐标系, 并确定鼠标点击位置
- mouse.x = e.clientX / renderer.domElement.clientWidth * 2 - 1;
- mouse.y = -(e.clientY / renderer.domElement.clientHeight * 2) + 1;
- // 以 camera 为 z 坐标, 确定所点击物体的 3D 空间位置
- raycaster.setFromCamera(mouse, camera);
- // 确定所点击位置上的物体数量
- let intersects = raycaster.intersectObjects(scene.children, true);
- return intersects;
- }
- function initThreeScene() {
- scene = new THREE.Scene();
- camera = new THREE.PerspectiveCamera(70, Windows.innerWidth / Windows.innerHeight, 0.1, 2000);
- camera.position.set(0, 50, 300);
- renderer = new THREE.WebGLRenderer({
- antialias: true,
- alpha: true
- }); //antialias: true; 让渲染的平面是光滑的, alpha: true; 让渲染的 3d 背景透明.
- renderer.setSize(Windows.innerWidth, Windows.innerHeight);
- document.body.appendChild(renderer.domElement);
- // 给场景添加一个环境光
- let ambientLight = new THREE.AmbientLight(0xf5f5f5);
- scene.add(ambientLight);
- // 辅助线
- let grid = new THREE.GridHelper(400, 30, 0xcccccc, 0xcccccc);
- scene.add(grid);
- // 创建的球
- let ball = new THREE.SphereGeometry(25, 100, 100); //25: 球半径 第一个 100: 水平分割面的数量. 第二个 100: 垂直分割面的数量.
- let ballColor = new THREE.MeshPhongMaterial({
- color: 0xff0000
- });
- myBall = new THREE.Mesh(ball, ballColor);
- myBall.name = 'myBall';
- scene.add(myBall);
- let controls = new THREE.OrbitControls(camera, renderer.domElement);
- controls.enableZoom = true; // 允许缩放
- // 设置相机移动距离
- controls.minDistance = 1;
- controls.maxDistance = 2000;
- controls.enableRotate = true;
- function render() {
- requestAnimationFrame(render);
- renderer.render(scene, camera);
- }
- render();
- Windows.onresize = function() {
- camera.aspect = Windows.innerWidth / Windows.innerHeight; // 相机重置可视范围
- camera.updateProjectionMatrix();
- renderer.setSize(Windows.innerWidth, Windows.innerHeight); // 渲染器重新渲染可视范围
- }
- }
- }
- </script>
- </body>
- </HTML>
posted on 2020-01-16 14:08 明明, 世界很美好 阅读 (...) 评论 (...) 编辑 收藏
来源: https://www.cnblogs.com/lmf-sky/p/12196119.html