Vue整合EsMap
一、EsMap场景整合到Vue
1.1. 生成EsMap本地文件
1.1.1. 进入EsMap数字孪生三维可视化云平台
打开链接https://www.esmap.cn/esmapv/content/cn/member/index.html
1.1.2. 新建三维场景
点击新增三维场景,输入信息,点击下一步即可。
- 其中三维场景ID和建筑token在web端需要使用
- ID为项目工程名
1.1.3. 绘制三维场景
在画板上添加元素后,点击提交即可。
1.1.4. 绘制场景导航
打开导航编辑。
点击开始绘制,在图层上进行描点,描点后点击提交即可。
如需删除点或者导航线,结束绘制后,左键单击选择需要修改的点或线,再右键打开选择列操作即可。
提交完后,点击模拟导航,即可查看导航线是否生效。
1.1.5. 下载三维场景
回到场景页面,点击下载场景即可。
下载后的文件如下:
1.2. 整合Vue
1.2.1. 将下载的场景文件放到/public/data文件夹下
1.2.2. 添加如下代码到/public/index.html中
<script src="./libs/esmap-3.0.min.js"></script>
1.2.3. 在页面中引入esmap
- 先通过
new window.esmap.ESMap
生成map模型数据,再通过this.map = map
使得可以在vue任何地方调用map模型的方法,对3D模型进行操作。 - 通过
map.on('mapClickNode', (e) => {})
可绑定每次点击模型触发的方法。
<template>
<div id='map-container'></div>
</template>
<script>
let that
export default {
mounted() {
that = this;
// 生成3D地图
this.loadMap();
},
methods: {
loadMap() {
var map = new window.esmap.ESMap({
mode: window.esmap.MapMode.Building, // *配置三维场景模式* Building为室内模式,City为城市模式
container: document.getElementById('map-container'), // 三维场景显示容器
mapDataSrc: './data', // 离线三维场景数据位置
token: '210323', // 打开三维场景对应的token(即创建场景的token)
});
map.openMapById('parkingLot_01'); // 创建场景的ID
// 地图点击事件
map.on('mapClickNode', (e) => {
console.log(e);
})
this.map = map;
}
}
}
二、EsMap相关API整合
2.1. 绘制线标注
- 实现esmap的
drawLineMark(linemark, callback)
方法。- linemark:
esmap.ESLineMarker
对象。 - callback:回调方法
- linemark:
- 该方法需在
map.on('loadComplete', function () {})
中调用。
makeLine() {
// 点坐标集合
let points = [{x: 12723047.070994029, y: 3579191.674298213}, {x: 12723061.788526496, y: 3579214.131607668}]
// 在三维场景加载完成事件中绘制线标注
this.map.on('loadComplete', function () {
let lineMarker = new window.esmap.ESLineMarker({
id: 1,
name: 'lineMarker',
points: points,
style: {
lineWidth: 6, // 线宽度
alpha: 0.8, // 线透明度
offsetHeight: 0, // 向上偏移量(距离地面高度,默认为0)
lineType: esmap.ESLineType.FULL, // 线样式
pickable: true, // 是否支持选中
color: '#FF0000', // 线条颜色
smooth: true, // 曲线是否光滑
fixedWidth: false, // 缩放时线宽是否保持不变
lineWidthMeter: false, // 线宽是否代表真实宽度
seeThrough: false, // 线是否穿透显示
},
})
that.map.drawLineMark(lineMarker)
})
},
线标注样式说明
2.2. 修改模型颜色
- 该方法需在
map.on('loadComplete', function () {})
中调用。
map.on('loadComplete', function () {
map.changeModelColor({
id: 3068654, //模型ID,id可为数组
//name: '停车场', //模型name
//fnum: 1, //建筑楼层,可为数组
color: '#00ff00'
})
});
2.3. 添加气泡标注
- 通过
esmap.ESPopMarker
对象生成。 - 该方法需绑定在左键点击触发事件上,或者button按钮点击事件上(暂只测试了这两种,不通过这两种方式,直接默认事件调用会报错)。
- 该方法无需使用
map.on('loadComplete', function () {})
,否则无法生效。
let pop = {x: 123, y: 321};
var popMarker = new esmap.ESPopMarker({
mapCoord: {
x: pop.x, // 坐标
y: pop.y,
height: 4, // 距离地面高度
fnum: 1 // 所在楼层
},
width: 300, // 气泡标注宽度
height: 100, // 气泡标注高度
content: `HTML代码<a target='_bank' href='http:// www.esmap.cn'>易景空间官方网站</a>`, // 气泡框的内容,支持输入HTML代码
closeCallBack: function () { // 信息窗点击关闭后的回调函数
},
created: function () { // 创建完成的回调函数
}
});
2.4. 添加圆形标注
- 通过
esmap.ESPolygonMarker
生成。 - 该方法需在
map.on('loadComplete', function () {})
中调用。 - 在
map.on('loadComplete', function () {})
中调用map的方法不能用this
,要用that.map
。
let that = this
this.map.on('loadComplete', function () {
var marker = new esmap.ESPolygonMarker({
name: "圆形标注",
id: 20,
color: 'rgb(0, 0, 255)', //设置颜色
alpha: 0.3, //设置透明度
lineWidth: 2, //设置边框线的宽度
height: 6.2, //设置高度
points: {
type: 'circle', //设置为圆形
center: { //设置此形状的中心坐标
x: coord.x + 20,
y: coord.y - 20
},
radius: radius, //设置半径
segments: 40, //设置段数,默认为40段
}
});
var building = that.map.getBuilding();
// 获取第一层的楼层对象
var floor = building.getFloor(1);
// 获取或者新建一个name为'mypoly'的多边形标注层
var layer = floor.getOrCreateLayerByName('mypoly', esmap.ESLayerType.POLYGON_MARKER);
layer && layer.addMarker(marker);
})
2.5. 添加矩形标注
- 通过
esmap.ESPolygonMarker
生成。 - 该方法需在
map.on('loadComplete', function () {})
中调用。 - 在
map.on('loadComplete', function () {})
中调用map的方法不能用this
,要用that.map
。
let that = this;
this.map.on('loadComplete', function () {
// 创建一个多边形对象
var rectangleMarker = new esmap.ESPolygonMarker({
id: 'polygon1', // 标注ID
name: '多边形标注', // 标注名称
color: '#9F35FF', // 填充颜色
opacity: 0.3, // 透明度
height: 6, // 距离地面高度
points: {
type: 'rectangle', // 设置为矩形
center: { // 设置此形状的中心坐标
x: that.map.center.x,
y: that.map.center.y
},
width: 3, // 设置矩形宽度
height: 4 // 设置矩形长度
}
});
var building = that.map.getBuilding();
// 获取第一层的楼层对象
var floor = building.getFloor(1);
// 获取或者新建一个name为'mypoly'的多边形标注层
var layer = floor.getOrCreateLayerByName('mypoly', esmap.ESLayerType.POLYGON_MARKER);
// 添加标注
layer.addMarker(rectangleMarker);
});
2.6. 删除矩形和圆形标注
- layer为多边形标注层实例对象
layer.remove(marker) // 删除某个标注(marker是指由new esmap.ESPolygonMarker生成的多边形实例对象)
layer.removeAll() // 删除该图层下所有标注
2.7. 视角飞行动画
// 获取点击元素所处的楼层计算出高度
const height = this.map.focusBuilding.getFloorHeight(1)
// 聚焦点击元素所在的位置
this.map.cameraFlyTo({
directionAngle: 20.16, // 视角朝向的方向角
pitchAngle: 90.85, // 视角朝向的俯仰角
radius: 100.4, // 视角中心目标点至相机位置本身的距离
time: 1.5, // 视角移动飞行时间,单位(秒/s)
x: popConfig.mapCoord.x, // 聚焦点x轴
y: popConfig.mapCoord.y, // 聚焦点y轴
z: height, // 聚焦点高度
callback: () => { // 飞行动画结束后的回调函数
}
})
2.8. 绑定dom元素到气泡上
2.8.1. 获取esmap点击事件
- 此处通过esmap的点击事件触发气泡。
- 所以在点击事件触发后收集点击位置的值。
// 绑定esmap点击事件
map.on('mapClickNode', (e) => {
// 存储点击元素的配置到onlyPopConfig响应式对象,生成单个气泡子组件
this.onlyPopConfig.value = e;
})
2.8.2. dom元素封装
- 此处是将组件popDialog绑定到气泡上,直接使用html元素也可以。
- 组件外层需要一层container层,此层级用于包裹esmap图层(使用绝对定位覆盖esmap图层),使得popDialog组件可以在esmap相对图层上偏移。
- 需给container层设置一个父级home层,此层级使用相对定位,确保popDialog组件偏移位置
<div class="home">
<!-- esmap图层 -->
<div id='map-container'></div>
<div class="container">
<!-- 单个气泡组件,点击事件触发后,才生成气泡 -->
<popDialog
v-if="onlyPopConfig.value"
:popData="onlyPopConfig.value"
:key="onlyPopConfig.value"
></popDialog>
</div>
</div>
css代码
.home {
box-sizing: border-box;
margin: auto;
position: relative; /*home层设置相对定位,使得dom元素的偏移位置和气泡位置一致*/
overflow: hidden;
}
.container {
position: absolute; /*container层设置绝对定位,覆盖esmap图层*/
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /*此层级不可点,防止层级覆盖esmap图层后,导致esmap图层不可点击*/
overflow: hidden;
}
2.8.3. 生成气泡,将dom元素的偏移位置绑定到气泡生成的位置
- 该步骤在组件内完成。
- 组件也必须为绝对定位。
- 动态设置组件的偏移位置,即气泡的生成位置。
- 通过
v-show="popTips.visible"
根据气泡生成实时展示数据。
<div
class="pop_container"
:style="{
position: 'absolute', // 必须设置绝对定位才能对应到地图场景上
transformOrigin: 'bottom', // 固定transform的原点位置,避免偏移方向出错
transform: popTips.transformStyle // 通过气泡的onUpdatePosition方法动态绑定偏移位置
}"
>
<div v-show="popTips.visible">
<!-- 以下均为气泡需要展示的内容,以下为可参考内容 -->
<div class="pop_extend">
<!-- 标题栏 -->
<div class="pop_title">
<span>标题</span>
<li @click="hidePop">X</li>
</div>
<!-- 具体内容栏 -->
<div class="pop_detail">
我是绑定到气泡上的组件
</div>
</div>
<!-- 底部线条 -->
<div
class="pop_line"
:style="{
backgroundImage:
'linear-gradient(to bottom, #1594FF, rgba(0, 0, 0, 0))',
height: popData.height + 'px '
}"
></div>
</div>
</div>
js
- 此处用的是vue3的写法。
- 即在组件初始化的时候,生成气泡,再将页面dom元素的偏移位置与气泡位置绑定。
import {reactive, onMounted, onUnmounted} from 'vue'
export default {
// 组件名称
name: 'popDialog',
// 组件参数 接收来自父组件的数据
props: {
popData: {
type: Object, default: () => {
}
}
},
// 组件状态值
setup(props) {
// 定义该子组件的偏移和显隐的响应式对象
let popTips = reactive({
transformStyle: '',
visible: false
})
// 定义气泡标注对象
let PopMarker = reactive({})
onMounted(() => {
// 初始化生命周期执行气泡初始化
initPop()
})
onUnmounted(() => {
// 销毁组件生命周期执行气泡销毁
PopMarker.close()
PopMarker = null
})
// 初始化气泡标注并将位置信息绑定到该子组件的popTips响应式对象上
function initPop() {
// 根据父组件传的popData值,生成气泡标注
PopMarker = new esmap.ESPopMarker({
mapCoord: {
x: props.popData.mapCoord.x,
y: props.popData.mapCoord.y,
height: 1,
fnum: props.popData.FloorNum || 1,
},
bid: props.popData.bid,
content: props.popData.html,
className: props.popData.className,
showLevel: props.popData.showLevel,
updateSize: props.popData.updateSize,
created: function () {
// 将气泡标注的偏移位置绑定到该子组件的css属性transformStyle中
PopMarker.onUpdatePosition((transformStyle, displayStyle) => {
popTips.transformStyle = transformStyle;
popTips.visible = displayStyle.display === 'none' ? false : true
})
}
})
}
// 隐藏气泡标注,可以在组件内设置点击事件用于关闭气泡
function hidePop() {
PopMarker.hide()
}
return {popTips, hidePop}
}
}
css
- 当前使用的是scss的写法,在vue中需引入。
- 如需自定义内容,则只需要
.pop_container
的内容即可,以下全是自定义内容。 - 如需气泡内的某一元素可点击,在css设置
pointer-events: auto;
即可。
.pop_container {
user-select: none;
pointer-events: none;
z-index: 98;
font-size: 40px;
color: #fff;
.pop_line {
position: relative;
left: 50%;
transform: translateX(-50%);
width: 2px;
height: 50px;
background-image: linear-gradient(to bottom, #f3ab0f, rgba(0, 0, 0, 0));
animation: scale-in-bl 0.5s;
&::after {
content: '';
width: 8px;
height: 8px;
transform: translateX(-50%);
position: absolute;
bottom: 0;
left: 0;
background-color: #fff;
border-radius: 50%;
}
}
.pop_extend {
backdrop-filter: blur(4px);
color: #fff;
animation: shutter-in-top 0.3s 0.3s forwards;
opacity: 0;
width: 150px;
.pop_title {
height: 25px;
line-height: 25px;
font-size: 12px;
white-space: nowrap;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
background-color: #325f9c;
padding: 0 10px;
& > li {
border: 1px solid #fff;
padding: 2px;
width: 10px;
height: 10px;
line-height: 10px;
text-align: center;
cursor: pointer;
pointer-events: auto; /* 设置关闭按钮为可点击元素 */
list-style: none; /* 无序列表前面的点不显示*/
}
}
.pop_detail {
background-color: #40669ca2;
white-space: nowrap;
pointer-events: auto;
}
}
}
@keyframes scale-in-bl {
0% {
transform: scaleY(0) translateX(-50%);
opacity: 1;
}
100% {
transform: scaleY(1) translateX(-50%);
opacity: 1;
}
}
@keyframes shutter-in-top {
0% {
transform: rotateX(-100deg);
opacity: 0;
}
100% {
transform: rotateX(0deg);
opacity: 1;
}
}
2.9. 生成导航线
- 要生成两点之间的导航线,必须在esmap图层中绘制导航线!(详情参考)。
- 通过
esmap.ESNavigation
生成。 - 该方法必须在
this.map.on('loadComplete', function () {})
中调用。
let that = this;
this.map.on('loadComplete', function () {
// 初始化导航对象
var navi = new esmap.ESNavigation({
lineStyle: { // 路径规划线样式配置
color: '#33cc61', // 导航线颜色
lineType: esmap.ESLineType.ESARROW, // 设置导航线样式
lineWidth: 3, // 设置导航线的宽度
offsetHeight: 0.5, // 设置导航线的高度
smooth: true, // 设置导航线的转角线平滑效果
seeThrough: false, // 设置导航线的穿透楼层地板总是显示的效果
noAnimate: false, // 设置导航线的动画效果
godEdgeColor: '#920000', // 设置边线的颜色
godArrowColor: '#ff0000' // 设置箭头颜色
},
});
// 获取楼层数,building实例对象生成详情可参考【2.10.1. 相关实例对象生成】
var fnum = that.building.getFloorNumByName('F1');
// 确定起点
navi.setStartPoint({
x: 12723034.833164,
y: 3579182.2929972,
fnum: fnum, // 楼层
height: 0, // 起点距离地面的高度
url: 'image/startPoint.png', // 图片路径
size: 64, // 图片尺寸
showLevel: 23 // 到达三维场景缩放等级隐藏导航对象
});
// 确定终点
navi.setEndPoint({
x: 12723057.4100803,
y: 3579213.7764181,
fnum: fnum,
height: 0,
url: 'image/endPoint.png',
size: 64,
showLevel: 23
});
navi.getRouteResult({
drawRoute: true, // 绘制导航线
})
})
2.10. 其他相关方法
2.10.1. 相关实例对象生成
- 该对象需在
map.on('loadComplete', function () {})
中生成。
let that = this;
map.on('loadComplete', function () {
that.building = map.getBuilding();
// 获取第一层的楼层对象
var floor = that.building.getFloor(1);
// 获取或者新建一个name为'mypoly'的多边形标注层,用于绘制多边形标注
that.layer = floor.getOrCreateLayerByName('mypoly', esmap.ESLayerType.POLYGON_MARKER);
})
2.10.2. 相关图层工具生成
map.showCompass = true; //显示指北针
map.showScaler = true; // 显示三维场景比例尺,默认显示
// 1.声明楼层控件配置参数
var ctlOpt = new esmap.ESControlOptions({
// LEFT_TOP: 左上方, LEFT_BOTTOM: 左下方, RIGHT_TOP: 右上方, RIGHT_BOTTOM: 右下方
position: esmap.ESControlPositon.RIGHT_TOP, // 控件放置位置
// 位置x,y的偏移量
offset: {
x: 0,
y: 0
},
textColor: '#000', // 字体颜色
toggleColor: '#1F6ED4', // 单层多层按钮和2d3d按钮颜色
bgColor: '#FFF', // 背景颜色
borderRadius: 5, // 圆角
focusTextColor: '#FFF', // 聚焦楼层字体颜色
focusBgColor: '#1F6ED4', // 聚焦楼层背景颜色
scale: 1, // 放大比例
showBtnCount: 3, // 显示按钮个数
expandBtn: true, // 显示楼层展开按钮
expandSpan: 20, // 楼层展开高度
viewModeBtn: true, // 显示2D/3D切换按钮
fnumToggleBtn: true // 显示多楼层切换按钮
});
// 2.在三维场景加载完成事件中新建楼层控制控件对象
map.on('loadComplete', function () {
var floorControl = new esmap.ESScrollFloorsControl(map, ctlOpt);
// 动态设置控件位置
floorControl.x = 0
floorControl.y = 0
})