threejs 笔记 vue ----2 创建一个物体点击时准确获取其在世界坐标中位置

1.创建楼栋

在上一篇文章中已经了解了创建物体的几个要素,本文在上一基础来进行实现,同样的所有的变量声明还是在init中完成,或者直接在mounted中声明,不要在data中,可以试验下在data中的效果,在这个例子中会很明显的感觉到浏览器渲染的卡顿

撸起,在init中添加创建楼栋this.createFloor();

createFloor(){
        let _this = this;
         let a = createFloorOne(10,-66.6,7,'A栋',['402'],['501']);
          a.rotateY(Math.PI / 2);
          a.position.set(-36.9,0,103);
          _this.mesh.add(a);
          let C = createFloorOne(10,-66.6,7,'C栋');
          _this.mesh.add(C);
          /*x,y 表示楼栋起点坐标,floorNum 表示有多少楼层 name 楼栋名称,contractWarning 表示合同到期预警,freeRoom 表示空闲房间 两个都是数组 */
        function createFloor(x,y,floorNum,name,freeRoom,contractWarning) {
          // l楼的长度 w 楼的宽度 h每层楼的高度 t墙的厚度  hd窗户高度的初始底数,既是第一层窗户的离地距离
          let l= 20, w = 60 , h = 8, hd = 6 , t = 0.5;  freeRoom = freeRoom || [];contractWarning = contractWarning || [];
          let group = new THREE.Group();// 声明组对象
          /**** 楼栋的矩形平面图 外轮廓起点值x,y轴小于内轮廓值 中间转点值则大于 内轮廓***/
          let BRoadOut = [
            [x, y],
            [x, y + w],
            [x + l, y + w],
            [x + l, y],
            [x, y]
          ];
          // 内墙轮廓
          let BRoadIn = [
            [x + t, y + t],
            [x + t, y + w - t],
            [x + l - t, y + w - t],
            [x + l - t, y + t],
            [x + t, y + t]
          ];
          /*楼栋*/
          let BRoadShape = createShape(BRoadOut, BRoadIn);// 生成矩形
          let BRoadGeometry = createExtrudeBufferGeometry(BRoadShape, h * floorNum + 2);// 拉伸矩形成长方体,并绕y轴旋转
          let BRoadMesh = createMesh1(BRoadGeometry, {color: 0xd3d7d4, side: THREE.DoubleSide});
          BRoadMesh.name = name;// 设置几何体的name值
          BRoadMesh.roomType = 'B';// 设置几何体类型
          BRoadMesh.castShadow = true;//开启阴影投射
          group.add(BRoadMesh);
          /* 楼顶 */
          let BFloorRoofTop = [
            [x + t, y + t],
            [x + t, y + w - t],
            [x + l - t, y + w - t],
            [x + l - t, y + t],
            [x + t, y + t]
          ];
          let BFloorRoofTopShape = createShape(BFloorRoofTop);
          let BFloorRoofTopGeometry = createExtrudeBufferGeometry(BFloorRoofTopShape, h * floorNum + 0.5);
          let BFloorRoofTopMesh = createMesh1(BFloorRoofTopGeometry, {color: 0xfafafa, side: THREE.DoubleSide});
          BFloorRoofTopMesh.name = name;
          BFloorRoofTopMesh.roomType = 'F';
          group.add(BFloorRoofTopMesh);
          // 楼栋名称logo
          let geometry = new THREE.CircleGeometry(2.5, 32);// 创建圆形几何体
          let brandMesh = createMesh2(geometry,{color:0xC6E2FF,map:createCircleText(name),side: THREE.DoubleSide});// 绘制文字
          brandMesh.position.set(x + (l - 9) / 2 + 4.5,h * floorNum - 0.5,Math.abs(y || 0) + 0.06);
          group.add(brandMesh);

          // 设置两个几何体之间的距离0.06,否则会重叠,遮盖
          // 正面
          let xNum = 2;
          let whiteWindow = createWindow(0xffffff);// 普通房间使用白色窗户
          let redWindow = createWindow(0xca0b0b);// 空置房间使用红色窗户
          let warningWindow = createWindow(0xE6A23C);// 即将到期房间使用黄色
          for (let i = 0; i < xNum; i++) {
            for (let j = 0; j < floorNum; j++) {
              let windows = whiteWindow.clone();
              freeRoom.forEach(item =>{
                if ((j+1)+'01' === item) {
                  windows = redWindow.clone();
                  windows.roomStatus = '1';
                }
              });
              contractWarning.forEach(item =>{
                if ((j+1)+'01' === item) {
                  windows = warningWindow.clone();
                  windows.roomStatus = '2';
                }
              });
              windows.name = name +'-'+ (j + 1) + '层' +'-'+ (j+1)+'01';
              windows.roomType = 'R';
              windows.position.x = x + (l - 9 * (xNum - 1)) / 2 + 9 * i;
              windows.position.y = hd + h * j;
              windows.position.z = Math.abs(y || 0) + 0.06;
              group.add(windows)
            }
          }
          // // 背面
          for (let i = 0; i < xNum; i++) {
            for (let j = 0; j < floorNum; j++) {
              let windows = whiteWindow.clone();
              freeRoom.forEach(item =>{
                if ((j+1)+'02' === item) {
                  windows = redWindow.clone();
                  windows.roomStatus = '1';
                }
              });
              contractWarning.forEach(item =>{
                if ((j+1)+'02' === item) {
                  windows = warningWindow.clone();
                  windows.roomStatus = '2';
                }
              });
              windows.roomType = 'R';
              windows.name = name +'-'+ (j + 1) + '层'+'-'+ (j+1)+'02';
              /** 正反面的窗户 x轴位置 = x起点 + (墙宽度 - 窗户总间隔) / 2  +   窗户之间的间隔平均值 ------
               * (墙宽度 - 窗户总间隔) / 2 得到的是当前墙面的窗户 距离两边墙沿的距离  **/
              windows.position.x = x + (l - 9 * (xNum - 1)) / 2 + 9 * i;
              windows.position.y = hd + h * j;
              windows.position.z = Math.abs(y || 0) - w - 0.06;
              windows.rotateY(Math.PI);
              group.add(windows)
            }
          }
          // 右侧
          for (let i = 0; i < 7; i++) {
            for (let j = 0; j < floorNum; j++) {
              let windows = whiteWindow.clone();
              let room =i < 3 ? '01':i > 3 ? '02': '';
              freeRoom.forEach(item =>{
                if ((j + 1) + room === item) {
                  windows = redWindow.clone();
                  windows.roomStatus = '1';
                }
              });
              contractWarning.forEach(item =>{
                if ((j + 1) + room === item) {
                  windows = warningWindow.clone();
                  windows.roomStatus = '2';
                }
              });
              let number = room ? '-'+(j + 1) + room : '';
              windows.roomType = 'R';
              if(i === 3)windows.roomType = 'F';
              windows.name = name + '-' + (j + 1) + '层' + number;
              windows.position.z = Math.abs(y || 0) - (w - 8 * (7 - 1)) / 2 - 8 * i;
              windows.position.y = hd + h * j;
              /*x轴的位置 等于x轴值 + 楼栋的宽度 0.2 + 窗户与楼栋的间隔*/
              windows.position.x = x + l + 0.06;
              windows.rotateY(Math.PI / 2);
              group.add(windows)
            }
          }

          // 左侧
          let group4 = new THREE.Group();
          for (let i = 0; i < 7; i++) {
            for (let j = 0; j < floorNum; j++) {
              let windows = whiteWindow.clone();
              let room =i < 3 ? '01':i > 3 ? '02': '';
              freeRoom.forEach(item =>{
                if ((j + 1) + room === item) {
                  windows = redWindow.clone();
                  windows.roomStatus = '1';
                }
              });
              contractWarning.forEach(item =>{
                if ((j + 1) + room === item) {
                  windows = warningWindow.clone();
                  windows.roomStatus = '2';
                }
              });
              let number = room ? '-'+(j + 1) + room : '';
              windows.roomType = 'R';
              if(i === 3)windows.roomType = 'F';
              windows.name = name + '-' + (j + 1) + '层' + number;
              windows.position.z = Math.abs(y || 0) - (w - 8 * (7 - 1)) / 2 - 8 * i;
              windows.position.y = hd + h * j;
              windows.rotateY(Math.PI / -2);
              group4.add(windows)
            }
          }
          group4.position.x = x - 0.06;
          group.add(group4);
          return group;
        }

function createShape(array1,array2) {// 创建对象轮廓,绘制一个二维平面形状
          let shape;
          if (array1) {
            shape = new THREE.Shape();// 创建二维形状对象,绘制外轮廓
            shape.moveTo(array1[0][0], array1[0][1]); // 开始绘制的点
            for (let i = 1; i < array1.length; i++) {
              shape.lineTo(array1[i][0], array1[i][1]) // 需要到达的点的坐标
            }
            if (array2) {//
              let path = new THREE.Path();// 创建二维路径对象 绘制内轮廓
              path.moveTo(array2[0][0], array2[0][1]);
              for (let i = 1; i < array2.length; i++) {
                path.lineTo(array2[i][0], array2[i][1])
              }
              shape.holes.push(path) // //把内轮廓参数添加到shape对象的holes属性上  设置内轮廓
            }
            return shape
          } 
        }
function createExtrudeBufferGeometry(shape,depth) {// 拉伸几何体
          let extrudeSetting = {
            steps: 1, // 拉伸几何体,分为几段
            depth : depth, // 拉伸几何体的高度
            bevelEnabled: false // 设置为true的情况下会有斜角
          };
          let geometry = new THREE.ExtrudeBufferGeometry(shape,extrudeSetting) ;
          geometry.rotateX( Math.PI / -2);
          return geometry;
        }
function createMesh(geometry,obj) {// 非光泽表面的材质,没有镜面高光。
          let material = new THREE.MeshLambertMaterial(obj); //材质对象Material
          return new THREE.Mesh(geometry, material);
        }
        function createMesh1(geometry,obj) {// 具有镜面高光的光泽表面的材质
          let material = new THREE.MeshPhongMaterial(obj); //材质对象Material
          return new THREE.Mesh(geometry, material);
        }

function createCircleText(text) {// 创建一个圆形文本贴图
          let canvas = document.createElement('canvas');
          canvas.width = 32;
          canvas.height = 32;
          let ctx = canvas.getContext('2d');
          // 背景
          ctx.arc(16, 16, 17, 0, 2 * Math.PI);// 画圆
          ctx.fillStyle = '#FFB90F';
          ctx.fill();// 充满画布
          // 文字
          ctx.font = "12px bold";
          ctx.fillStyle = '#FF4500';
          ctx.textAlign = 'center';
          ctx.textBaseline = 'middle';
          ctx.fillText(text, 16, 16);
          // CanvasTexture  从Canvas元素中创建纹理贴图 needsUpdate默认为true 这是必须的,以便使得Canvas中的数据能够载入
          return new THREE.CanvasTexture(canvas);
        }
function createWindow(color) {// 创建窗户
          let geometry = new THREE.BoxBufferGeometry(4, 4, 1);
          let mesh = createMesh(geometry, {color: color, side: THREE.DoubleSide});
          mesh.position.z = -0.5;
          return mesh
        }
},

以上方法用threejs中的几何体来创建一个楼栋,样子虽丑但是涉及的功能类api还是有一些的,可以很好地学习threejs的一些api,接下来就是要给房子添加点击事件了,让我们在点击时能准确的获取我们想要的房号值

2.添加点击事件

监听事件,在mounted内的$nextTick   document.body.addEventListener('click', this.choose);

choose(el){
        let x = el.clientX,y = el.clientY;
        this.left = x + 20;
        this.top = y + 20;
        let mouse = new THREE.Vector2();// 创建一个新的Vector2。
        let getBoundingClientRect = this.container && this.container.getBoundingClientRect();// 获取scene内容元素的大小及其相对于窗口的位置
        //这里的container就是画布所在的div,以整个scene所在的容器来界定范围,不以window.innerWidth为范围
    // 因为我采用的左侧边导航栏样式,以及顶部背景条,所以减去这两个值
        mouse.x = ((x - getBoundingClientRect.left) / this.container.offsetWidth) * 2 - 1;
        mouse.y = -((y - getBoundingClientRect.top) / this.container.offsetHeight) * 2 + 1;
        let raycaster = new THREE.Raycaster();// 鼠标拾取  -- 在三维空间中计算出鼠标移过了什么物体
        raycaster.setFromCamera(mouse, this.camera);// 将鼠标位置统一为当前三维坐标。x 和 y 方向的取值范围是 (-1 到 +1)
        let intersects = raycaster.intersectObjects( this.scene.children,true );// 获取当前鼠标位置的物体以及物体的后代
        if(intersects.length > 0){
          // console.log(intersects[0].object.name,intersects[0].object);
          if(!intersects[0].object.name)return;
          let obj = intersects[0].object;
          clearTimeout(this.timeOut);// 点击事件发生后 清除初始化时的展示框关闭延时器
          // 房产状态不是合同到期预警的
          if(obj.roomStatus !== '2'){
            let area = Math.random() * 1000 + 1;
            this.$message(obj.name + '空置房间面积' + area + 'm²');
           }
          
          // 房产状态不是空置房产提示的
          if(obj.roomStatus !== '1'){
            let num = Math.random() * 10 + 1;
            this.$message(obj.name + '到期个数'+ num )
          }
        }
      },

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值