右上角陀螺仪也可点击,需要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>