three.js 航拍全景图(+陀螺仪)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

右上角陀螺仪也可点击,需要https的环境,手动下载DeviceOrientationControls.js文件
后台包含打点功能

<template>
  <div style="position: relative">
    <div
      id="quanjing"
      :style="{
        height: '100vh',
        width: isShowPoint ? 'calc(100% - 280px)' : '100vw',
      }"
    >
      <span
        id="tip"
        style="position: absolute; color: red; top: 0; left: 0"
      ></span>
      <!-- 封面图切换结束 -->
      <img
        :src="imageUrl + aerialData.asteroidImg"
        style="
          position: absolute;
          top: 50%;
          height: 100vh;
          left: 50%;
          transform: translate(-50%, -50%);
        "
        v-if="isShowFM"
      />
      <img
        v-if="isShowHand"
        src="../assets/images/quanjingHand.png"
        style="
          z-index: 2;
          position: absolute;
          top: 50%;
          left: 50%;
          margin-left: -80px;
          margin-top: -90px;
          border-radius: 10px;
          width: 160px;
          height: 180px;
        "
      />
      <!-- 封面图切换结束 -->
      <img
        src="../assets/images/quanjinglogo.png"
        style="position: absolute; top: 10px; left: 10px; width: 100px"
      />

      <!-- 右上角全景图标切换开始 -->
      <div class="rig-list" id="hangpaiIcon" v-if="!isPc">
        <img v-if="isFull" src="../assets/images/hangpaiIcon2.png" />
        <img v-else src="../assets/images/hangpaiIcon.png" />
      </div>
      <!-- 右上角全景图标切换开始结束 -->
      <!-- 标注增删改查开始 -->

      <div id="biaozhudian"></div>
      <!-- 底部icon按钮 -->
      <div class="bot-icon" v-if="isShowPoint">
        <img
          src="https://qverse.3dnest.cn/static/image/tag/addtag.png"
          @click="addPonit"
        />
      </div>
      <!-- 中心点+ -->
      <div class="bot-icon-center" v-if="isShowPoint">
        <p>+</p>
      </div>
    </div>
    <!-- 右侧列表 -->
    <div class="addPoint" v-if="isShowPoint">
      <div class="drawer-con" v-show="isPointList">
        <el-card class="box-card">
          <div slot="header" class="clearfix">
            <span>标记列表</span>
          </div>
          <div class="del" @click="delAllPoint">
            <img src="../assets/images/clear-tags.png" />一键清空
          </div>
          <div class="list" v-for="(item, index) in tagAllList" :key="item.id">
            <img
              class="list-img"
              v-if="item.chooseType == 0"
              src="../assets/images/ld-point.png"
            />
            <img
              class="list-img"
              v-if="item.chooseType == 1"
              src="../assets/images/dd-point.png"
            /><img
              class="list-img"
              v-if="item.chooseType == 2"
              src="../assets/images/sy-point.png"
            />
            <p>{{ item.title }}</p>

            <img
              @click="listDel(item)"
              class="list-rig"
              src="../assets/images/delete-tag.png"
            />
          </div>
        </el-card>
        <el-card class="box-card" style="margin-top: 10px">
          <div slot="header" class="clearfix">
            <span>初始方向</span>
          </div>
          <el-button @click="getPosition" type="warn">获取</el-button>
          <el-button @click="resetPosition" type="success">重置</el-button>
          <p style="font-size: 12px; color: rgb(198, 4, 4); margin: 5px 0 0 0">
            (注:以白色+为中心参考点)
          </p>
          <p style="font-size: 12px; color: gray">
            相机方向:{{ this.cameraPosition }}
          </p>
        </el-card>
      </div>
      <el-card
        class="box-card"
        style="overflow-y: auto; height: calc(100% - 60px)"
        v-show="!isPointList"
      >
        <el-collapse v-model="activeNames">
          <el-collapse-item title="标记类型" name="1"
            ><div class="imgChoose">
              <!-- 改为click -->
              <img
                @click="chooseTypeFun(0)"
                :style="{
                  borderColor: initPoint.chooseType == 0 ? 'red' : 'gray',
                }"
                src="../assets/images/ld-point.png"
              />
              <img
                @click="chooseTypeFun(1)"
                :style="{
                  borderColor: initPoint.chooseType == 1 ? 'red' : 'gray',
                }"
                src="../assets/images/dd-point.png"
              /><img
                :style="{
                  borderColor: initPoint.chooseType == 2 ? 'red' : 'gray',
                }"
                @click="chooseTypeFun(2)"
                src="../assets/images/sy-point.png"
              />
            </div>
          </el-collapse-item>
          <el-collapse-item
            title="标记背景颜色"
            name="2"
            v-if="initPoint.chooseType == 2"
          >
            <el-color-picker
              v-model="initPoint.color"
              show-alpha
              @change="changBiaoji"
              :predefine="predefineColors"
            >
            </el-color-picker
            ><span style="color: gray">(点击箭头选择颜色)</span>
          </el-collapse-item>
          <el-collapse-item
            title="竖线长度"
            name="3"
            @change="changBiaoji"
            v-if="initPoint.chooseType == 2"
          >
            <el-slider
              v-model="stepLong"
              :step="20"
              show-stops
              @change="changBiaoji"
            >
            </el-slider>
          </el-collapse-item>
          <el-collapse-item
            title="地点图标"
            name="4"
            v-if="initPoint.chooseType == 1"
          >
            <div class="imgChoose">
              <img
                @click="changeIconFun(0)"
                :style="{
                  borderColor: initPoint.chooseIcon == 0 ? 'red' : 'gray',
                }"
                src="http://image.qiniu.fangdadi.com/apanorama/icons/quanjingIcon.gif"
              />
              <img
                @click="changeIconFun(1)"
                :style="{
                  borderColor: initPoint.chooseIcon == 1 ? 'red' : 'gray',
                }"
                src="http://image.qiniu.fangdadi.com/icons/gif/quanjingLoc2.gif"
              /><img
                :style="{
                  borderColor: initPoint.chooseIcon == 2 ? 'red' : 'gray',
                  background: 'lightgrey',
                }"
                @click="changeIconFun(2)"
                src="http://image.qiniu.fangdadi.com/icons/gif/quanjingLoc4.gif"
              />
              <img
                @click="changeIconFun(3)"
                :style="{
                  borderColor: initPoint.chooseIcon == 3 ? 'red' : 'gray',
                }"
                src="http://image.qiniu.fangdadi.com/icons/gif/quanjingLoc3.gif"
              />
            </div>
          </el-collapse-item>
          <el-collapse-item title="标题" name="5">
            <p style="color: gray; line-height: 15px; margin: 0 0 5px 0">
              文本内容
            </p>
            <el-input
              type="textarea"
              autosize
              placeholder="请输入内容"
              v-model="initPoint.title"
              @change="changBiaoji"
            >
            </el-input>
          </el-collapse-item>
        </el-collapse>
      </el-card>

      <div class="bot-fixed" v-show="!isPointList">
        <el-button type="info" @click="backPonitList">取消</el-button
        ><el-button type="primary" @click="submitPointOne">确定</el-button>
      </div>
      <div class="bot-fixed" v-show="isPointList">
        <el-button @click="submitPoint" type="primary">上传</el-button>
      </div>
    </div>
  </div>
</template>
<!-- [{"title":"东直门","x":"445.42720890862677","y":"-218.0846523283593","z":"-57.86235074865762"}] -->
<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { DeviceOrientationControls } from "three/examples/jsm/controls/DeviceOrientationControls.js";
import TWEEN from "@tweenjs/tween.js";
import {
  CSS2DRenderer,
  CSS2DObject,
} from "three/examples/jsm/renderers/CSS2DRenderer.js";
import { aerialData, editAerial, updateMark } from "@/api/house/house.js";
export default {
  name: "quanjing",
  data() {
    return {
      drawer: false, //标记点抽屉
      isShowFM: false,
      isShowHand: false,
      imageUrl: "https://obj.qiniu.fangdadi.com/",

      scene: null,
      camera: null,
      renderer: null,
      css2Renderer: null,
      loader: null,
      texture: null,
      sphereGeometry: null,
      mesh: null,
      axesHelper: null,
      tween: null,
      controls: null,
      activeNames: ["1"], //折叠面板
      //标点
      vector: null,
      screenVector: null,
      doc: null,
      activePoint: null,
      div: null,
      raycaster: null,
      mouse: null,
      tagObject: null,
      //陀螺仪
      clock: null,
      isFull: null,
      cameraPosition: { x: 0, y: 0, z: 0.1 }, //相机方向
      dcontrols: null,
      aerialData: {},
      stepLong: 20,
      isPointList: true,
      initPoint: {
        id: 0,
        chooseIcon: 0,
        chooseType: 0,
        color: "",
        title: "请输入地点",
        x: "",
        y: "",
        z: "",
        step: 20,
      },
      pointIndex: null,
      isShowPoint: false,
      tagArray: [],
      whichPoint: "",
      tagArrayDL: [],
      tagArraySY: [],
      tagOne: "",
      tagAll: [],
      tagAllList: [],
      isPc: false,
      R: 500,
      fov: 130, //拍摄距离  视野角值越大,场景中的物体越小
      sxFov: 75,
      near: 1, //最小范围

      far: 1000, //最大范围
      tagAllListTag: [],
      //缩放移动端事件
      startX: null,
      startY: null,
      distanceStart: null,
      distanceEnd: null,
      distanceDiff: null,
      scale: null,
      dbTouch: true, //判断双指
      shuBiao: false,
      predefineColors: [
        "#ff4500",
        "#ff8c00",
        "#ffd700",
        "#90ee90",
        "#00ced1",
        "#1e90ff",
        "#c71585",
        "rgba(255, 69, 0, 0.68)",
        "rgb(255, 120, 0)",
        "hsv(51, 100, 98)",
        "hsva(120, 40, 94, 0.5)",
        "hsl(181, 100%, 37%)",
        "hsla(209, 100%, 56%, 0.73)",
        "#c7158577",
      ],
    };
  },

  watch: {
    deep: true,
  },
  created() {
    this.getAerialData();
  },
  mounted() {
    //判断是什么环境,手机还是电脑
    if (document.documentElement.clientWidth < 720) {
      this.isPc = false;
    } else {
      this.isPc = true;
    }

    // 点击右上角陀螺仪
    document
      .getElementById("hangpaiIcon")
      .addEventListener("click", this.fullOrExit, false);
    //监听鼠标滚动事件
    //取消鼠标右键弹窗
    document.oncontextmenu = function (e) {
      return false;
      //或者 e.preventDefault()
    };
  },

  methods: {
    //获取数据
    getAerialData() {
      let idNum = null;
      if (this.$route.params.id.split("+").length == 2) {
        this.isShowPoint = true;
        idNum = this.$route.params.id.split("+")[0];
      } else {
        this.isShowPoint = false;
        idNum = this.$route.params.id;
      }
      aerialData(idNum)
        .then((res) => {
          if (res.data) {
            document.title = res.data.newhouseName
              ? res.data.newhouseName
              : "房大地";
            this.aerialData = res.data;
            this.isShowFM = true;
            if (res.data.markData) {
              let obj = JSON.parse(res.data.markData);
              if (obj.point) {
                obj.point.forEach((i) => {
                  if (!i.id) {
                    i.id = Math.random();
                  }
                });
                this.tagAllList = obj.point;
                console.log(this.tagAllList);
              } else {
                this.tagAllList = [];
              }

              if (obj.cameraPosition) {
                this.cameraPosition = obj.cameraPosition;
              }
            }
          }
        })

        .then(() => {
          this.clock = new THREE.Clock();
          this.container = document.body;
          this.isFull = false;
          //判断是什么环境,手机还是电脑
          // if (document.documentElement.clientWidth < 720) {
          // }
          this.initThree();
          this.clickBiaoji();
          this.objTween();

          window.addEventListener("pointerdown", this.onMouseDown, false);

          var _this = this;
          // 更改渲染器画布大小
          window.onresize = function () {
            // 重置渲染器输出画布canvas尺寸
            _this.renderer.setSize(window.innerWidth, window.innerHeight);
            // 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
            _this.camera.aspect = window.innerWidth / window.innerHeight;
            // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
            // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
            // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
            _this.camera.updateProjectionMatrix();
          };
        });
    },
    // /**
    // 准备全景图像:
    // 首先,您需要获得或创建全景图像。全景图像是一种呈现完整360度视野的特殊图像。您可以使用专业相机拍摄全景照片,或者从互联网上获取全景图像。确保全景图像采用通常的全景图像格式,如equirectangular格式。
    // 设置Three.js场景:
    // 创建一个HTML页面,引入Three.js库。您可以从Three.js官方网站下载或使用CDN来加载库。
    // 创建Three.js场景:
    // 在您的HTML页面中,创建一个Three.js场景、相机和渲染器。
    //  */
    // // 相机第一个参数fov 130->75
    initThree() {
      this.scene = new THREE.Scene();
      this.camera = new THREE.PerspectiveCamera(
        this.fov,
        window.innerWidth / window.innerHeight,
        this.near,
        this.far
      );
      this.renderer = new THREE.WebGLRenderer({
        clearAlpha: 1,
        alpha: true,
        antialias: true,
      });
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.renderer.domElement.style.zIndex = -1;
      this.css2Renderer = new CSS2DRenderer();
      this.css2Renderer.setSize(window.innerWidth, window.innerHeight);

      this.LoadingImg();
    },

    LoadingImg() {
      var _this = this;
      /*
    加载全景图像:
    使用Three.js的TextureLoader加载全景图像。
    */
      this.loader = new THREE.TextureLoader();
      this.texture = this.loader.load(
        this.imageUrl + this.aerialData.panoramaImg,
        function (obj) {
          _this.isShowFM = false;
        }
      ); // If texture is used for color information, set colorspace.
      this.texture.encoding = THREE.sRGBEncoding;

      this.sphereGeometry = new THREE.SphereGeometry(this.R, 60, 32);
      this.sphereGeometry.scale(-1, 1, 1); //创建的球形几何体执行这个方法, 镜像就正回来了
      this.sphereMaterial = new THREE.MeshBasicMaterial({
        map: this.texture,
        side: THREE.DoubleSide,
      });
      this.mesh = new THREE.Mesh(this.sphereGeometry, this.sphereMaterial);
      this.scene.add(this.mesh);
      // // AxesHelper:辅助观察的坐标系
      // this.axesHelper = new THREE.AxesHelper(150);
      // this.scene.add(this.axesHelper);
      /*
    设置相机视角:
    将相机朝向全景图像的中心,以确保全景图像填充整个视野。
    */

      this.camera.position.set(0, this.R, 0.1);
      this.camera.lookAt(0, 0, 0);
      /**
       * 模型旋转
       */
      this.mesh.rotateY(-Math.PI * 2); //绕x轴旋转π/2
    },

    objTween() {
      var _this = this;
      // // 旋转过渡效果
      this.tween = new TWEEN.Tween({
        // 相机position
        x: 0,
        y: this.R,
        z: 0,
        ry: -Math.PI * 2,
        fov: 130,
        // 相机lookAt
        lx: 0,
        ly: 499.9,
        lz: 0,
      }) // 开始位置(2D)
        .to(
          {
            x: _this.cameraPosition.x,
            y: _this.cameraPosition.y,
            z: _this.cameraPosition.z,
            ry: 0,
            fov: 75,
            // 相机lookAt
            lx: 0,
            ly: 0,
            lz: 0,
          },
          3000
        ) // 结束位置(3D)
        .easing(TWEEN.Easing.Quadratic.InOut) // 缓动函数
        .onUpdate(function (obj) {
          _this.camera.position.set(obj.x * 1, obj.y * 1, obj.z * 1);
          _this.camera.lookAt(obj.lx, obj.ly, obj.lz);
          //_this.camera.lookAt(_this.scene.position);
          //_this.camera.lookAt(0, 0, 0);
          _this.mesh.rotation.y = obj.ry;
          //_this.camera.rotation.x = obj.ry;
          _this.camera.fov = obj.fov;
          _this.camera.updateProjectionMatrix();
        })
        .delay(3000)
        .start()
        .onComplete(function () {
          //运动结束后地图打点
          if (_this.tagAllList.length != 0) {
            _this.tagAllList.forEach((i) => {
              _this.biaojiOne(i, "all");
            });
          }

          _this.isShowHand = true;
          setTimeout(() => {
            _this.isShowHand = false;
          }, 2000);
        });

      this.OrbitControlsFun();
    },
    getPosition() {
      this.$message("旋转航拍角度,点击右侧确认按钮");
      this.cameraPosition = this.camera.position;
    },
    resetPosition() {
      this.cameraPosition = { x: 0, y: 0, z: 0.1 };
    },
    // /*
    // 渲染场景:
    // 使用requestAnimationFrame函数循环渲染Three.js场景。
    // */
    animate() {
      requestAnimationFrame(this.animate);
      TWEEN.update(); // 更新Tween.js动画
      this.renderer.render(this.scene, this.camera);
      this.css2Renderer.render(this.scene, this.camera);
      if (this.isFull) {
        this.dcontrols.update(this.clock.getDelta());
      }
    },

    /*
    添加交互性(可选):
    您还可以添加鼠标或触摸交互,以允许用户在全景图像中浏览。Three.js提供了相关的控制器,如OrbitControls。
    */
    OrbitControlsFun() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableZoom = false;
      this.controls.enablePan = false;
      //this.controls.enableDamping = true;
      this.controls.enableRotate = true;
      this.controls.rotateSpeed = -0.3; //·控制相机旋转速度,默认是1,负值表示反方向
      //是否可以缩放
      // 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景

      document.getElementById("quanjing").appendChild(this.renderer.domElement);
      var _this = this;
      if (document.documentElement.clientWidth < 720) {
        window.addEventListener(
          "touchstart",
          function (e) {
            var touches = e.touches;
            if (touches.length == 2) {
              _this.controls.enableRotate = false;

              _this.dbTouch = false;
              // 双指触摸
              _this.startX = Math.abs(touches[0].pageX - touches[1].pageX);
              _this.startY = Math.abs(touches[0].pageY - touches[1].pageY);

              _this.distanceStart = Math.sqrt(
                _this.startX * _this.startX + _this.startY * _this.startY
              ); // 计算两个触点之间的距离
            }
          },
          false
        );
        window.addEventListener(
          "touchmove",
          function (e) {
            var touches = e.touches;
            if (touches.length == 2) {
              // 双指触摸
              var endX = Math.abs(touches[0].pageX - touches[1].pageX);
              var endY = Math.abs(touches[0].pageY - touches[1].pageY);

              _this.distanceEnd = Math.sqrt(endX * endX + endY * endY); // 计算两个触点之间的距离

              _this.distanceDiff =
                _this.distanceEnd * 1 - _this.distanceStart * 1; // 计算距离差值

              _this.touchmovePhone(_this.distanceDiff);

              _this.scale = _this.distanceEnd / _this.distanceStart; // 计算缩放比例
            }
          },
          false
        );

        window.addEventListener("touchend", function (e) {
          if (_this.dbTouch == false) {
            _this.startX = null;
            _this.startY = null;
            _this.distanceStart = null;
            _this.distanceEnd = null;
            _this.controls.enableRotate = true;
            // this.dbTouch = true;
          }
        });
      } else {
        window.addEventListener("mousewheel", this.mousewheel, {
          passive: false,
        });
      }

      this.animate();
    },
    //设置一个标记点开始
    biaojiOne(initPoint, type) {
      if (this.tagOne != "" && !type) {
        this.scene.remove(this.tagOne);
      }
      if (initPoint.chooseType == 0) {
        //楼栋字
        var info1 = document.createElement("div");
        info1.innerText = initPoint.title;
        info1.style.marginTop = "-10px";
        info1.style.pointerEvents = "auto";
        info1.style.fontSize = "12px";
        info1.style.background = initPoint.color
          ? initPoint.color
          : "linear-gradient(to right bottom, rgb(255 172 65 / 78%), rgb(247 1 1 / 78%))";
        info1.style.padding = "3px 5px";
        info1.style.borderRadius = "7px";
        info1.style.color = "white";
        var sanjiao = document.createElement("div");
        sanjiao.style.position = "absolute";
        sanjiao.style.left = "50%";
        sanjiao.style.marginLeft = "-4px";
        sanjiao.style.bottom = "-4px";
        sanjiao.style.width = "0";
        sanjiao.style.height = "0";
        sanjiao.style.borderTop = "4px solid #ffffffc2";
        sanjiao.style.borderRight = "4px solid transparent";
        sanjiao.style.borderLeft = "4px solid transparent";
        info1.appendChild(sanjiao);
      } else if (initPoint.chooseType == 1) {
        var info1 = document.createElement("div");
        //地点标记
        info1.style.width = "40px";
        info1.style.height = "40px";
        info1.style.position = "absolute";
        info1.style.pointerEvents = "auto";
        var img = document.createElement("img");
        if (initPoint.chooseIcon == 0) {
          img.src =
            "http://image.qiniu.fangdadi.com/apanorama/icons/quanjingIcon.gif";
        } else if (initPoint.chooseIcon == 1) {
          img.src =
            "http://image.qiniu.fangdadi.com/icons/gif/quanjingLoc2.gif";
        } else if (initPoint.chooseIcon == 2) {
          img.src =
            "http://image.qiniu.fangdadi.com/icons/gif/quanjingLoc4.gif";
        } else if (initPoint.chooseIcon == 3) {
          img.src =
            "http://image.qiniu.fangdadi.com/icons/gif/quanjingLoc3.gif";
        }

        img.style.width = "40px";
        img.style.height = "40px";
        info1.appendChild(img);
        var p = document.createElement("p");
        p.innerText = initPoint.title;
        p.style.position = "absolute";
        p.style.bottom = "70%";
        p.style.left = "-70%";
        p.style.minWidth = "100px";
        p.style.textAlign = "center";
        p.style.minHeight = "20px";
        p.style.background = initPoint.color ? initPoint.color : "#31313194";
        p.style.padding = "5px";
        p.style.borderRadius = "8px";
        p.style.color = "white";
        info1.appendChild(p);
      } else if (initPoint.chooseType == 2) {
        //商业标记
        var info1 = document.createElement("div");

        //商业
        info1.innerText = initPoint.title;
        info1.style.fontSize = "12px";
        info1.style.transform = "translate(-50% ,0)";
        info1.style.pointerEvents = "auto";
        info1.style.background = initPoint.color
          ? initPoint.color
          : "#fc7676d1";
        info1.style.padding = "3px 8px";
        info1.style.top =
          "-" + (10 * (initPoint.step ? initPoint.step / 20 : 1) + 12) + "px";
        info1.style.borderRadius = "8px";
        info1.style.color = "white";
        var shuXian = document.createElement("div");
        shuXian.style.position = "absolute";
        shuXian.style.left = "50%";
        shuXian.style.bottom =
          "-" + 10 * (initPoint.step ? initPoint.step / 20 : 1) + "px";
        shuXian.style.width = "1px";
        shuXian.style.height =
          10 * (initPoint.step ? initPoint.step / 20 : 1) + "px";
        shuXian.style.background = "#ffffff99";
        info1.appendChild(shuXian);
      }

      ///

      let tag = new CSS2DObject(info1);
      tag.name = "proLabel";
      tag.position.set(initPoint.x, initPoint.y, initPoint.z);
      this.scene.add(tag);
      if (!type) {
        this.tagOne = tag;
      }

      this.tagAllListTag.push(tag);
      this.css2Renderer.domElement.style.position = "absolute";
      this.css2Renderer.domElement.style.top = "0";
      this.css2Renderer.domElement.style.pointerEvents = "none";

      document
        .getElementById("biaozhudian")
        .appendChild(this.css2Renderer.domElement);
    },

    clickBiaoji() {
      this.vector = new THREE.Vector3();
      this.screenVector = new THREE.Vector3();
      this.doc = document;
      this.activePoint = null;
      this.div = this.doc.getElementById("tip");
      this.raycaster = new THREE.Raycaster();
      this.mouse = new THREE.Vector2();
      this.tagObject = new THREE.Object3D();
    },

    /**
     * 鼠标点击触发
     **/
    onMouseDown(event) {
      if (event.buttons === 2 && this.shuBiao) {
        // 屏幕坐标转标准设备坐标
        this.vector.set(
          (event.clientX / window.innerWidth) * 2 - 1,
          -(event.clientY / window.innerHeight) * 2 + 1,
          0
        );
        // 将标准设备坐标转为世界坐标
        this.vector.unproject(this.camera);
        this.raycaster = new THREE.Raycaster(
          this.camera.position,
          this.vector.sub(this.camera.position).normalize()
        );
        let intersects = this.raycaster.intersectObjects([this.mesh]);

        if (intersects.length > 0) {
          this.activePoint = intersects[0].point;
          let point = this.toScreenPosition({
            point: this.activePoint,
          });
          this.div.style.left = point.x + "px";
          this.div.style.top = point.y + 20 + "px";
        }
        let xyz = this.activePoint;
        if (xyz.x != "" && xyz.x && xyz.x != 0) {
          this.$message({
            message: "获取到坐标,右侧编辑",
            type: "success",
          });
        }

        this.initPoint.x = xyz.x;
        this.initPoint.y = xyz.y;
        this.initPoint.z = xyz.z;

        this.shuBiao = false;
        this.isPointList = false;

        this.biaojiOne(this.initPoint);
      }
    },

    toScreenPosition({ obj = null, point = null }) {
      point ? this.screenVector.set(...point) : this.screenVector.set();
      // 屏幕坐标系中心
      let widthHalf = this.renderer.getContext().canvas.width / 2;
      let heightHalf = this.renderer.getContext().canvas.height / 2;

      if (obj) {
        // 更新物体及其后代的全局变换
        obj.updateMatrixWorld();
        // 提取位置相关的分量
        this.screenVector.setFromMatrixPosition(obj.matrixWorld);
      }

      // 世界坐标转标准设备坐标。范围[-1,1]
      this.screenVector.project(this.camera);

      //标准设备坐标转屏幕坐标(2D)
      this.screenVector.x = this.screenVector.x * widthHalf + widthHalf;
      this.screenVector.y = -this.screenVector.y * heightHalf + heightHalf;

      return {
        x: this.screenVector.x,
        y: this.screenVector.y,
      };
    },
    // 陀螺仪
    setOrientationControls(e) {
      // 判断手机电脑端
      if (!e.alpha) {
        return;
      }
      this.isFull = true;
      this.dcontrols = new DeviceOrientationControls(this.camera, true);

      this.dcontrols.connect();
      this.dcontrols.update();
      window.removeEventListener(
        "deviceorientation",
        this.setOrientationControls,
        true
      );
    },

    // 陀螺仪权限判断
    fullOrExit() {
      let _this = this;
      if (!_this.isFull) {
        try {
          console.log("浏览器UA---->", navigator.userAgent);
          if (
            navigator.userAgent.includes("iPhone") ||
            navigator.userAgent.includes("iPad") ||
            navigator.userAgent.includes("iPod") ||
            navigator.userAgent.includes("Macintosh")
          ) {
            // 这是苹果设备上的浏览器
            console.log("这是苹果设备上的浏览器");
            if (
              window.DeviceOrientationEvent !== undefined &&
              typeof window.DeviceOrientationEvent.requestPermission ===
                "function"
            ) {
              window.DeviceOrientationEvent.requestPermission()
                .then(function (response) {
                  if (response == "granted") {
                    window.addEventListener(
                      "deviceorientation",
                      _this.setOrientationControls,
                      true
                    );
                  }
                })
                .catch(function (error) {
                  console.error(
                    "THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:",
                    error
                  );
                });
            } else {
              window.addEventListener(
                "deviceorientation",
                _this.setOrientationControls,
                true
              );
            }
          } else {
            // 这不是苹果设备上的浏览器
            console.log("这不是苹果设备上的浏览器");
            window.addEventListener(
              "deviceorientation",
              _this.setOrientationControls,
              true
            );
          }
        } catch (error) {
          console.error("监听事件处理程序出错:", error);
        }
      } else {
        if (_this.dcontrols) {
          _this.dcontrols.disconnect();
          _this.dcontrols.update();
          window.removeEventListener(
            "deviceorientation",
            _this.setOrientationControls,
            true
          );
        } else {
          console.log("dcontrols 未创建");
        }
        _this.isFull = false;
      }
    },

    //上传标点信息
    async submitPoint() {
      let FormData = new window.FormData();
      FormData.append("aerialId", this.aerialData.aerialId);
      FormData.append(
        "markData",
        JSON.stringify({
          point: this.tagAllList,
          cameraPosition: this.cameraPosition,
        })
      );

      let res = await updateMark(FormData);
      if (res.data == 1) {
        this.$message.success("上传成功");
      }
    },

    //鼠标滑轮-鼠标上下滑轮实现放大缩小效果____pc
    mousewheel(e) {
      e.preventDefault();
      if (e.wheelDelta) {
        //判断浏览器IE,谷歌滑轮事件
        if (e.wheelDelta > 0) {
          if (this.sxFov <= 50) {
            this.sxFov = 50;
          } else {
            this.sxFov -= this.near < this.sxFov ? 1 : 0;
          }
          //当滑轮向上滚动时
        }
        if (e.wheelDelta < 0) {
          if (this.sxFov >= 75) {
            this.sxFov = 75;
          } else {
            this.sxFov += this.sxFov < this.far ? 1 : 0;
          }
          //当滑轮向下滚动时
        }
      }

      //改变fov值,并更新场景的渲染
      this.camera.fov = this.sxFov;
      this.camera.updateProjectionMatrix();
      this.renderer.render(this.scene, this.camera);
    },
    //手机上缩放_____移动端
    touchmovePhone(e) {
      if (e > 0) {
        if (this.sxFov <= 50) {
          this.sxFov = 50;
        } else {
          this.sxFov -= this.near < this.sxFov ? 1 : 0;
        }
        //当滑轮向上滚动时
      }
      if (e < 0) {
        if (this.sxFov >= 75) {
          this.sxFov = 75;
        } else {
          this.sxFov += this.sxFov < this.far ? 1 : 0;
        }
        //当滑轮向下滚动时
      }
      this.camera.fov = this.sxFov;

      this.camera.updateProjectionMatrix();
      this.renderer.render(this.scene, this.camera);
    },
    //新增单个标记点按钮
    addPonit() {
      if (!this.isPointList) {
        return;
      }
      this.initPoint = {
        id: Date.now(),
        chooseIcon: 0,
        chooseType: 0,
        color: "",
        title: "请输入地点",
        x: "",
        y: "",
        z: "",
      };
      this.shuBiao = true;
      this.tagOne = "";
      this.$message("鼠标右键点击在地图上");
    },
    //编辑页面返回按钮
    backPonitList() {
      this.isPointList = true;
      if (this.tagOne != 0) {
        // this.scene.remove(this.tagOne);
        this.scene.remove(tagAllListTag[tagAllListTag.length - 1]);
        this.tagAllListTag.pop();
      }
      this.tagOne = "";
    }, //编辑页面确定按钮
    submitPointOne() {
      this.isPointList = true;

      this.tagAllList.push(this.initPoint);
      this.$message.success("添加到标记列表");
      this.tagOne = "";
    },

    //删除单独标记点
    listDel(item) {
      this.tagAllList.forEach((i, t) => {
        if (item.id == i.id) {
          this.tagAllList.splice(t, 1);

          this.scene.remove(this.tagAllListTag[t]);
          this.tagAllListTag.splice(t, 1);
          // 释放资源(从 DOM 中移除元素)

          this.$message.success("删除成功");
        }
      });
    },
    //删除全部标记点
    delAllPoint() {
      this.tagAllListTag.forEach((i, t) => {
        this.scene.remove(i);
      });
      this.tagAllList = [];
      this.tagAllListTag = [];
      this.$message.success("删除成功");
    },
    changBiaoji() {
      this.initPoint.step = this.stepLong;
      this.scene.remove(this.tagAllListTag[this.tagAllListTag.length - 1]);
      this.tagAllListTag.pop();
      this.biaojiOne(this.initPoint);
    },
    changeIconFun(num) {
      this.initPoint.chooseIcon = num;
      this.scene.remove(this.tagAllListTag[this.tagAllListTag.length - 1]);
      this.tagAllListTag.pop();
      this.biaojiOne(this.initPoint);
    },
    chooseTypeFun(num) {
      this.initPoint.chooseType = num;
      this.scene.remove(this.tagAllListTag[this.tagAllListTag.length - 1]);
      this.tagAllListTag.pop();
      this.biaojiOne(this.initPoint);
    },
  },
};
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
// ::v-deep

#quanjing {
  overflow: hidden;
  width: 100vw;
  height: 100vh;
  touch-action: none;
  position: absolute;
  top: 0px;
}
.info1 {
  position: absolute;

  width: 40px;
  height: 40px;
}

.info1 img {
  width: 40px;
  height: 40px;
}

.info1 p {
  position: absolute;
  bottom: 100%;
  left: -70%;
  min-width: 100px;
  text-align: center;
  min-height: 20px;
  background: #31313194;
  color: white;
  padding: 5px;
  border-radius: 10px;
}
.info2 {
  width: 40px;
  height: 40px;
  background: red;
}
.info2 img {
  width: 40px;
  height: 40px;
}
.info2 p {
  position: absolute;
  bottom: 100%;
  left: -70%;
  min-width: 100px;
  text-align: center;
  min-height: 20px;
  background: #f4050594;
  color: white;
  padding: 5px;
  border-radius: 10px;
}
.rig-list {
  position: absolute;
  right: 10px;
  top: 40px;
  img {
    width: 40px;
    height: 40px;
    margin-bottom: 10px;
  }
  #hangpaiIcon {
    z-index: 2;
  }
}
.addPoint {
  width: 280px;
  height: 100vh;
  position: absolute;
  overflow: hidden;
  padding: 10px;
  background: rgb(235, 235, 235);
  right: 0;
  top: 0;
}
.bot-icon {
  position: fixed;
  bottom: 0;
  width: 70px;
  height: 70px;
  left: 50%;
  margin-left: -35px;
  img {
    width: 70px;
    height: 70px;
    margin: 0 auto;
    display: block;
    margin-bottom: 20px;
  }
}
.bot-icon-center {
  position: fixed;
  width: 20px;
  height: 20px;
  left: 50%;
  top: 50%;
  margin: -10px 0 0 -10px;
  p {
    margin: 0;
    padding: 0;
    opacity: 0.7;
    text-align: center;
    color: white;
    font-size: 20px;
  }
}
.row {
  width: 100%;
  overflow: hidden;
  margin-bottom: 5px;
  div {
    float: left;

    width: 50px;
  }
  .inp {
    float: left;
    border: none;
    outline: none;
    width: 100px;
    margin-right: 10px;
  }
}
.drawer-con {
  margin-bottom: 10px;
  overflow-y: auto;
  height: calc(100% - 60px);
}
.del {
  img {
    width: 15px;
    height: 15px;
    margin: 0 10px;
    display: inline;
    vertical-align: middle;
  }
  font-size: 13px;
  margin-bottom: 10px;
}
.list {
  padding: 10px;
  border: 1px solid rgb(213, 213, 213);
  vertical-align: middle;
  margin-bottom: 10px;
  p {
    font-size: 15px;
    display: inline;
  }
}
.list-lo {
  display: inline;
  width: 20px;
  height: 20px;
  margin: 0px 10px 0px 0px;
  overflow: hidden;
  border-radius: 50%;
}
.list-rig {
  margin: 0 5px;
  display: inline;
  width: 18px;
  height: 18px;

  float: right;
}
.drawer-tit {
  font-size: 15px;
  font-weight: bold;
}
.imgChoose {
  overflow: hidden;
  img {
    border: 2px solid gray;
    float: left;
    width: 60px;
    height: 60px;
    display: block;
    margin-right: 10px;
  }
}
.bot-fixed {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  background: white;
  padding: 10px;
  display: flex;
  box-shadow: 0 -10px 10px #e1e1e1;
  justify-content: space-around;
}
.list-img {
  width: 30px;
  height: 30px;
  overflow: hidden;
  border-radius: 50%;
  vertical-align: middle;
  margin-right: 10px;
}
</style>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值