var Map_QM, elevator, straight, elevatorDown, deviceJSON, renderFrame = -1, timeS = 0, _indexPathFloor = 0, pathShop = '', clock = new THREE.Clock() var outTime = -1, oTime = -1, language = 'cn', deviceShow = false, iconNameShow = false, forShopArr = {} //deviceShow 设备图标是否显示 iconNameShow--图标名称是否显示 var isPathPlay = true, basePath = '', graphPath = '', ftPath = '', dtPath = '', facAllArr = [] //basePath 基础路径 graphPath最佳路径 ftPath 扶梯路径 dtPath 电梯路径 var cameraPerspective, cameraOrtho, aspect, isPathState = false, pathCameraState = '2D' // ConfigFun = function () { this.imgUrl = 'http://1000my.com/MallSite' this.playSpeed = 6 //动画播放速度 this.collision = true //是否支持名称的碰撞检测 this.selectBuild = 0 this.selectFloor = 0 this.is4k = false //是否4K显示 this.showModelIcon = true //电梯扶梯是否显示3D模型 true 模型 false 图标 this.deviceObj = {} //angle --- 设备旋转角度 node ---- 设备导航点位 floor --- 设备楼层 this.startObj = {} // 导航起点; this.overObj = {} //导航结束点 this.otherPath = null //人为干预的路线 this.distance = 6000 //楼栋间距 this.mapDistance = { min: 100, max: 1000 } //限制地图缩放 this.tubeMaterial = new THREE.MeshPhongMaterial({ color: 0xb47834, transparent: true, opacity: 0.6 }) //路径材质 this.cameraDist = { x: 0, y: 220, z: 220, state: '3D' } //相机坐标 state---2D 平面 3D 立体 this.sceneGap = { x: 0, y: 0, z: 0, scale: 0.095 } //改变地图位置,大小 this.cameraZoom = 3 //设置我的方向状态地图放大倍数; this.deviceAng = false //地图初始化方向是否使用设备角度 this.showlap = false //showlap 是否叠层预览 this.mapAngle = 0 //如果不使用设备角度则此值可设置默认角度; this.perc_H = '-50%' //弹窗的偏移百分比17 或者像素 this.doubleDist = 500 //双叠层状态下楼层的间距 this.overlap = false //是否叠层 this.mapScale = 6 //地图比例尺 this.angleRadius = 2 //圆角半径 大于2 则店铺box显示圆角 this.buildHeight = 5 this.shopHeight = 50 //店铺高度 this.parkMaterial = new THREE.MeshBasicMaterial({ //MeshBasicMaterial MeshLambertMaterial MeshPhongMaterial color: 0xffffff }) //isPathState--导航状态 this.navColor = 0xee6a50 //服务icon 英文配置 this.iconEn = { 洗手间: 'Toilets', 停车场: 'Parking', 电梯: 'Lift', 扶梯: 'Escalator', 母婴室: 'Baby care room', 服务台: 'service desk' } this.initModelArr = [] // {url:"./static/model/jianfa.gltf",type:"out", scale:0.038, rot:{x:0,y:30,z:0}, site:{x:-20,y:-100,z:20}} this.modelArr = [] //一直显示不隐藏, 在楼层内显示 this.modelStr = [ //种树 { key: 'tree', url: 'static/img/model/tree.gltf', colorModel: 'line', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 1, y: 1, z: 1 } }, { key: 'tree2', url: 'static/img/model/tree2.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'huatan1', url: 'static/img/model/huatan1.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'huatan2', url: 'static/img/model/huatan2.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'penquan1', url: 'static/img/model/penquan1.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'penquan2', url: 'static/img/model/penquan2.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'qiche1', url: 'static/img/model/qiche1.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'qiche2', url: 'static/img/model/qiche2.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'qiche3', url: 'static/img/model/qiche3.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'qiche4', url: 'static/img/model/qiche4.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'qiche5', url: 'static/img/model/qiche5.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'qiche6', url: 'static/img/model/qiche6.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'qiche7', url: 'static/img/model/qiche7.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'zuoyi1', url: 'static/img/model/zuoyi1.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'zuoyi2', url: 'static/img/model/zuoyi2.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'zhuozi1', url: 'static/img/model/zhuozi1.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 30, y: 20, z: 30 } }, { key: 'zhuozi2', url: 'static/img/model/zhuozi2.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 30, y: 20, z: 30 } }, { key: 'yizi1', url: 'static/img/model/yizi1.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'gjz1', url: 'static/img/model/gjz1.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } }, { key: 'ggp', url: 'static/img/model/ggp.glb', colorModel: 'gama', rot: { x: 1.5708, y: 0, z: 0 }, size: { x: 20, y: 20, z: 20 } } ] this.fbxModels = [] //精灵模型 //标签数据 click --是否可点击 this.labelIconArr = [] this.spriteMaterialArr = [] this.lineBasicMaterialArr = [] this.meshMaterialArr = [] this.shopData = [] //店铺数据 this.allMap = [] /*** ------------------------------------------------ 参数 API START ------------------------------------------------- */ /** * @api {方法} setModelState() 模型显示状态 * @apiGroup 地图显示 * @apiDescription 设置模型显示状态 默认 3d * @apiVersion 1.0.0 * @apiParam {String} state 传入状态 2d 或 3d * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.setModelState("3d"); */ this.setModelState = function (state = '3d') { //设置模型显示状态 2d 3d if (Map_QM) { Map_QM.renderer.clear() Map_QM.changeIconState(state) } } this.exportImg = function () { let tempSrc = Map_QM.renderer.domElement.toDataURL('image/png') let a = document.createElement('a') a.href = tempSrc a.setAttribute('download', 'floor.png') a.click() } /** * @api {方法} setIconNameShow() 设施名称显隐 * @apiGroup 地图显示 * @apiDescription 设置设施名称显示或隐藏 * @apiVersion 1.0.0 * @apiParam {boolean} isShow 是否显示 true显示 false隐藏 * * @apiSampleRequest off * @apiParamExample {boolean} 请求示例 * * Config.setIconNameShow(true); */ this.setIconNameShow = function (isShow) { iconNameShow = isShow } /** * @api {方法} setPlaySpeed() 改变导航速度 * @apiGroup 地图导航 * @apiDescription 改变导航播放速度,范围 2-20 默认是6 * @apiVersion 1.0.0 * @apiParam {int} speed 播放速度 * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.setPlaySpeed(8); */ this.setPlaySpeed = function (sp) { this.playSpeed = parseInt(sp) } //设置弹窗偏离位置 默认 -50% 范围 -100% 100% this.setPercH = function (perc = '-50%') { this.perc_H = perc } /** * @api {方法} setDeviceShow() 设备显示状态 * @apiGroup 地图显示 * @apiDescription 设置设备的显示状态 默认false * @apiVersion 1.0.0 * @apiParam {boolean} value 是否显示 * * @apiSampleRequest off * @apiParamExample {boolean} 请求示例 * * Config.setDeviceShow(true); */ this.setDeviceShow = function (value) { deviceShow = value } // /** * @api {方法} setCollision() 设置碰撞检测 * @apiGroup 地图显示 * @apiDescription 设置box名称是否支持碰撞检测 默认支持 * @apiVersion 1.0.0 * @apiParam {boolean} value 是否支持 * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.setCollision(true); */ this.setCollision = function (value) { this.collision = value } /** * @api {方法} setShowModelIcon() 公共设施模型 * @apiGroup 地图显示 * @apiDescription 设置公共设施模型效果 true 3D模型 false icon显示 * @apiVersion 1.0.0 * @apiParam {boolean} value 显示状态 * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.setShowModelIcon(true); */ this.setShowModelIcon = function (value) { this.showModelIcon = value } /** * @api {方法} setSceneGap() 地图模型展示 * @apiGroup 地图显示 * @apiDescription 设置公共设施模型效果 x/y/z 模型中心点位置 * @apiVersion 1.0.0 * @apiParam {Number} x 水平方向位置,负数左移,正数右移 * @apiParam {Number} y 垂直方向位置,负数下移,正数上移 * @apiParam {Number} z 远近位置,默认为0 * @apiParam {Number} scale 缩放默认为0.1 * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.setSceneGap({x:0,y:0,z:0,scale:0.1}); */ this.setSceneGap = function (value) { this.sceneGap = value } //设置其它可行路径 this.setOtherPath = function (value) { this.otherPath = value } /** * 是否所有楼层叠层显示 * @param {boolean} value */ this.setShowLap = function (value) { Config.showlap = value Map_QM.showFloor(Config.deviceObj.floor) } /** * @api {方法} setSceneGapInit() 地图初始参数 * @apiGroup 地图数据 * @apiDescription 设置地图初始位置、大小 * @apiVersion 1.0.0 * @apiParam {int} x X轴上位移 * @apiParam {int} y Y轴上位移 * @apiParam {int} z Z轴上位移 * @apiParam {Number} scale 地图缩放比例 * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.setSceneGapInit({x:-100,y:0,z:0,scale:0.1}); */ this.setSceneGapInit = function (value) { this.sceneGap = value } // /** * @api {方法} setLabelIconArr() 设置外部标签 * @apiGroup 地图数据 * @apiDescription 设置外部标签 * @apiVersion 1.0.0 * @apiParam {Array} value 标签数组 * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.setLabelIconArr([{floor:0,title:'', click:false, site:{x:0,y:1870,z:10},data:{type:"labelIcon",id:"1",show:"cn"}}]); */ this.setLabelIconArr = function (value) { this.labelIconArr = value } /** * @api {方法} setModelArr() 设置外部模型 * @apiGroup 地图数据 * @apiDescription 设置外部模型 * @apiVersion 1.0.0 * @apiParam {Array} value 模型数组 * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.setModelArr({floor:0,url:'./static/img/model/yj.gltf',list:[{site:{x :0, y :0, z :0}, rot:{x :1.5708, y :0, z :0}, size:{x :1, y :1, z :1}} ]}); */ this.setModelArr = function (value) { this.modelArr = value } /** * @api {方法} getInstance() 初始化方法 * @apiGroup 地图数据 * @apiDescription 地图数据初始化 (可重复调用) * @apiVersion 1.0.0 * @apiParam {Function} callBack 回调函数 * @apiParam {int} build 导航起点楼栋编号 * @apiParam {int} floor 导航起点楼层编号 * @apiParam {int} navPoint 导航起点点位编号 传 -1 不显示起点 * @apiParam {int} angle 导航起点初始角度 * @apiParam {String} mallCode 商场Code (可选) * @apiParam {String} _url 云地址 (可选) * * @apiSampleRequest off * @apiParamExample {String} 请求示例 * * Config.getInstance(,0,1,1,0,"993f60c6-fef5-4999-9c3e-a4f71eb48364","http://122.112.233.82/mall"); * * @apiSuccess {int} code 状态码 200-正常;404-地图无法显示;500-地图关联数据错误 * @apiSuccess {String} msg 状态提示信息 * @apiSuccess {Array} data 返回楼层数据 * @apiSuccessExample {json} success-example * { * "code": 200, * "msg": "加载成功" * "data": [] * } */ this.getInstance = function (callBack, build = 0, floor = 0, navPoint = 1, angle = 0, mapData, shopData) { Config.startObj.build = Config.deviceObj.build = parseInt(build) || 0 Config.startObj.floor = Config.deviceObj.floor = parseInt(floor) || 0 Config.startObj.node = Config.deviceObj.node = parseInt(navPoint) || 1 Config.deviceObj.angle = parseInt(angle) || 0 Config.selectFloor = Config.deviceObj.floor Config.selectBuild = Config.deviceObj.build let backObj = { code: 200, msg: '加载成功', data: [] } if (mapData) { try { if (mapData.mallKey != 'Zeditor') { Config.allMap = JSON.parse(mapData.mapData) } else { Config.allMap = JSON.parse(LZString.decompressFromBase64(mapData.mapData)) } } catch (e) { console.log(e) backObj.code = 404 backObj.msg = '地图数据JSON格式错误' callBack(backObj) callBack = null } Config.getShopData(callBack, shopData) } } /*** ----------------------------------------------- 参数 API END ----------------------------------------------- **** */ this.Point = function (x = 0, y = 0) { this.x = x this.y = y } this.WallLine = function (start, end) { this.start = start //起始点位 this.end = end //结束点位 this.leftParLine //左侧平行线段 this.rightParLine //右侧平行线段 this.leftPoint //左侧平行线交点 this.rightPoint //右侧平行线交点 } /** * 检测点是否在多边形区域内 */ this.checkBoundary = function (p, ptPolygon) { // 判断边界方法 let nCount = ptPolygon.length let nCross = 0 for (let i = 0; i < nCount; i++) { let p1 = ptPolygon[i] //当前节点 let p2 = ptPolygon[(i + 1) % nCount] //下一个节点 // 求解 y=p.y 与 p1p2 的交点 if (p1.y == p2.y) // p1p2 与 y=p0.y平行 continue if (p.y < Math.min(p1.y, p2.y)) // 交点在p1p2延长线上 continue if (p.y >= Math.max(p1.y, p2.y)) // 交点在p1p2延长线上 continue // 从P发射一条水平射线 求交点的 X 坐标 ------原理: ((p2.y-p1.y)/(p2.x-p1.x))=((y-p1.y)/(x-p1.x)) //直线k值相等 交点y=p.y let x = ((p.y - p1.y) * (p2.x - p1.x)) / (p2.y - p1.y) + p1.x if (x > p.x) nCross++ // 只统计单边交点 } // 单边交点为偶数,点在多边形之外 --- return nCross % 2 == 1 } //加密 this.encrypt = function (word, keyStr) { keyStr = keyStr ? keyStr : '0123456789QMSaas' var key = CryptoJS.enc.Utf8.parse(keyStr) //Latin1 w8m31+Yy/Nw6thPsMpO5fg== var srcs = CryptoJS.enc.Utf8.parse(word) var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }) return encrypted.toString() } //解密 this.decrypt = function (word, keyStr) { keyStr = keyStr ? keyStr : '0123456789QMSaas' var key = CryptoJS.enc.Utf8.parse(keyStr) //Latin1 w8m31+Yy/Nw6thPsMpO5fg== var decrypt = CryptoJS.AES.decrypt(word, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }) return CryptoJS.enc.Utf8.stringify(decrypt).toString() } this.timeStamp = function () { return parseInt(new Date().getTime() / 1000) } this.readTextFile = function (file, callback) { let rawFile = new XMLHttpRequest() rawFile.overrideMimeType('application/json') rawFile.open('GET', file, true) rawFile.onreadystatechange = function () { if (rawFile.readyState === 4 && rawFile.status == 200) { try { let jsonObject = JSON.parse(rawFile.response) callback(jsonObject) } catch (e) { //console.log(e); } } } rawFile.onerror = function () { callBack(null) } rawFile.send(null) } //判断点是fou顺时针 this.isClockwise = function (polygon) { let len = polygon.length let su = 0, ni = 0 for (let i = 0; i < len; i++) { let p = polygon[i] let p1 = polygon[i == 0 ? len - 1 : i - 1] let p2 = polygon[i == len - 1 ? 0 : i + 1] let vx1 = p1.x - p.x let vy1 = p1.y - p.y let vx2 = p2.x - p.x let vy2 = p2.y - p.y // 负值多是顺时针,正值多是逆时针 if (vx1 * vy2 + -1 * vy1 * vx2 < 0) { su++ } else { ni++ } return su > ni } } this.blocked = (dom, checkList, cfloor) => { const { top, left, right, bottom } = dom.getBoundingClientRect() let corners = [ [left, top], [right, top], [left, bottom], [right, bottom] ] for (let i = 0; i < corners.length; i++) { const [x, y] = corners[i] const raycaster = new THREE.Raycaster() const p = new THREE.Vector2() let ele = document.getElementById('mapContainer') p.x = (x / parseInt(ele.clientWidth)) * 2 - 1 p.y = -(y / parseInt(ele.clientHeight)) * 2 + 1 raycaster.setFromCamera(p, Map_QM.camera) let intersects = raycaster.intersectObjects(checkList) for (let t = 0; t < intersects.length; t++) { if (intersects[t].object.userData.order > cfloor) { return true } } } return false } //绘制3D线 传入起点(3D坐标)、终点(3D坐标)、控制点1、控制点2(如没有则绘制直线) this.drawDashedLine = function (startPoint, endPoint, dash = 50, color = 0x2269dd, ctrlPoint1 = null, ctrlPoint2 = null) { //THREE.Vector3 let gopObj = new THREE.Group() let curve if (!ctrlPoint1 && !ctrlPoint2) { //控制点为空 ctrlPoint1 = new THREE.Vector3( startPoint.x + (endPoint.x - startPoint.x) / 4, startPoint.y + (endPoint.y - startPoint.y) / 4, startPoint.z + (endPoint.z - startPoint.z) / 4 ) ctrlPoint2 = new THREE.Vector3( endPoint.x + (startPoint.x - endPoint.x) / 4, endPoint.y + (startPoint.y - endPoint.y) / 4, endPoint.z + (startPoint.z - endPoint.z) / 4 ) } curve = new THREE.CubicBezierCurve3(startPoint, ctrlPoint1, ctrlPoint2, endPoint) let points = curve.getPoints(dash) let array = [] points.forEach(element => { array.push(element.x, element.y, element.z) }) let geometry = new THREE.LineSegmentsGeometry() // 几何体传入顶点坐标 geometry.setPositions(array) // 自定义的材质 let material = new THREE.LineMaterial({ color: color, linewidth: 4, scale: 1, dashSize: 1, gapSize: 2 }) // 把渲染窗口尺寸分辨率传值给材质LineMaterial的resolution属性 // resolution属性值会在着色器代码中参与计算 material.resolution.set(window.innerWidth, window.innerHeight) let mesh = new THREE.Mesh(geometry, material) gopObj.add(mesh) let jtArr = [] if (Math.abs(endPoint.x - startPoint.x) > Math.abs(endPoint.y - startPoint.y)) { jtArr.push(endPoint.x - 10, endPoint.y, endPoint.z + 10) jtArr.push(endPoint.x, endPoint.y, endPoint.z) jtArr.push(endPoint.x + 10, endPoint.y, endPoint.z + 10) jtArr.push(endPoint.x, endPoint.y, endPoint.z) jtArr.push(endPoint.x, endPoint.y, endPoint.z + 10) } else { jtArr.push(endPoint.x, endPoint.y - 10, endPoint.z + 10) jtArr.push(endPoint.x, endPoint.y, endPoint.z) jtArr.push(endPoint.x, endPoint.y + 10, endPoint.z + 10) jtArr.push(endPoint.x, endPoint.y, endPoint.z) jtArr.push(endPoint.x, endPoint.y, endPoint.z + 10) } let jtgeom = new THREE.LineGeometry() jtgeom.setPositions(jtArr) let jtmesh = new THREE.Mesh(jtgeom, material) gopObj.add(jtmesh) gopObj.userData.type = 'toLine' return gopObj } /**碰撞检测 * 传入A中心点和A的宽、高 * B的中心点和B的宽、高 */ this.isCollision = function (A, aW, aH, B, bW, bH) { let noCol = false if (Math.abs(A.x - B.x) < (aW + bW) / 2 + 20 && Math.abs(A.y - B.y) < (aH + bH) / 2 + 10) { noCol = true } return noCol } this.changeParkToString = function (area) { let areaArr = [] for (let i = 0; i < area.hasLines.length; i++) { let line = area.hasLines[i] let array = [] array.push(line.startPoint.x, line.startPoint.y, line.endPoint.x, line.endPoint.y) areaArr.push(array) } return areaArr } this.changeAreaToString = function (area) { let areaArr = [] for (let i = 0; i < area.hasLines.length; i++) { let line = area.hasLines[i] let array = [] if (line.isStrLine) { array.push(line.startPoint.x, line.startPoint.y, line.endPoint.x, line.endPoint.y) } else { array.push(line.startPoint.x, line.startPoint.y, line.ctrlPoint1.x, line.ctrlPoint1.y, line.ctrlPoint2.x, line.ctrlPoint2.y, line.endPoint.x, line.endPoint.y) } areaArr.push(array) } return areaArr } this.changeWallToString = function (area) { let areaArr = [] let points = Config.getWallPoints(area.pathPoints, area.thick) for (let i = 0; i < points.length; i++) { let array = [] let pend = i == points.length - 1 ? points[0] : points[i + 1] array.push(points[i].x, points[i].y, pend.x, pend.y) areaArr.push(array) } return areaArr } this.QM_Line_Father = function (sPoint, ePoint, ctrlPoint1, ctrlPoint2, isStrLine) { this.startPoint = sPoint //起始点 this.endPoint = ePoint //结束点 this.ctrlPoint1 = ctrlPoint1 this.ctrlPoint2 = ctrlPoint2 this.isStrLine = isStrLine //是否是直线 } //根据配置参数转换店铺圆角 this.changeShopLinesToString = function (area) { let areaStr = [] let lines = [] for (let m = 0; m < area.hasLines.length; m++) { let sPoint, ePoint, cPoint1, cPoint2 sPoint = new Config.Point(area.hasLines[m].startPoint.x, area.hasLines[m].startPoint.y) ePoint = new Config.Point(area.hasLines[m].endPoint.x, area.hasLines[m].endPoint.y) if (area.hasLines[m].isStrLine) { cPoint1 = null cPoint2 = null } else { cPoint1 = new Config.Point(area.hasLines[m].ctrlPoint1.x, area.hasLines[m].ctrlPoint1.y) cPoint2 = new Config.Point(area.hasLines[m].ctrlPoint2.x, area.hasLines[m].ctrlPoint2.y) } let line = new Config.QM_Line_Father(sPoint, ePoint, cPoint1, cPoint2, area.hasLines[m].isStrLine) lines.push(line) } for (let i = 0; i < lines.length; i++) { let line0 = lines[i] let line1 = i < lines.length - 1 ? lines[i + 1] : lines[0] if (Config.angleRadius > 2) { if ( line0.isStrLine && line1.isStrLine && Math.abs(line0.endPoint.x - line0.startPoint.x) + Math.abs(line0.endPoint.y - line0.startPoint.y) > parseInt(Config.angleRadius) * 2 ) { let x1 = line0.endPoint.x let y1 = line0.endPoint.y let x2 = line0.startPoint.x let y2 = line0.startPoint.y let x3 = line1.endPoint.x let y3 = line1.endPoint.y if (Math.abs((x3 - x1) / (x2 - x1) - (y3 - y1) / (y2 - y1)) < 0.1) { let yArr = [] yArr.push(line0.startPoint.x, line0.startPoint.y, line0.endPoint.x, line0.endPoint.y) areaStr.push(yArr) continue } let result = Config.getIncircleByLines(x1, y1, x2, y2, x3, y3, Config.angleRadius) let bezierResult = Config.getBezier( result.center.x, result.center.y, result.tangencyPoints[0].x, result.tangencyPoints[0].y, result.tangencyPoints[1].x, result.tangencyPoints[1].y, x1, y1, Config.angleRadius ) if (i > 0) { let ctrlPoint1, ctrlPoint2, array = [] ctrlPoint1 = ctrlPoint2 = new Config.Point( ((bezierResult[0].x - line0.startPoint.x) / 2 + line0.startPoint.x) >> 0, ((bezierResult[0].y - line0.startPoint.y) / 2 + line0.startPoint.y) >> 0 ) //控制点 array.push(line0.startPoint.x, line0.startPoint.y, ctrlPoint1.x, ctrlPoint1.y, ctrlPoint2.x, ctrlPoint2.y, bezierResult[0].x, bezierResult[0].y) areaStr.push(array) } else { lines[0].endPoint.x = bezierResult[0].x lines[0].endPoint.y = bezierResult[0].y } let arr = [] arr.push(bezierResult[0].x, bezierResult[0].y, bezierResult[1].x, bezierResult[1].y, bezierResult[2].x, bezierResult[2].y, bezierResult[3].x, bezierResult[3].y) areaStr.push(arr) line1.startPoint.x = bezierResult[3].x line1.startPoint.y = bezierResult[3].y } else { ///////////////////////////// if (i != 0) { let pArr = [] if (line0.isStrLine) { pArr.push(line0.startPoint.x, line0.startPoint.y, line0.endPoint.x, line0.endPoint.y) } else { pArr.push( line0.startPoint.x, line0.startPoint.y, line0.ctrlPoint1.x, line0.ctrlPoint1.y, line0.ctrlPoint2.x, line0.ctrlPoint2.y, line0.endPoint.x, line0.endPoint.y ) } areaStr.push(pArr) } } if (i == lines.length - 1) { let ocPoint1, ocPoint2, oArr = [] if (line1.isStrLine) { oArr.push(line1.startPoint.x, line1.startPoint.y, line1.endPoint.x, line1.endPoint.y) } else { ocPoint1 = new Config.Point(line1.ctrlPoint1.x, line1.ctrlPoint1.y) ocPoint2 = new Config.Point(line1.ctrlPoint2.x, line1.ctrlPoint2.y) oArr.push(line1.startPoint.x, line1.startPoint.y, ocPoint1.x, ocPoint1.y, ocPoint2.x, ocPoint2.y, line1.endPoint.x, line1.endPoint.y) } areaStr.push(oArr) } } else { let yArr = [] if (line0.isStrLine) { yArr.push(line0.startPoint.x, line0.startPoint.y, line0.endPoint.x, line0.endPoint.y) } else { yArr.push( line0.startPoint.x, line0.startPoint.y, line0.ctrlPoint1.x, line0.ctrlPoint1.y, line0.ctrlPoint2.x, line0.ctrlPoint2.y, line0.endPoint.x, line0.endPoint.y ) } areaStr.push(yArr) } } return areaStr } //根据圆心、两个切点、切点相交线顶点和半径 计算三次贝塞尔曲线的控制点 this.getBezier = function (x1, y1, x2, y2, x3, y3, x4, y4, radius) { //切线向量A var vectorAx = x2 - x1 var vectorAy = y2 - y1 //切线向量B var vectorBx = x3 - x1 var vectorBy = y3 - y1 //计算切点和圆形组成相交线的夹角 var angle = Math.acos( (vectorAx * vectorBx + vectorAy * vectorBy) / (Math.sqrt(vectorAx * vectorAx + vectorAy * vectorAy) * Math.sqrt(vectorBx * vectorBx + vectorBy * vectorBy)) ) //计算切点到控制点的距离 var tempDistence = (4 / 3) * radius * Math.tan(angle / 4) return [ { x: x2, y: y2 }, Config.getPointFromLine(x2, y2, x4, y4, tempDistence), Config.getPointFromLine(x3, y3, x4, y4, tempDistence), { x: x3, y: y3 } ] } //根据半径计算两条线段相切圆的圆心和切点坐标 this.getIncircleByLines = function (x1, y1, x2, y2, x3, y3, radius) { //向量夹角 let angle = Config.getVectorAngle(x2 - x1, y2 - y1, x3 - x1, y3 - y1) angle = (angle > 180 ? 360 - angle : angle) / 2 //根据夹角计算侧边切点相对于顶点距离 let distance = radius / Math.tan((Math.PI * angle) / 180) //计算侧边相切点具体坐标 let tangencyPoints = [Config.getPointFromLine(x1, y1, x2, y2, distance), Config.getPointFromLine(x1, y1, x3, y3, distance)] let centerX, centerY let areaSize = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1) //计算圆心坐标 if (areaSize < 0) { centerX = (tangencyPoints[0].x * (1 / Math.tan((Math.PI * angle) / 180)) + tangencyPoints[0].y - y1) / (1 / Math.tan((Math.PI * angle) / 180)) centerY = (tangencyPoints[0].y * (1 / Math.tan((Math.PI * angle) / 180)) + x1 - tangencyPoints[0].x) / (1 / Math.tan((Math.PI * angle) / 180)) } else { centerX = (tangencyPoints[1].x * (1 / Math.tan((Math.PI * angle) / 180)) + tangencyPoints[1].y - y1) / (1 / Math.tan((Math.PI * angle) / 180)) centerY = (tangencyPoints[1].y * (1 / Math.tan((Math.PI * angle) / 180)) + x1 - tangencyPoints[1].x) / (1 / Math.tan((Math.PI * angle) / 180)) } return { center: { x: centerX, y: centerY }, tangencyPoints: tangencyPoints, angle: (Math.PI * angle) / 90 } } //根据距离计算线段上某一点的具体坐标 this.getPointFromLine = function (startX, startY, endX, endY, distance) { if (startX == endX) return { x: startX, y: startY < endY ? startY + distance : startY - distance } let k = ((startY - endY) * 1.0) / (startX - endX) let b = startY - k * startX let A = Math.pow(k, 2) + 1 let B = 2 * ((b - startY) * k - startX) let C = Math.pow(b - startY, 2) + Math.pow(startX, 2) - Math.pow(distance, 2) let x1 = (-B + Math.sqrt(Math.pow(B, 2) - 4 * A * C)) / (2 * A) let x2 = (-B - Math.sqrt(Math.pow(B, 2) - 4 * A * C)) / (2 * A) let x = 0 if (x1 == x2) x = x1 else if ((startX <= x1 && x1 <= endX) || (endX <= x1 && x1 <= startX)) x = x1 else if ((startX <= x2 && x2 <= endX) || (endX <= x2 && x2 <= startX)) x = x2 let y = k * x + b return { x: x, y: y } } //计算两个向量之间的夹角 this.getVectorAngle = function (x1, y1, x2, y2) { let epsilon = 1.0e-6 let dist, dot, degree, angle dist = Math.sqrt(x1 * x1 + y1 * y1) x1 /= dist y1 /= dist dist = Math.sqrt(x2 * x2 + y2 * y2) x2 /= dist y2 /= dist dot = x1 * x2 + y1 * y2 if (Math.abs(dot - 1.0) <= epsilon) angle = 0 else if (Math.abs(dot + 1.0) <= epsilon) angle = Math.PI else { angle = Math.acos(dot) let cross = x1 * y2 - x2 * y1 if (cross < 0) angle = 2 * Math.PI - angle } degree = (angle * 180) / Math.PI return degree } //检测区域是否在区域内 true (area2包含area) this.checkAreaInArea = function (area, area2) { let ptPolygon = [] for (let i = 0; i < area2.hasLines.length; i++) { let line = area2.hasLines[i] let pArr if (line.isStrLine) { pArr = Config.getPointArrOnLine(line.startPoint, line.endPoint) } else { pArr = Config.getPointArr(line.startPoint, line.ctrlPoint1, line.ctrlPoint2, line.endPoint, 0.1) } ptPolygon.push(...pArr) } for (let f = 0; f < area.hasLines.length; f++) { let line2 = area.hasLines[f] let sPoint = Config.checkBoundary(new Config.Point(line2.startPoint.x, line2.startPoint.y), ptPolygon) let ePoint = Config.checkBoundary(new Config.Point(line2.endPoint.x, line2.endPoint.y), ptPolygon) if (!sPoint || !ePoint) { return false } } return true } /** * 返回取得点的数组 * s1--起点 s2 --终点 s3,s4 --控制点 */ this.getPointArr = function (s1, s3, s4, s2, sp = 0.01) { let pArr = [] let sz = [s1, s3, s4, s2] let p = Config.P_BEZ(0, sz) for (let j = 0; j < 1; j += sp) { p = Config.P_BEZ(j, sz) pArr.push(p) } return pArr } this.P_BEZ = function (t, sz) { //n次 let x_p = 0 let y_p = 0 let n = sz.length for (let i = 0; i < sz.length; i++) { let son = Config.jie_cheng(n - 1) let mother = Config.jie_cheng(i) * Config.jie_cheng(n - 1 - i) let b = (son / mother) * Math.pow(t, i) * Math.pow(1 - t, n - 1 - i) x_p += sz[i].x * b y_p += sz[i].y * b } x_p = Number(x_p * 1000) / 1000 y_p = Number(y_p * 1000) / 1000 return new Config.Point(x_p, y_p) } this.jie_cheng = function (i) { //阶乘 let n = 1 for (let j = 1; j <= i; j++) { n *= j } return n } /** * 获取线段上的所有点 */ this.getPointArrOnLine = function (s1, s2) { let points = [] if (s1.x == s2.x) { let vy = s1.y < s2.y ? 1 : -1 for (let m = 1; m < Math.abs(s1.y - s2.y); m++) { let y0 = s1.y + m * vy let x0 = s1.x points.push(new Config.Point(x0, y0)) } return points } let k = (s1.y - s2.y) / (s1.x - s2.x) // 坐标直线斜率k let b = s1.y - k * s1.x // 坐标直线b if (Math.abs(s1.x - s2.x) > Math.abs(s1.y - s2.y)) { let vx = s1.x < s2.x ? 1 : -1 for (let i = 1; i < Math.abs(s1.x - s2.x); i++) { let x0 = s1.x + i * vx let y0 = k * x0 + b points.push(new Config.Point(x0, y0)) } } else { let vy = s1.y < s2.y ? 1 : -1 for (let n = 1; n < Math.abs(s1.y - s2.y); n++) { let y0 = s1.y + n * vy let x0 = (y0 - b) / k points.push(new Config.Point(x0, y0)) } } return points } //转换公共设施type值 this.getFacType = function (str) { let typeObj = { ft: 0, mys: 3, xsj: 4, dt: 5, fwt: 7, tcc: 8, cjr: 10, xys: 11, ztg: 17, thg: 18, td: 20, dit: 21, czc: 22, atm: 23, jcfw: 24, sjcd: 25, bc: 26, cjc: 27, jtn: 28, jtv: 29, ksgj: 30, sjxsn: 31, sjxsv: 32, tcjf: 33, vip: 34, xsjn: 35, xsjv: 36, yszj: 37, xxt: 38, door: 39, pq: 40, upft: 0, downft: 0, ysp: 50, B1up: 51, B1down: 52, B2up: 53, B2down: 54, B3up: 55, B3down: 56, lt: 88, xcgc: 57, tthy: 58, fwt2: 60, syt2: 61, syt1: 62, gwc: 63, fwt1: 64, jrc: 65, qbc: 66, zxc: 67, jws: 68, etxsj: 69, vip_xxq: 70, ab: 71, abjks: 72, bys: 73, cpgys: 74, gzyld: 75, hqgys: 76, jjs: 77, jw: 78, ksj: 79, kt: 80, qzgysn: 81, rsggys: 82, swzl: 83, tsgbs1: 84, tsgbs2: 85, tsgbs3: 86, wxc: 87, yhs: 89, yls: 90, ylz: 91, ydygys: 92, ydygysn: 93, ydygysv: 94, zls: 95, zys: 96, zas: 97, xxq: 98, jtxsj: 99, hzs: 100, brs: 101, mtl: 102, dgnxsj: 103, wxbxsj: 104 } return typeObj[str] } this.getFacName = function (str) { let typeObj = { ft: '扶梯', mys: '母婴室', xsj: '洗手间', dt: '直梯', fwt: '服务台', tcc: '停车场', cjr: '无障碍洗手间', xys: '吸烟室', dit: '地铁', czc: '出租车', atm: 'ATM', jcfw: '寄存服务', td: '人行通道', sjcd: '手机充电', bc: '泊车', cjc: '裁剪处', jtn: '家庭洗手间(男)', jtv: '家庭洗手间(女)', ksgj: '公交', sjxsn: '男伤健人士洗手间', sjxsv: '女伤健人士洗手间', tcjf: '停车缴费', vip: 'VIP', xsjn: '男洗手间', xsjv: '女洗手间', yszj: '雨伞租借', xxt: '信息台', door: '出入口', pq: '喷泉', upft: '上扶梯', downft: '下扶梯', ysp: '艺术品', lt: '楼梯', xcgc: '下沉广场', tthy: '天台花园', fwt2: '超市服务台', syt2: '超市收银台', syt1: 'mall收银台', gwc: '购物车', fwt1: 'mall服务台', jrc: '自助加热', qbc: '自助取冰', zxc: '自行车停放', jws: '警务室', etxsj: '儿童洗手间', vip_xxq: 'vip休息区', ab: '安保', abjks: '安保监控室', bys: '播音室', cpgys: '裁判更衣室', gzyld: '观众医疗点', hqgys: '后勤更衣室', jjs: '急救室', jw: '警卫', ksj: '开水间', kt: '看台', qzgysn: '亲自更衣(男)', rsggys: '热身馆更衣室', swzl: '失物招领', tsgbs1: '特殊贵宾室1', tsgbs2: '特殊贵宾室2', tsgbs3: '特殊贵宾室3', wxc: '闻讯处', yhs: '医护室', yls: '医疗室', ylz: '医疗站', ydygys: '运动员更衣室', ydygysn: '运动员男更衣室', ydygysv: '运动员女更衣室', zls: '诊疗室', zys: '直饮水', zas: '治安室', xxq: '休息区', jtxsj: '家庭洗手间', hzs: '化妆室', brs: '哺乳室', mtl: '摩天轮', dgnxsj: '多功能洗手间', wxbxsj: '无性别洗手间', ztg: 'L1自提柜', thg: 'B1自提柜' } return typeObj[str] } this.parkSort = function (a, b) { return a.name < b.name ? -1 : 1 } //店铺排序 this.sortShopByFloor = function (a, b) { return a.floorOrder < b.floorOrder ? -1 : 1 } //店铺排序 this.sortHouseNum = function (a, b) { return a.houseNumber < b.houseNumber ? -1 : 1 } this.sortNode = function (a, b) { return a.id - b.id } /**该方法用来绘制一个圆角矩形 *@param cxt:canvas的上下文环境 *@param x:左上角x轴坐标 *@param y:左上角y轴坐标 *@param width:矩形的宽度 *@param height:矩形的高度 *@param radius:圆的半径 **/ this.drawRoundRectPath = function (cxt, width, height, radius) { cxt.beginPath(0) //从右下角顺时针绘制,弧度从0到1/2PI cxt.arc(width - radius, height - radius, radius, 0, Math.PI / 2) //矩形下边线 cxt.lineTo(radius, height) //左下角圆弧,弧度从1/2PI到PI cxt.arc(radius, height - radius, radius, Math.PI / 2, Math.PI) //矩形左边线 cxt.lineTo(0, radius) //左上角圆弧,弧度从PI到3/2PI cxt.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2) //上边线 cxt.lineTo(width - radius, 0) //右上角圆弧 cxt.arc(width - radius, radius, radius, (Math.PI * 3) / 2, Math.PI * 2) //右边线 cxt.lineTo(width, height - radius) cxt.closePath() } this.wordToSreen = function (world_vector) { let vector = world_vector.project(Map_QM.camera) let halfWidth = window.innerWidth / 2, halfHeight = window.innerHeight / 2 return { x: Math.round(vector.x * halfWidth + halfWidth), y: Math.round(-vector.y * halfHeight + halfHeight) } } //////////////////////////////////////////////////////////////////////////////////////////// this.getWallPoints = function (points, wallWidth) { if (points.length < 2) { //console.log("getWallPoints", "points size is letter than 2"); return new Array() } //构建线段列表 let lines = new Array() for (let index = 0; index < points.length - 1; index++) { let startPoint = points[index] let endPoint = points[index + 1] let line = Config.getParallelLine(startPoint, endPoint, wallWidth) lines.push(line) } //生成线段对应的左右两侧平行线 for (let index = 0; index < lines.length - 1; index++) { let start = lines[index] let end = lines[index + 1] if (start.leftParLine != null && start.rightParLine != null && end.leftParLine != null && end.rightParLine != null) { start.leftPoint = Config.getIntersectionByLines(start.leftParLine, end.leftParLine) start.rightPoint = Config.getIntersectionByLines(start.rightParLine, end.rightParLine) } } //循环线段列表 获取墙体所有点位 顺序为 左侧起始点->左侧所有交点->左侧结束点->右侧结束点->右侧所有交点->右侧起始点 let leftPointList = new Array() let rightPointList = new Array() for (let index = 0; index < lines.length; index++) { //第一条线段 记录左右两侧平行线的起点坐标 if (index == 0) { leftPointList.push(lines[index].leftParLine.start) rightPointList.push(lines[index].rightParLine.start) } //最后一条线段 记录左右两侧平行线的终点坐标 if (index == lines.length - 1) { leftPointList.push(lines[index].leftParLine.end) rightPointList.push(lines[index].rightParLine.end) } else { //记录线段左右平行线交点坐标 if (!isNaN(lines[index].leftPoint.x) || !isNaN(lines[index].leftPoint.y) || !isNaN(lines[index].rightPoint.x) || !isNaN(lines[index].rightPoint.y)) { leftPointList.push(lines[index].leftPoint) rightPointList.push(lines[index].rightPoint) } } } rightPointList.reverse() return leftPointList.concat(rightPointList) } //生成线段左右两侧的平行线 this.getParallelLine = function (start, end, wallWidth) { let line = new Config.WallLine(start, end) //计算当前线段的斜率 let gradient = (start.y - end.y) / (start.x - end.x) //计算垂直线的斜率 let perGradient = -1 / gradient //获取垂直线上左右两侧 与当前点位相距一定距离的两个定点 let startResult = Config.getParallelPoints(perGradient, start, wallWidth) let endResult = Config.getParallelPoints(perGradient, end, wallWidth) let x1 = startResult[0].x let y1 = startResult[0].y let x2 = endResult[0].x let y2 = endResult[0].y let x3 = end.x let y3 = end.y let x4 = startResult[1].x let y4 = startResult[1].y let x5 = endResult[1].x let y5 = endResult[1].y let s = (x1 - x3) * (y2 - y3) - (y1 - y3) * (x2 - x3) //判断点位位于线段的左侧还是右侧 if (s >= 0) { line.leftParLine = new Config.WallLine(new Config.Point(x1, y1), new Config.Point(x2, y2)) line.rightParLine = new Config.WallLine(new Config.Point(x4, y4), new Config.Point(x5, y5)) } else { line.leftParLine = new Config.WallLine(new Config.Point(x4, y4), new Config.Point(x5, y5)) line.rightParLine = new Config.WallLine(new Config.Point(x1, y1), new Config.Point(x2, y2)) } return line } // 生成线段起始 和 结束 点位 对应的 两条 与线段垂直的直线 并记录坐标 this.getParallelPoints = function (gradient, point, wallWidth) { let x, y //斜率为无穷大时 计算不了垂直线 指定点位 if (gradient == Number.NEGATIVE_INFINITY || gradient == Number.POSITIVE_INFINITY) { x = point.x y = point.y + 5 } else { //不是横线时 根据斜率计算点位 x = point.x + 5 y = parseInt(gradient * (x - point.x) + point.y) } return Config.pointXY(point, new Config.Point(x, y), wallWidth / 2) } // 获取点位在直线上的坐标 this.pointXY = function (curPoint, nextPoint, length) { let result = new Array() //x值相等 说明是竖线 只需增减y轴坐标 if (curPoint.x == nextPoint.x) { result.push(new Config.Point(curPoint.x, curPoint.y + length)) result.push(new Config.Point(curPoint.x, curPoint.y - length)) return result } //根据 斜率 和 距离 计算出对应的两个点位 let k = (curPoint.y - nextPoint.y) / (curPoint.x - nextPoint.x) let b = curPoint.y - k * curPoint.x let A = Math.pow(k, 2) + 1 let B = 2 * ((b - curPoint.y) * k - curPoint.x) let C = Math.pow(b - curPoint.y, 2) + Math.pow(curPoint.x, 2) - Math.pow(length, 2) let x1 = (-B + Math.sqrt(Math.pow(B, 2) - 4 * A * C)) / (2 * A) let x2 = (-B - Math.sqrt(Math.pow(B, 2) - 4 * A * C)) / (2 * A) result.push(new Config.Point(parseInt(x1), parseInt(k * x1 + b))) result.push(new Config.Point(parseInt(x2), parseInt(k * x2 + b))) return result } //计算两条直线的相交点 this.getIntersectionByLines = function (line1, line2) { //直线斜率 let gradient1 = (line1.end.y - line1.start.y) / (line1.end.x - line1.start.x) let gradient2 = (line2.end.y - line2.start.y) / (line2.end.x - line2.start.x) //斜率差值小于一定范围 表示两条线近似平行 因为交点太远 可能超出屏幕 直接取线段中点为交点 if (Math.abs(gradient1 - gradient2) < 0.1) return new Config.Point(line1.end.x, line1.end.y) let x1 = line1.start.x let y1 = line1.start.y let x2 = line1.end.x let y2 = line1.end.y let x3 = line2.start.x let y3 = line2.start.y let x4 = line2.end.x let y4 = line2.end.y //计算交点坐标 let x = ((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1)) / ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4)) let y = ((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4)) / ((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4)) return new Config.Point(x, y) } ///////////////////////////////////////////////////////////////////////////////////////////////// //计算点到线段的距离 this.PointToLineDistance = function (xx, yy, x1, y1, x2, y2) { let ang1, ang2, ang, m let result = 0 // 分别计算三条边的长度 const a = Math.sqrt((x1 - xx) * (x1 - xx) + (y1 - yy) * (y1 - yy)) if (a === 0) { return [0, { x: x1, y: y1 }] } const b = Math.sqrt((x2 - xx) * (x2 - xx) + (y2 - yy) * (y2 - yy)) if (b === 0) { return [0, { x: x2, y: y2 }] } const c = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) // 如果线段是一个点则退出函数并返回距离 if (c === 0) { result = a return [result, { x: x1, y: y1 }] } // 如果点(xx, yy到点x1, y1)这条边短 if (a < b) { // 如果直线段AB是水平线。得到直线段AB的弧度 if (y1 === y2) { if (x1 < x2) { ang1 = 0 } else { ang1 = Math.PI } } else { m = (x2 - x1) / c if (m - 1 > 0.00001) { m = 1 } ang1 = Math.acos(m) if (y1 > y2) { ang1 = Math.PI * 2 - ang1 } // 直线(x1, y1)-(x2, y2)与折X轴正向夹角的弧度 } m = (xx - x1) / a if (m - 1 > 0.00001) { m = 1 } ang2 = Math.acos(m) if (y1 > yy) { ang2 = Math.PI * 2 - ang2 } // 直线(x1, y1)-(xx, yy)与折X轴正向夹角的弧度 ang = ang2 - ang1 if (ang < 0) { ang = -ang } if (ang > Math.PI) { ang = Math.PI * 2 - ang } // 如果是钝角则直接返回距离 if (ang > Math.PI / 2) { return [a, { x: x1, y: y1 }] } // 返回距离并且求得当前距离所在线段的坐标 if (x1 === x2) { return [b * Math.sin(ang), { x: x1, y: yy }] } else if (y1 === y2) { return [b * Math.sin(ang), { x: xx, y: y1 }] } // 直线的斜率存在且不为0的情况下 let x = 0, y = 0 const k1 = (y2 - y1) / x2 - x1 const kk = -1 / k1 const bb = yy - xx * kk const b1 = y2 - x2 * k1 x = (b1 - bb) / (kk - k1) y = kk * x + bb return [a * Math.sin(ang), { x, y }] } // 如果两个点的纵坐标相同,则直接得到直线斜率的弧度 if (y1 === y2) { if (x1 < x2) { ang1 = Math.PI } else { ang1 = 0 } } else { m = (x1 - x2) / c if (m - 1 > 0.00001) { m = 1 } ang1 = Math.acos(m) if (y2 > y1) { ang1 = Math.PI * 2 - ang1 } } m = (xx - x2) / b if (m - 1 > 0.00001) { m = 1 } ang2 = Math.acos(m) // 直线(x2-x1)-(xx, yy)斜率的弧度 if (y2 > yy) { ang2 = Math.PI * 2 - ang2 } ang = ang2 - ang1 if (ang < 0) { ang = -ang } if (ang > Math.PI) { ang = Math.PI * 2 - ang } // 交角的大小 // 如果是对角则直接返回距离 if (ang > Math.PI / 2) { return [b, { x: x2, y: y2 }] } // 如果是锐角,返回计算得到的距离,并计算出相应的坐标 if (x1 === x2) { return [b * Math.sin(ang), { x: x1, y: yy }] } else if (y1 === y2) { return [b * Math.sin(ang), { x: xx, y: y1 }] } // 直线的斜率存在且不为0的情况下 let x = 0, y = 0 const k1 = (y2 - y1) / x2 - x1 const kk = -1 / k1 const bb = yy - xx * kk const b1 = y2 - x2 * k1 x = (b1 - bb) / (kk - k1) y = kk * x + bb return [b * Math.sin(ang), { x, y }] } //点到直线距离 this.PointToLineDis = function (xx, yy, x1, y1, x2, y2) { let len if (x1 - x2 == 0) { len = Math.abs(xx - x1) } else { let A = (y1 - y2) / (x1 - x2) let B = y1 - A * x1 len = Math.abs((A * xx + B - yy) / Math.sqrt(A * A + 1)) } return len } this.getShopData = function (callBack, shopData) { let backObj = { code: 200, msg: '加载成功', data: [] } if (shopData) { Config.shopData = shopData.shopList if (Config.shopData && Array.isArray(Config.shopData)) { Config.shopData.sort(Config.sortShopByFloor) try { if (!Map_QM) { Map_QM = new MainMap_QM(callBack, backObj) Map_QM.initBuild() } else { Map_QM.callBackLoadOver = callBack Map_QM.backObj = backObj Map_QM.loaderOver() } } catch (e) { console.log(e) callBack({ code: 404, msg: '地图数据解析失败' }) callBack = null } } else { backObj.code = 500 backObj.msg = '店铺数据错误' } if (backObj.code == 404) { callBack(backObj) } } else { backObj.code = 500 backObj.msg = '店铺数据错误' callBack(backObj) } } ////////////////////////////////////////////////////////////////////////////---------------------------end /** * 根据色值获取材质 */ this.getMeshMaterial = function (color, alphaModle = 0.9) { let meshMaterial for (let k = 0; k < Config.meshMaterialArr.length; k++) { let color2 = new THREE.Color(color) if (Config.meshMaterialArr[k].color.equals(color2) && Config.meshMaterialArr[k].opacity == alphaModle) { meshMaterial = Config.meshMaterialArr[k] } } if (!meshMaterial) { meshMaterial = new THREE.MeshPhongMaterial({ color: color, emissive: 0x000000, specular: 0x000000, transparent: true, side: THREE.DoubleSide, opacity: alphaModle }) Config.meshMaterialArr.push(meshMaterial) } return meshMaterial } this.rotateYZ = function (geometry, ry, rz) { let center = new THREE.Vector3() geometry.computeBoundingBox() geometry.boundingBox.getCenter(center) let x = center.x let y = center.y let z = center.z geometry.center() geometry.rotateY(ry) geometry.rotateX(rz) geometry.translate(x, y, z) } this.rotateTextYZ = function (geometry, rx, ry, rz) { let center = new THREE.Vector3() geometry.computeBoundingBox() geometry.boundingBox.getCenter(center) let x = center.x let y = center.y let z = center.z geometry.center() geometry.rotateX(rx) geometry.rotateY(ry) geometry.rotateZ(rz) geometry.translate(x, y, z) } //对象克隆 this.cloneObject = function (sourceObj) { let target = sourceObj instanceof Array ? [] : {} for (attr in sourceObj) { if (!obj.hasOwnProperty(attr)) continue target[attr] = typeof sourceObj[i] == 'object' ? obj[attr].clone() : obj[attr] } return target } } var Config = new ConfigFun() //////////////////////////////-------------------------------------------配置 ConfigFun /** * 地图主类,入口 初始化设备点位 * @param width,height 勾图的地图宽、高 * @param floor, 楼层编号从0开始 * @param navPoint, 导航点位 * @param angle(-180~180) 设备角度 */ MainMap_QM = function (callBack, backObj) { this.callBackLoadOver = callBack this.backObj = backObj let ele = document.getElementById('mapContainer') this.w = parseInt(window.getComputedStyle(ele, null).getPropertyValue('width')) this.h = parseInt(window.getComputedStyle(ele, null).getPropertyValue('height')) this.scene = new THREE.Scene() this.scene.name = 'scene' aspect = this.w / this.h cameraPerspective = new THREE.PerspectiveCamera(45, aspect, 10, 10000) cameraPerspective.position.set(Config.cameraDist.x, Config.cameraDist.y, Config.cameraDist.z) //x 水平 y 垂直旋转 z 展示大小 cameraPerspective.lookAt(new THREE.Vector3(0, 0, 0)) cameraOrtho = new THREE.OrthographicCamera((340 * aspect) / -2, (340 * aspect) / 2, 340 / 2, 340 / -2, 10, 10000) cameraOrtho.position.set(0, 200, 0) cameraOrtho.lookAt(new THREE.Vector3(0, 0, 0)) this.camera = Config.cameraDist.state == '2D' ? cameraOrtho : cameraPerspective // this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, preserveDrawingBuffer: true }) // preserveDrawingBuffer 是否可以截图 //this.renderer.outputEncoding = THREE.sRGBEncoding; this.renderer.setSize(this.w, this.h) this.renderer.setPixelRatio(window.devicePixelRatio) this.renderer.shadowMap.enabled = true // 阴影类型 this.renderer.shadowMap.type = THREE.PCFSoftShadowMap ele.appendChild(this.renderer.domElement) this.renderer.domElement.style.width = this.w this.renderer.domElement.style.height = this.h this.labelRenderer = new THREE.CSS2DRenderer() this.labelRenderer.setSize(this.w, this.h, Config.perc_H) this.labelRenderer.domElement.style.position = 'absolute' this.labelRenderer.domElement.style.top = 0 ele.appendChild(this.labelRenderer.domElement) let aLight = new THREE.AmbientLight(0xffffff, 0.3) aLight.name = 'light' this.scene.add(aLight) let hemiLight = new THREE.HemisphereLight(0xdddddd, 0xffffff, 0.56) hemiLight.position.set(0, 50, 0) hemiLight.name = 'light' this.scene.add(hemiLight) this.light = new THREE.DirectionalLight(0xffffff, 0.3) this.light.name = 'light' this.light.position.set(280, 400, -300) if (Config.cameraDist.state === '3D') { this.light.castShadow = true //阴影 } this.light.shadow.camera.top = 320 this.light.shadow.camera.bottom = -320 this.light.shadow.camera.right = 320 this.light.shadow.camera.left = -320 this.light.shadow.camera.far = 1200 this.light.shadow.camera.near = 50 this.light.shadow.mapSize.set(1024, 1024) this.scene.add(this.light) this.mixers = [] this.controls = new THREE.OrbitControls(this.camera, ele) //鼠标控制 //this.controls.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; //this.controls.enabled = true; this.controls.minZoom = 0.8 this.controls.maxZoom = Config.cameraZoom //设置相机距离原点的最远距离 this.controls.minDistance = Config.mapDistance.min //设置相机距离原点的最远距离 this.controls.maxDistance = Config.mapDistance.max this.controls.minPolarAngle = Config.cameraDist.state == '2D' ? 0 : 0.3 // radians this.controls.maxPolarAngle = Config.cameraDist.state == '2D' ? 0 : Math.PI / 2 - 0.3 // radians this.controls.addEventListener('change', this.collisionChock, false) //控制器变化 if (window.PointerEvent) { document.getElementById('mapContainer').addEventListener('pointerup', this.onMouseClickBox, false) //地图点击 } else { document.getElementById('mapContainer').addEventListener('click', this.onMouseClickBox, false) //地图点击 } document.addEventListener('rezise', this.changeDocmentResize) //窗口变化 this.mapArr = [] this.selectShop this.selectEle = null //当前使用的电梯 this.overShop //终点店铺 this.shape = new THREE.Shape() this.shape.moveTo(-10, -10) this.shape.lineTo(10, -10) this.shape.lineTo(10, 10) this.shape.lineTo(-10, 10) this.dtLineGroup = new THREE.Group() this.dtLineGroup.name = 'dtLine' if (document.getElementById('moveFloorBG')) { this.moveFloorbg = new THREE.CSS2DObject(document.getElementById('moveFloorBG')) this.scene.add(this.moveFloorbg) } this.man_3d this.man_2d this.guide this.sceneGap = new THREE.Group() this.outModelGap = new THREE.Group() //全局外立面模型 this.scene.add(this.outModelGap) this.qiModel //起点 this.qiIcon //起点Icon this.dirIcon //我的方向Icon this.endIcon //终点Icon this.endModel this.forShopArr = [] //途径数据 } MainMap_QM.prototype = { //加载全局模型 initOutModel: function () { if (Config.initModelArr && Config.initModelArr.length > 0) { for (let i = 0; i < Config.initModelArr.length; i++) { let loader = new THREE.GLTFLoader() loader.load(Config.initModelArr[i].url, function (collada) { collada.scene.scale.x = collada.scene.scale.y = collada.scene.scale.z = Config.initModelArr[i].scale collada.scene.position.set(Config.initModelArr[i].site.x, Config.initModelArr[i].site.y, Config.initModelArr[i].site.z) collada.scene.rotation.set( (Config.initModelArr[i].rot.x * Math.PI) / 180, (Config.initModelArr[i].rot.y * Math.PI) / 180, (Config.initModelArr[i].rot.z * Math.PI) / 180 ) collada.scene.userData.type = Config.initModelArr[i].type collada.scene.traverse(function (child) { if (child.type === 'Mesh') { child.castShadow = true child.receiveShadow = true if (child.material.map) { child.material.map.encoding = THREE.LinearEncoding //贴图需要转换成 线性编码 } child.material.color.convertGammaToLinear(0.7) } }) collada.scene.name = 'model' //删除其它元素时过滤 Map_QM.outModelGap.add(collada.scene) // 添加进入商场标签 let SpriteDiv = document.createElement('div') SpriteDiv.style = "position: absolute; background: url('./static/img/map-btn-bg.png'); width: 220px; height: 64px;" SpriteDiv.innerHTML = '

建发现代城

' + '

购物中心

' + '

进入

' + '' let pointLabel = new THREE.CSS2DObject(SpriteDiv) pointLabel.position.set(-7.2, -30, 0.2) pointLabel.userData.type = '2d_IP' pointLabel.center = new THREE.Vector2(0.5, 0) pointLabel.userData.show = 'cn' Map_QM.outModelGap.add(pointLabel) if (pointLabel.userData.show != language) { pointLabel.element.style.opacity = 0 } let SpriteDiv2 = document.createElement('div') SpriteDiv2.style = "position: absolute; background: url('./static/img/map-btn-bg.png'); width: 220px; height: 64px;" SpriteDiv2.innerHTML = '

MODERN CITY

' + '

Shopping Mall

' + '

GO

' + '' let pointLabel2 = new THREE.CSS2DObject(SpriteDiv2) pointLabel2.position.set(-7.2, -30, 0.2) pointLabel2.userData.type = '2d_IP' pointLabel2.center = new THREE.Vector2(0.5, 0) pointLabel2.userData.show = 'en' Map_QM.outModelGap.add(pointLabel2) if (pointLabel2.userData.show != language) { pointLabel2.element.style.opacity = 0 } }) if (i == Config.initModelArr.length - 1) { Map_QM.initBuild() } } } }, initBuild: function (e) { elevator = null straight = null elevatorDown = null if (Config.showModelIcon) { let loader = new THREE.GLTFLoader() loader.load('./static/img/elevator.gltf', function (collada) { collada.scene.scale.x = collada.scene.scale.y = collada.scene.scale.z = 15 collada.scene.rotation.x = (-90 * Math.PI) / -180 collada.scene.renderOrder = 300 elevator = collada.scene collada.scene.children[0].traverse(function (child) { if (child.isMesh && child.name == 'boli2') { //child.material.color = new THREE.Color(0xffffff); child.material.opacity = 0.8 } if (child.isMesh && child.name == 'pidai') { //child.material.color = new THREE.Color(0xffffff); child.material.opacity = 0.8 } }) new THREE.GLTFLoader().load('./static/img/elevatorDown.gltf', function (collada) { collada.scene.scale.x = collada.scene.scale.y = collada.scene.scale.z = 15 collada.scene.rotation.x = (-90 * Math.PI) / -180 collada.scene.renderOrder = 300 elevatorDown = collada.scene collada.scene.children[0].traverse(function (child) { if (child.isMesh && child.name == 'boli2') { //child.material.color = new THREE.Color(0xffffff); child.material.opacity = 0.8 } if (child.isMesh && child.name == 'pidai') { //child.material.color = new THREE.Color(0xffffff); child.material.opacity = 0.8 } }) new THREE.GLTFLoader().load('./static/img/dt.gltf', function (collada) { collada.scene.scale.x = collada.scene.scale.y = collada.scene.scale.z = 15 collada.scene.rotation.x = (-90 * Math.PI) / -180 collada.scene.renderOrder = 300 straight = collada.scene //console.log(straight); straight.traverse(function (child) { if (child.isMesh && child.name == 'zhitiboli') { //child.material.color = new THREE.Color(0xffffff); child.material.opacity = 0.6 child.material.side = 1 } // if (child.isMesh && child.name == "zhitijiegou") { // child.material.color = new THREE.Color(0xffffff); // child.material.opacity = 0.8; // } }) Map_QM.index = 0 Map_QM.initTreeModel() }) }) }) } else { Map_QM.index = 0 Map_QM.initTreeModel() } }, loaderOver: function (e) { Map_QM.cancelRender() this.controls.reset() basePath = null this.renderer.dispose() this.remove_child(this.sceneGap) this.scene.remove(this.sceneGap) this.sceneGap = new THREE.Group() this.sceneGap.position.set(Config.sceneGap.x, Config.sceneGap.y, Config.sceneGap.z) this.sceneGap.scale.set(Config.sceneGap.scale, Config.sceneGap.scale, Config.sceneGap.scale) this.scene.add(this.sceneGap) this.buildObj = new THREE.Group() this.sceneGap.add(this.buildObj) this.CSSObject = new THREE.Object3D() this.buildObj.add(this.CSSObject) this.buildObj.add(this.dtLineGroup) this.initGuide() let pathData = Config.allMap[parseInt(Config.deviceObj.build)].buildArr[parseInt(Config.deviceObj.floor)].mapData.path if (parseInt(Config.deviceObj.node) != -1) { if (pathData && pathData.nodes.length > parseInt(Config.deviceObj.node)) { Config.deviceObj.xaxis = pathData.nodes[parseInt(Config.deviceObj.node)].x Config.deviceObj.yaxis = pathData.nodes[parseInt(Config.deviceObj.node)].y } } Config.mapScale = Config.allMap[Config.selectBuild].scale || 18 facAllArr = [] this.mapArr.length = 0 basePath = '{' for (let bd = 0; bd < Config.allMap.length; bd++) { for (let i = 0; i < Config.allMap[bd].buildArr.length; i++) { this.convertPath(bd, i) } } if (basePath.length > 1) { basePath = basePath.substr(0, basePath.length - 1) } if (Config.otherPath) { //如果多楼栋需要配置楼栋之间通行路径 basePath += Config.otherPath } basePath += '}' //初始化基础路径; let graphPathObj = JSON.parse(basePath) let ftPathObj = JSON.parse(basePath) let dtPathObj = JSON.parse(basePath) for (let j = 0; j < facAllArr.length; j++) { for (let k = 0; k < facAllArr[j].length; k++) { let facP = facAllArr[j][k].buildOrder + '_' + facAllArr[j][k].floorOrder + '_' + facAllArr[j][k].navCode for (let h = 0; h < facAllArr[j].length; h++) { if (h != k) { let nP = facAllArr[j][h].buildOrder + '_' + facAllArr[j][h].floorOrder + '_' + facAllArr[j][h].navCode if (facAllArr[j][h].facCode == 'dt') { ftPathObj[facP][nP] = 6000 if (facAllArr[j][k].selFac) { graphPathObj[facP][nP] = 300 + 100 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) dtPathObj[facP][nP] = 300 + 100 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) } else { graphPathObj[facP][nP] = 1500 + 100 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) dtPathObj[facP][nP] = 1500 + 100 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) } } else if (facAllArr[j][h].facCode == 'lt') { graphPathObj[facP][nP] = 500 + 300 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) ftPathObj[facP][nP] = 500 + 300 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) dtPathObj[facP][nP] = 500 + 300 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) } else { dtPathObj[facP][nP] = 6000 if (facAllArr[j][k].selFac) { graphPathObj[facP][nP] = 300 + 100 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) ftPathObj[facP][nP] = 300 + 100 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) } else { graphPathObj[facP][nP] = 1500 + 100 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) ftPathObj[facP][nP] = 1500 + 100 * Math.abs(parseInt(facAllArr[j][h].floorOrder) - parseInt(facAllArr[j][k].floorOrder)) } } } } } } graphPath = graphPathObj ftPath = ftPathObj dtPath = dtPathObj var fIndex = 0, bIndex = 0 this.mapArr[bIndex] = [] intTimer = setInterval(() => { let floor = new FloorMap_QM(bIndex, fIndex, Config.allMap[bIndex].buildArr[fIndex].name) floor.floorName = Config.allMap[bIndex].buildArr[fIndex].name floor.initDraw() floor.allObj.position.set(bIndex * Config.distance, (fIndex - Config.selectFloor) * Config.doubleDist, 0) //floor.allObj.renderOrder = 20 - fIndex; this.buildObj.add(floor.allObj) this.mapArr[bIndex].push(floor) fIndex++ if (fIndex >= Config.allMap[bIndex].buildArr.length) { if (bIndex == Config.allMap.length - 1) { clearInterval(intTimer) this.controls.minPan = new THREE.Vector3(this.w / -10, 0, this.h / -10) this.controls.maxPan = new THREE.Vector3(this.w / 10, 0, this.h / 10) let pathData = Config.allMap[parseInt(Config.deviceObj.build)].buildArr[parseInt(Config.deviceObj.floor)].mapData.path if (Config.deviceObj.xaxis) { Map_QM.mapArr[parseInt(Config.deviceObj.build)][parseInt(Config.deviceObj.floor)].setStartSite( Config.deviceObj.xaxis, Config.deviceObj.yaxis, parseInt(Config.shopHeight) ) } else { if (parseInt(Config.deviceObj.node) != -1) { if (pathData && !Config.deviceObj.xaxis && pathData.nodes.length > parseInt(Config.deviceObj.node)) { Config.deviceObj.xaxis = pathData.nodes[parseInt(Config.deviceObj.node)].x Config.deviceObj.yaxis = pathData.nodes[parseInt(Config.deviceObj.node)].y } else { console.warn('初始化点位失败') } Map_QM.mapArr[parseInt(Config.deviceObj.build)][parseInt(Config.deviceObj.floor)].setStartSite( Config.deviceObj.xaxis, Config.deviceObj.yaxis, parseInt(Config.shopHeight) ) } } this.initFloor() } else { bIndex++ fIndex = 0 this.mapArr[bIndex] = [] } } }, 50) }, initTreeModel: function () { let url = Config.modelStr[this.index].url this.gltfLoad(url) }, gltfLoad: function (url) { let sopce = this new THREE.GLTFLoader().load(url, function (object) { //加载路径fbx文件 object.scene.traverse(function (child) { if (child.type === 'Mesh') { child.castShadow = true child.receiveShadow = true if (child.material.map) { child.material.map.encoding = THREE.LinearEncoding //贴图需要转换成 线性编码 } if (Config.modelStr[sopce.index].colorModel === 'gama') { child.material.color.convertGammaToLinear(0.5) } } }) object.scene.children[0].scale.set(Config.modelStr[sopce.index].size.x, Config.modelStr[sopce.index].size.y, Config.modelStr[sopce.index].size.z) Config.fbxModels.push({ key: Config.modelStr[sopce.index].key, obj: object.scene, operation: Config.modelStr[sopce.index] }) if (sopce.index < Config.modelStr.length - 1) { sopce.index++ sopce.initTreeModel() } else { if (Config.allMap && Config.allMap.length > 0) { Map_QM.loaderOver() } else { Config.getInstance(Map_QM.callBackLoadOver, Config.deviceObj.build, Config.deviceObj.floor, Config.deviceObj.node, Config.deviceObj.angle) } } }) }, initGuide: function () { new THREE.GLTFLoader().load('./static/img/runman.gltf', function (obj) { obj.scene.scale.x = obj.scene.scale.y = obj.scene.scale.z = 15 obj.scene.children[0].children[1].children[0].material.color = new THREE.Color(0xfe9219) Map_QM.sceneGap.add(obj.scene) obj.scene.visible = false obj.scene.children[0].rotation.x = Math.PI / 2 obj.scene.children[0].rotation.y = Math.PI // obj作为参数创建一个混合器,解析播放obj及其子对象包含的动画数据 let mixer = new THREE.AnimationMixer(obj.scene) let AnimationAction = mixer.clipAction(obj.animations[0]) AnimationAction.timeScale = 2 AnimationAction.play() Map_QM.mixers.push(mixer) Map_QM.man_3d = obj.scene Map_QM.guide = Map_QM.man_3d }) new THREE.TextureLoader().load('./static/img/guide.png', textu => { let planeMaterial = new THREE.MeshPhongMaterial({ map: textu, depthTest: true, transparent: true, alphaTest: 0.1 }) let planeGeometry = new THREE.PlaneGeometry(128, 128) Map_QM.man_2d = new THREE.Mesh(planeGeometry, planeMaterial) Map_QM.man_2d.center = new THREE.Vector2(0.5, 0.5) Map_QM.man_2d.visible = false Map_QM.sceneGap.add(Map_QM.man_2d) }) }, initFloor: function () { Map_QM.changeFloorInner() //初始化方向为第一人称方向 let angle = Config.deviceAng ? Config.deviceObj.angle : Config.mapAngle Map_QM.rotationAngle(angle) Map_QM.startRender() Map_QM.controls.saveState() }, /** * 解析路径 */ convertPath: function (buildOrder, floorOrder) { let mapData = Config.allMap[buildOrder].buildArr[floorOrder].mapData let pathData = mapData.path if (!pathData) { return } if (pathData.nodes.length > 0) { pathData.nodes.sort(Config.sortNode) for (let i = 0; i < pathData.nodes.length; i++) { let a = pathData.nodes[i].id basePath += '"' + buildOrder + '_' + floorOrder + '_' + a + '":{' for (let n = 0; n < pathData.nodes[i]['list'].length; n++) { let b if (pathData.nodes[i]['list'][n].id || pathData.nodes[i]['list'][n].id == '0') { b = pathData.nodes[i]['list'][n].id } else { b = a == pathData.nodes[i]['list'][n].selfNode.id ? pathData.nodes[i]['list'][n].nextNode.id : pathData.nodes[i]['list'][n].selfNode.id } basePath += '"' + buildOrder + '_' + floorOrder + '_' + b + '":' + pathData.nodes[i]['list'][n].cost + ',' } if (pathData.nodes[i]['list'].length > 0) { basePath = basePath.substr(0, basePath.length - 1) } basePath += '},' } } let selUpFacNo = -1, selDownFacNo = -1, seldtFacNo = -1, minupS = -1, mindownS = -1, minds = -1, minls = -1 for (let h = 0; h < mapData.stairs.length; h++) { mapData.stairs[h].selFac = false if (mapData.stairs[h].state && mapData.stairs[h].no != '') { let ms = Math.abs(parseInt(mapData.stairs[h].x) - parseInt(Config.deviceObj.xaxis)) + Math.abs(parseInt(mapData.stairs[h].y) - parseInt(Config.deviceObj.yaxis)) if (mapData.stairs[h].facCode.search('ft') != -1) { if (mapData.stairs[h].facCode == 'upft') { if (minupS == -1) { minupS = ms selUpFacNo = mapData.stairs[h].no } else { if (minupS > ms) { minupS = ms selUpFacNo = mapData.stairs[h].no } } } else { if (mindownS == -1) { mindownS = ms selDownFacNo = mapData.stairs[h].no } else { if (mindownS > ms) { mindownS = ms selDownFacNo = mapData.stairs[h].no } } } } else if (mapData.stairs[h].facCode == 'dt') { if (minds == -1) { minds = ms seldtFacNo = mapData.stairs[h] } else { if (minds > ms) { minds = ms seldtFacNo = mapData.stairs[h] } } } } } seldtFacNo.selFac = true selUpFacNo.selFac = true selDownFacNo.selFac = true let noHas for (let j = 0; j < mapData.stairs.length; j++) { if ((mapData.stairs[j].hasOwnProperty('state') && mapData.stairs[j].state) || !mapData.stairs[j].state) { //排除禁用的设施 noHas = true for (let k = 0; k < facAllArr.length; k++) { //facAllArr 记录遍历结果 if (facAllArr[k][0].no != '' && facAllArr[k][0].navCode != '' && facAllArr[k][0].no == mapData.stairs[j].no) { if (facAllArr[k][0].facCode == mapData.stairs[j].facCode || (facAllArr[k][0].facCode.search('ft') != -1 && mapData.stairs[j].facCode.search('ft') != -1)) { noHas = false mapData.stairs[j].floorOrder = floorOrder mapData.stairs[j].buildOrder = buildOrder facAllArr[k].push(mapData.stairs[j]) } } } if (noHas) { mapData.stairs[j].buildOrder = buildOrder mapData.stairs[j].floorOrder = floorOrder let array = [mapData.stairs[j]] facAllArr.push(array) } } } }, /** * @api {方法} changeMapState("3d") 地图状态切换 * @apiGroup 地图交互 * @apiDescription 地图展示状态切换 * @apiVersion 1.0.0 * @apiParam {string} state 地图状态 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.changeMapState("2d"); * */ changeMapState: function (state) { if (state === '3d') { Map_QM.camera = cameraPerspective Map_QM.controls.maxPolarAngle = Math.PI / 2 - 0.3 Map_QM.controls.minPolarAngle = 0.3 Map_QM.controls.object = Map_QM.camera Map_QM.camera.updateProjectionMatrix() //必须update Map_QM.camera.position.set(Config.cameraDist.x, Config.cameraDist.y, Config.cameraDist.z) Map_QM.changeIconState(state) } else { Map_QM.camera = cameraOrtho Map_QM.controls.maxPolarAngle = 0 Map_QM.controls.minPolarAngle = 0 Map_QM.controls.object = Map_QM.camera Map_QM.camera.updateProjectionMatrix() //必须update Map_QM.controls.maxPolarAngle = 0 Map_QM.changeIconState(state) } }, /** * @api {方法} changeIconState("3d") ICON状态切换 * @apiGroup 地图交互 * @apiDescription ICON状态切换 * @apiVersion 1.0.0 * @apiParam {string} state 展示状态 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.changeIconState("2d"); * */ changeIconState: function (state) { for (let i = 0; i < Map_QM.mapArr[Config.selectBuild].length; i++) { Map_QM.mapArr[Config.selectBuild][i].serObj.traverse(obj => { if (obj.userData && obj.userData.use) { if (obj.userData.use != 'all' && obj.userData.use != state) { obj.visible = false } else { obj.visible = true } } }) } if (Map_QM.dirIcon) { Map_QM.dirIcon.visible = state == '2d' ? true : false } if (Map_QM.qiModel) { Map_QM.qiModel.visible = state == '2d' ? false : true } }, /** * @api {方法} changeBuild(0) 楼栋切换 * @apiGroup 地图交互 * @apiDescription 楼栋切换 传入楼栋编号 * @apiVersion 1.0.0 * @apiParam {int} index 传入楼栋编号,改变地图显示内容 * * @apiSampleRequest off * * @apiParamExample {int} 请求示例 * * Map_QM.changeBuild(0); * */ changeBuild: function (fIndex = -1, tFloor = 0) { clearTimeout(outTime) Map_QM.forShopArr.length = 0 forShopArr = { direction: '', wayList: [] } if (Map_QM.selectShop) { Map_QM.selectShop.scale.z = 1 } if (Map_QM.endModel && Map_QM.endModel.visible) { Map_QM.endModel.visible = false } if (Map_QM.endIcon && Map_QM.endIcon.visible) { Map_QM.endIcon.visible = false } Map_QM.clearFloor(Config.selectFloor) if (fIndex != -1) { Config.selectBuild = fIndex Config.selectFloor = tFloor } Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.visible = true Map_QM.mapArr[Config.selectBuild][Config.selectFloor].labelObj.traverse(obj => { obj.visible = true }) Map_QM.mapArr[Config.selectBuild][Config.selectFloor].iconLabel.traverse(obj => { obj.visible = true }) TweenMax.to(Map_QM.buildObj.position, 0.3, { x: Config.selectBuild * Config.distance * -1, onComplete: function () { Map_QM.changeFloorInner(Config.selectFloor) //结束后切换楼层 } }) }, /** * @api {方法} onShowMeDir() 我的方向 * @apiGroup 地图交互 * @apiDescription 我的方向 * @apiVersion 1.0.0 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.onShowMeDir(); * */ onShowMeDir: function (zoom = 0) { Map_QM.changeMapTo2D() if (Config.deviceObj.xaxis) { Map_QM.controls.object.position.set(Config.deviceObj.xaxis * Config.sceneGap.scale, 100, Config.deviceObj.yaxis * Config.sceneGap.scale) Map_QM.controls.target = new THREE.Vector3(Config.deviceObj.xaxis * Config.sceneGap.scale, 0, Config.deviceObj.yaxis * Config.sceneGap.scale) } else { Map_QM.controls.object.position.set(0, 100, 0) Map_QM.controls.target = new THREE.Vector3(0, 0, 0) } zoom = zoom == 0 ? Config.cameraZoom : zoom Map_QM.controls.setZoom(zoom) Map_QM.rotationAngle(Config.deviceObj.angle || 0) Map_QM.controls.enableRotate = false }, changeMapTo2D: function () { Config.setModelState('2d') Map_QM.light.castShadow = false //阴影 Map_QM.camera = cameraOrtho Map_QM.controls.maxPolarAngle = 0 Map_QM.controls.minPolarAngle = 0 if (isPathState) { Map_QM.dirIcon.visible = false Map_QM.qiIcon.visible = true } Map_QM.controls.object = Map_QM.camera }, changeMapModel: function (model) { if (model == '2D') { Config.setModelState('2d') Map_QM.camera = cameraOrtho Map_QM.controls.maxPolarAngle = 0 Map_QM.controls.minPolarAngle = 0 Map_QM.controls.object = Map_QM.camera } else { Config.setModelState('3d') Map_QM.camera = cameraPerspective Map_QM.controls.object = Map_QM.camera Map_QM.controls.maxPolarAngle = Math.PI / 2 - 0.3 Map_QM.controls.minPolarAngle = 0.3 Map_QM.controls.reset() let angle = Config.deviceAng ? Config.deviceObj.angle : Config.mapAngle Map_QM.rotationAngle(angle) } }, /** * @api {方法} onShowLocalSite(0) 局部显示放大 * @apiGroup 地图交互 * @apiDescription 局部显示放大 point 传入放大目标点,zoom放大级别 1-5 * @apiVersion 1.0.0 * @apiParam {Object} point 放大的地图位置 * @apiParam {int} zoom 放大倍数 * * @apiSampleRequest off * * @apiParamExample {Object} 请求示例 * * Map_QM.onShowLocalSite({point:{x:0,y:0},zoom:5}); * */ onShowLocalSite: function (obj) { Config.setModelState('3d') Map_QM.camera = cameraPerspective Map_QM.controls.object = Map_QM.camera Map_QM.controls.maxPolarAngle = Math.PI / 2 - 0.3 Map_QM.controls.minPolarAngle = 0.3 Map_QM.controls.reset() let angle = Config.deviceAng ? Config.deviceObj.angle : Config.mapAngle Map_QM.rotationAngle(angle) Map_QM.controls.target = new THREE.Vector3(obj.point.x * Config.sceneGap.scale, 0, obj.point.y * Config.sceneGap.scale) let disX = 60 + (5 - parseInt(obj.zoom)) * 300 Map_QM.controls.object.position.set(obj.point.x * Config.sceneGap.scale, disX, obj.point.y * Config.sceneGap.scale + disX) // 加 disX 保持地图倾斜角度 }, /** * @api {方法} changeLanguage() 切换中英文 * @apiGroup 地图交互 * @apiDescription 切换中英文 cn en * @apiVersion 1.0.0 * @apiParam {String} str 显示语言 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.changeLanguage("en"); * */ changeLanguage: function (lang = 'cn') { language = lang for (let t = 0; t < Map_QM.mapArr.length; t++) { for (let i = 0; i < Map_QM.mapArr[t].length; i++) { Map_QM.mapArr[t][i].labelObj.traverse(obj => { if (obj.element) { obj.element.innerText = language == 'en' ? obj.element.dataset.nameEn : obj.element.dataset.name } }) Map_QM.mapArr[t][i].iconLabel.traverse(obj => { if (obj.element) { obj.element.innerText = language == 'en' ? obj.element.dataset.nameEn : obj.element.dataset.name } }) } } Map_QM.collisionChock() }, /** * @api {方法} onShowDeviceSite() 地图方向复位 * @apiGroup 地图交互 * @apiDescription 地图方向复位 * @apiVersion 1.0.0 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.onShowDeviceSite(); * */ onShowDeviceSite: function () { Config.setModelState('3d') Map_QM.camera = cameraPerspective Map_QM.controls.setZoom(1) if (Config.cameraDist.state === '3D') { Map_QM.light.castShadow = true //阴影 } Map_QM.controls.object = Map_QM.camera Map_QM.controls.maxPolarAngle = Math.PI / 2 - 0.3 Map_QM.controls.minPolarAngle = 0.3 if (Map_QM.qiModel) { Map_QM.qiModel.visible = true } if (Map_QM.dirIcon) { Map_QM.dirIcon.visible = false } Map_QM.controls.enabled = true Map_QM.controls.enableRotate = true Map_QM.controls.reset() let angle = Config.deviceAng ? Config.deviceObj.angle : Config.mapAngle Map_QM.rotationAngle(angle) }, /** * 在2D 状态下平移镜头 */ moveCameraBy2D: function (obj) { if (pathCameraState == '2D' && Map_QM.camera == cameraOrtho) { Map_QM.controls.minAzimuthAngle = (Config.deviceObj.angle * Math.PI) / -180 Map_QM.controls.maxAzimuthAngle = (Config.deviceObj.angle * Math.PI) / -180 Map_QM.controls.object.position.set(0, 200, obj.y * Config.sceneGap.scale) Map_QM.controls.target = new THREE.Vector3(obj.x * Config.sceneGap.scale, 0, obj.y * Config.sceneGap.scale) //移动 } }, /** * 方向复位 */ resetMeDir: function () { Config.setModelState('3d') Map_QM.camera = cameraPerspective Map_QM.controls.object = Map_QM.camera Map_QM.controls.setZoom(1) if (Config.cameraDist.state === '3D') { Map_QM.light.castShadow = true //阴影 } Map_QM.controls.maxPolarAngle = Math.PI / 2 - 0.3 Map_QM.controls.minPolarAngle = 0.3 Map_QM.controls.minAzimuthAngle = -Infinity Map_QM.controls.maxAzimuthAngle = Infinity Map_QM.controls.target = new THREE.Vector3(0, 0, 0) Map_QM.dirIcon.visible = false Map_QM.qiIcon.visible = false if (Map_QM.qiModel) { Map_QM.qiModel.visible = true } Map_QM.controls.reset() let angle = Config.deviceAng ? Config.deviceObj.angle : Config.mapAngle Map_QM.rotationAngle(angle) }, /** * @api {方法} changePathDir(pathState) 切换导航方向 * @apiGroup 地图交互 * @apiDescription 切换导航方向 * @apiVersion 1.0.0 * * @apiParam {String} pathState 地图导航方向 2D/3D * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.changePathDir("2D"); * */ changePathDir: function (pathState = '3D') { if (isPathState) { //导航状态 let initVis = false if (Map_QM.endModel && Map_QM.endIcon) { initVis = pathCameraState == '2D' ? Map_QM.endIcon.visible : Map_QM.endModel.visible } pathCameraState = pathState if (Map_QM.endModel && Map_QM.endIcon) { Map_QM.endModel.visible = pathCameraState == '2D' ? false : initVis Map_QM.endIcon.visible = pathCameraState == '2D' ? initVis : false } if (pathCameraState == '2D') { //2D导航 Map_QM.onShowMeDir(2) Map_QM.guide.visible = false let pos = Map_QM.guide.position Map_QM.guide = Map_QM.man_2d Map_QM.guide.position.x = pos.x Map_QM.guide.position.y = pos.y Map_QM.guide.visible = true Map_QM.controls.enabled = false } else { Map_QM.resetMeDir() Map_QM.controls.enabled = true Map_QM.controls.enableRotate = true Map_QM.guide.visible = false let pos = Map_QM.guide.position Map_QM.guide = Map_QM.man_3d Map_QM.guide.position.x = pos.x Map_QM.guide.position.y = pos.y Map_QM.guide.visible = true } } }, /** * @api {方法} cancelSelectShop() 取消店铺弹跳 * @apiGroup 地图交互 * @apiDescription 取消店铺弹跳效果 * @apiVersion 1.0.0 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.cancelSelectShop(); * */ cancelSelectShop: function () { TweenMax.killAll(true) if (Map_QM.selectShop) { Map_QM.selectShop.scale.z = 1 } }, /** * @api {方法} changeStateShopPro(isShow) 店铺促销标签 * @apiGroup 地图交互 * @apiDescription 店铺促销标签展示/隐藏 * @apiVersion 1.0.0 * * @apiParam {boolean} isShow 店铺促销标签是否显示 * * @apiSampleRequest off * * @apiParamExample {boolean} 请求示例 * * Map_QM.changeStateShopPro(true); * */ changeStateShopPro: function (isShow = false) { Map_QM.mapArr[Config.selectBuild][Config.selectFloor].tagObj.traverse(obj => { obj.visible = isShow }) }, /** * @api {方法} changeShowTagObjState(isShow) 自定义标签 * @apiGroup 地图交互 * @apiDescription 自定义标签展示/隐藏 * @apiVersion 1.0.0 * * @apiParam {boolean} isShow 自定义标签是否显示 * * @apiSampleRequest off * * @apiParamExample {boolean} 请求示例 * * Map_QM.changeShowTagObjState(true); * */ changeShowTagObjState: function (isShow = false) { Map_QM.mapArr[Config.selectBuild][Config.selectFloor].showTagObj.traverse(obj => { obj.visible = isShow }) }, /** * @api {方法} drawCurveLine(startShop,endShop) 获取店铺列表信息 * @apiGroup 地图数据 * @apiDescription 店铺列表 * @apiVersion 1.0.0 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.queryShopList() * */ queryShopList: function () { return JSON.parse(JSON.stringify(Config.shopData)) }, /** * @api {方法} drawCurveLine(startShop,endShop,color) 绘制引导线 * @apiGroup 地图交互 * @apiDescription 绘制引导线 * @apiVersion 1.0.1 * * @apiParam {string/Array} startShop 起始店铺编号或编号数组 * @apiParam {string/Array} endShop 终点店铺编号或编号数组 * * @apiSampleRequest off * * @apiParamExample 请求示例 * //绘制一到多 * Map_QM.drawCurveLine('L125',['L117','L127','L130'],'#2246d8') * //绘制多到一 * Map_QM.drawCurveLine(['L117','L127','L130'],'L125','#2246d8') * */ drawCurveLine: function (startShop, endShop, color = '#0099ff') { let sp, ep, cp1, cp2 if (Array.isArray(startShop) && Array.isArray(endShop)) { return { msg: '只能有一个数组' } } if (Array.isArray(startShop)) { //如果是数组 endShop = Map_QM.shopNumToNavPoint({ shopNum: endShop }, 'shop') for (let i = 0; i < startShop.length; i++) { startShop[i] = Map_QM.shopNumToNavPoint({ shopNum: startShop[i] }, 'shop') sp = new THREE.Vector3(startShop[i].xaxis, -1 * startShop[i].yaxis, Config.shopHeight) ep = new THREE.Vector3(endShop.xaxis, -1 * endShop.yaxis, Config.shopHeight) let s = Math.sqrt( (endShop.xaxis - startShop[i].xaxis) * (endShop.xaxis - startShop[i].xaxis) + (endShop.yaxis - startShop[i].yaxis) * (endShop.yaxis - startShop[i].yaxis) ) cp1 = new THREE.Vector3( startShop[i].xaxis + (endShop.xaxis - startShop[i].xaxis) / 3, -1 * startShop[i].yaxis - (endShop.yaxis - startShop[i].yaxis) / 3, Config.shopHeight + s / 5 ) cp2 = new THREE.Vector3(endShop.xaxis, -1 * endShop.yaxis, Config.shopHeight + s / 3) Map_QM.mapArr[Config.selectBuild][Config.selectFloor].lineObj.add(Config.drawDashedLine(sp, ep, s / 10, color, cp1, cp2)) } } else { startShop = Map_QM.shopNumToNavPoint({ shopNum: startShop }, 'shop') if (Array.isArray(endShop)) { //如果是数组 for (let i = 0; i < endShop.length; i++) { endShop[i] = Map_QM.shopNumToNavPoint({ shopNum: endShop[i] }, 'shop') sp = new THREE.Vector3(startShop.xaxis, -1 * startShop.yaxis, Config.shopHeight) ep = new THREE.Vector3(endShop[i].xaxis, -1 * endShop[i].yaxis, Config.shopHeight) let s = Math.sqrt( (endShop[i].xaxis - startShop.xaxis) * (endShop[i].xaxis - startShop.xaxis) + (endShop[i].yaxis - startShop.yaxis) * (endShop[i].yaxis - startShop.yaxis) ) cp1 = new THREE.Vector3( startShop.xaxis + (endShop[i].xaxis - startShop.xaxis) / 3, -1 * startShop.yaxis - (endShop[i].yaxis - startShop.yaxis) / 3, Config.shopHeight + s / 5 ) cp2 = new THREE.Vector3(endShop[i].xaxis, -1 * endShop[i].yaxis, Config.shopHeight + s / 3) Map_QM.mapArr[Config.selectBuild][Config.selectFloor].lineObj.add(Config.drawDashedLine(sp, ep, s / 10, color, cp1, cp2)) } } else { endShop = Map_QM.shopNumToNavPoint({ shopNum: endShop }, 'shop') sp = new THREE.Vector3(startShop.xaxis, -1 * startShop.yaxis, Config.shopHeight) ep = new THREE.Vector3(endShop.xaxis, -1 * endShop.yaxis, Config.shopHeight) let s = Math.sqrt((endShop.xaxis - startShop.xaxis) * (endShop.xaxis - startShop.xaxis) + (endShop.yaxis - startShop.yaxis) * (endShop.yaxis - startShop.yaxis)) cp1 = new THREE.Vector3( startShop.xaxis + (endShop.xaxis - startShop.xaxis) / 3, -1 * startShop.yaxis - (endShop.yaxis - startShop.yaxis) / 3, Config.shopHeight + s / 5 ) cp2 = new THREE.Vector3(endShop.xaxis, -1 * endShop.yaxis, Config.shopHeight + s / 3) Map_QM.mapArr[Config.selectBuild][Config.selectFloor].lineObj.add(Config.drawDashedLine(sp, ep, s / 10, color, cp1, cp2)) } } }, /** * @api {方法} drawColumnar(source,property) 绘制柱状图 * @apiGroup 地图交互 * @apiDescription 绘制柱状图 * @apiVersion 1.0.1 * * @apiParam source 起始店铺编号或编号数组 * @apiParam property 控制参数对象 * * @apiSampleRequest off * * @apiParamExample 请求示例 * //绘制多个 * Map_QM.drawColumnar(['L117','L127','L130'],{'height':200,'width':20,'color':'#2246d8'}) * //绘制单个 * Map_QM.drawColumnar('L125',{'height':200,'width':20,'color':'#2246d8'}) */ drawColumnar: function (source, property) { if (Array.isArray(source)) { if (source.length == 0) { return { msg: '不能解析空数组' } } for (let i = 0; i < source.length; i++) { Map_QM.drawOnlyColumer(source[i], property) } } else { Map_QM.drawOnlyColumer(source, property) } }, drawOnlyColumer: function (shopNum, property) { if (shopNum.trim() != '' && property) { endShop = Map_QM.shopNumToNavPoint({ shopNum: shopNum }, 'shop') let geometry = new THREE.BoxGeometry(property.width * 2, property.width * 2, property.width * 2) let c = new THREE.Color(property.color) let material = new THREE.ShaderMaterial({ uniforms: { targetColor: { value: new THREE.Vector3(c.r, c.g, c.b) }, height: { value: property.height / 5 } }, transparent: true, opacity: property.opacity, //depthTest:false, vertexShader: [ 'varying vec3 modelPos;', 'void main() {', ' modelPos = position;', ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', '}' ].join('\n'), fragmentShader: [ 'uniform vec3 targetColor;', 'uniform float height;', 'varying vec3 modelPos;', 'void main() {', ' gl_FragColor = vec4(targetColor.xyz,(1.0 - modelPos.y/height)*(1.0 - modelPos.y/height));', '}' ].join('\n') }) //let material = new THREE.MeshPhongMaterial( { color: property.color, opacity: property.opacity, transparent:true, side: THREE.DoubleSide } ); let mesh = new THREE.Mesh(geometry, material) mesh.position.set(endShop.xaxis, -1 * endShop.yaxis, property.height) mesh.rotation.x = Math.PI / -2 mesh.scale.setY(property.height / property.width) let material_line for (let k = 0; k < Config.lineBasicMaterialArr.length; k++) { let color2 = new THREE.Color(property.color) if (Config.lineBasicMaterialArr[k].color.equals(color2)) { material_line = Config.lineBasicMaterialArr[k] } } if (!material_line) { material_line = new THREE.LineBasicMaterial({ color: property.color }) //材质对象lineColor Config.lineBasicMaterialArr.push(material_line) } let cubeEdges = new THREE.EdgesGeometry(geometry, 60) mesh.add(new THREE.LineSegments(cubeEdges, material)) mesh.userData.type = 'columer' Map_QM.mapArr[Config.selectBuild][Config.selectFloor].lineObj.add(mesh) } }, /** * @api {方法} removeDrawEle(type) 删除绘制元素 * @apiGroup 地图交互 * @apiDescription 删除绘制元素 * @apiVersion 1.0.1 * * @apiParam type 传入删除的类型 toLine--引导线 columer--柱状样式 all---所有 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.removeDrawEle('toLine') * */ removeDrawEle: function (type) { let lineObj = Map_QM.mapArr[Config.selectBuild][Config.selectFloor].lineObj for (let j = lineObj.children.length - 1; j >= 0; j--) { console.log(lineObj.children[j].userData) if (type == 'all' || lineObj.children[j].userData.type == type) { lineObj.remove(lineObj.children[j]) } } }, /** * @api {方法} rotationAngle(angle) 改变水平角度 * @apiGroup 地图显示 * @apiDescription 改变地图水平角度 angle>-180 && angle<180 * @apiVersion 1.0.0 * * @apiParam {int} angle 旋转角度 * * @apiSampleRequest off * * @apiParamExample {int} 请求示例 * * Map_QM.rotationAngle(90); * */ rotationAngle: function (angle) { Map_QM.controls.setRotateHorizontal((angle / 180) * Math.PI) }, /** * @api {方法} setCameraDist(cDist) 调整地图大小 * @apiGroup 地图显示 * @apiDescription 调整地图大小(值越小地图越大) Config.mapDistance.min ~ Config.mapDistance.max * @apiVersion 1.0.0 * * @apiParam {int} cDist 摄像头距离 * * @apiSampleRequest off * * @apiParamExample {int} 请求示例 * * Map_QM.setCameraDist(150); * */ setCameraDist: function (cDist) { clearTimeout(oTime) oTime = setTimeout(() => { clearTimeout(oTime) if (Config.mapDistance.min < parseInt(cDist) && parseInt(cDist) < Config.mapDistance.max) { let oldObj = { dis: Map_QM.controls.getDistance() } TweenMax.killAll(true) TweenMax.to(oldObj, 0.5, { dis: cDist, onUpdate: function () { Map_QM.controls.setDistance(oldObj.dis) Map_QM.collisionChock() } }) } }, 200) }, /** * @api {方法} startRender() 启动地图渲染 * @apiGroup 地图显示 * @apiDescription 启动地图渲染 与 cancelRender 配合使用可节约资源 * @apiVersion 1.0.0 * * @apiSampleRequest off * @apiParamExample 请求示例 * * Map_QM.startRender(); * */ startRender: function () { Map_QM.cancelRender() renderFrame = requestAnimationFrame(Map_QM.startRender) let T = clock.getDelta() timeS = timeS + T // requestAnimationFrame默认调用render函数60次,通过时间判断,降低renderer.render执行频率 if (timeS > 0.03) { if (Map_QM.qiModel) { Map_QM.qiModel.rotateY(0.05) } if (Map_QM.endModel) { Map_QM.endModel.rotateY(0.05) } Map_QM.controls.update() Map_QM.renderer.render(Map_QM.scene, Map_QM.camera) Map_QM.labelRenderer.render(Map_QM.scene, Map_QM.camera) for (let item of Map_QM.mixers) { item.update(T) } //timeS置0 timeS = 0 } }, /** * @api {方法} cancelRender() 取消地图渲染 * @apiGroup 地图显示 * @apiDescription 取消地图渲染 与 startRender 配合使用可节约资源 * @apiVersion 1.0.0 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.cancelRender(); * */ cancelRender: function () { window.cancelAnimationFrame(renderFrame) renderFrame = -1 }, /** * @api {方法} showFloor(floorOrder) 楼层切换 * @apiGroup 地图交互 * @apiDescription 楼层切换,传入楼层编号,编号从下到上排序,最下面是0 * @apiVersion 1.0.0 * * @apiParam {int} floorOrder 楼层编号 * * @apiSampleRequest off * * @apiParamExample {int} 请求示例 * * Map_QM.showFloor(1); * */ showFloor: function (fIndex = -1) { isPathState = false if (Map_QM.qiIcon) { Map_QM.qiIcon.visible = false } Map_QM.controls.enabled = true Map_QM.controls.enableRotate = true Map_QM.controls.setZoom(1) Map_QM.controls.minAzimuthAngle = -Infinity Map_QM.controls.maxAzimuthAngle = Infinity Map_QM.dispatchEvent({ type: 'changeFloorinit', data: Map_QM.selectFloor }) Map_QM.clearFloor(fIndex) if (Config.cameraDist.state == '3D') { Map_QM.resetMeDir() } else { if (Map_QM.dirIcon) { Map_QM.dirIcon.visible = true } } clearTimeout(outTime) Map_QM.forShopArr.length = 0 forShopArr = { direction: '', wayList: [] } if (fIndex != -1) { Map_QM.changeFloorInner(fIndex) } Map_QM.controls.reset() if (Config.showlap) { Map_QM.controls.maxPolarAngle = Math.PI / 2 - 0.3 Map_QM.controls.minPolarAngle = 1.2 } else { Map_QM.controls.maxPolarAngle = Math.PI / 2 - 0.3 Map_QM.controls.minPolarAngle = 0.3 } // let angle = Config.deviceAng?Config.deviceObj.angle : Config.mapAngle; // Map_QM.rotationAngle(angle); }, /** * @api {方法} addElementLabel(divObj,x=0,y=0,type="2d_pop") 地图html标签 * @apiGroup 地图交互 * @apiDescription 地图显示Html标签 * @apiVersion 1.0.0 * @apiParam {Element} divObj div对象 * @apiParam {int} x 显示X坐标 * @apiParam {int} y 显示Y坐标 * @apiParam {string} type 对象标识 * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * Map_QM.addElementLabel(divObj,0,0,"shopInfo"); * */ addElementLabel: function (divObj, x = 0, y = 0, type = 'shopInfo') { Map_QM.elementDestroy(type) let shopInfo = new THREE.CSS2DObject(divObj) shopInfo.position.set(x, -1 * y, type === 'shopInfo' ? (Config.shopHeight || 50) + 100 : 150) shopInfo.applyMatrix(Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.matrix) shopInfo.userData.type = type shopInfo.name = 'shopInfo' Map_QM.CSSObject.add(shopInfo) return shopInfo }, /** * @api {方法} updateElementPosition(3DObj,x=0,y=0) 修改标签位置 * @apiGroup 地图交互 * @apiDescription 修改标签位置 * @apiVersion 1.0.0 * @apiParam {Object} obj 对象 * @apiParam {int} x 新的X坐标 * @apiParam {int} y 新的Y坐标 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.updateElementPosition("2d_pop"); * */ updateElementPosition: function (obj, x, y) { if (obj.hasOwnProperty('position')) { let h = Config.shopHeight || 50 obj.position.set(x, -1 * y, h) obj.applyMatrix(Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.matrix) } }, /** * @api {方法} elementDestroy(divObj,x=0,y=0,type="2d_pop") 销毁地图标签 * @apiGroup 地图交互 * @apiDescription 销毁地图上的html标签 * @apiVersion 1.0.0 * @apiParam {string} type 对象标识 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.elementDestroy("2d_pop"); * */ elementDestroy: function (type = '2d_pop') { for (let i = Map_QM.CSSObject.children.length - 1; i >= 0; i--) { if (Map_QM.CSSObject.children[i].userData && Map_QM.CSSObject.children[i].userData.type == type) { Map_QM.CSSObject.remove(Map_QM.CSSObject.children[i]) } } }, /** * @api {方法} initSingleDevice(floorOrder,code) 设置标签背景色 * @apiGroup 地图交互 * @apiDescription 设置标签背景色 * @apiVersion 1.0.0 * @apiParam {int} floorOrder 标签所在楼层 * @apiParam {string} code 对象标识 * @apiParam {string} color 选中颜色 (可选) * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.initSingleDevice(0,""); */ initSingleDevice: function (floorOrder, code, color = '#5F4E41', build = 0) { Map_QM.mapArr[build][floorOrder].showTagObj.traverse(obj => { if (obj.element && obj.userData.code == code) { obj.element.style.background = color } }) }, /** * @api {方法} clearDeviceSelectState() 清除标签选中 * @apiGroup 地图交互 * @apiDescription 清除标签选中状态 * @apiVersion 1.0.0 * @apiParam {int} floorOrder 标签所在楼层 * @apiParam {string} code 对象标识 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.clearDeviceSelectState(); */ clearDeviceSelectState: function () { for (let t = 0; t < Map_QM.mapArr.length; t++) { for (let i = 0; i < Map_QM.mapArr[t].length; i++) { Map_QM.mapArr[t][i].showTagObj.traverse(obj => { if (obj.element) { obj.element.style.background = '#777777' } }) } } }, //////////////////////////////////////////////////////////////////////////////// /** * 内部楼层切换 * @param {int} fIndex 楼层编号 * @param {int} midIndex 如果是叠层,需要传入 最多三层 * @param {int} endIndex 如果是叠层,需要传入 最多三层 */ changeFloorInner: function (fIndex = -1, endIndex = -1, midIndex = -1) { Map_QM.clearFloor(Config.selectFloor) pathShop = '' let addFloor = fIndex - Config.selectFloor > 0 ? 1 : -1 if (fIndex != -1) { Config.selectFloor = fIndex } for (let t = 0; t < Map_QM.mapArr.length; t++) { for (let i = 0; i < Map_QM.mapArr[t].length; i++) { Map_QM.mapArr[t][i].allObj.visible = t == Config.selectBuild ? true : false Map_QM.mapArr[t][i].labelObj.traverse(obj => { obj.visible = false }) Map_QM.mapArr[t][i].iconLabel.traverse(obj => { obj.visible = false }) Map_QM.mapArr[t][i].tagObj.traverse(obj => { obj.visible = false }) Map_QM.mapArr[t][i].showTagObj.traverse(obj => { obj.visible = false }) } } if (Map_QM.mapArr[Config.selectBuild][Config.selectFloor]) { Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.visible = true } else { return } for (let i = 0; i < Map_QM.mapArr[Config.selectBuild].length; i++) { if (Config.showlap) { let moveZ = (i - Config.selectFloor) * Config.doubleDist Map_QM.mapArr[Config.selectBuild][i].labelObj.traverse(obj => { obj.visible = false }) Map_QM.mapArr[Config.selectBuild][i].iconLabel.traverse(obj => { obj.visible = false }) Map_QM.mapArr[Config.selectBuild][i].showTagObj.traverse(obj => { obj.visible = false }) TweenMax.to(Map_QM.mapArr[Config.selectBuild][i].allObj.position, 0.3, { y: moveZ, onComplete: function () { Map_QM.mapArr[Config.selectBuild][i].allObj.visible = true Map_QM.mapArr[Config.selectBuild][i].iconLabel.traverse(obj => { obj.visible = true }) Map_QM.mapArr[Config.selectBuild][i].showTagObj.traverse(obj => { obj.visible = true }) if (i == Config.selectFloor) { Map_QM.mapArr[Config.selectBuild][i].labelObj.traverse(obj => { obj.visible = true }) } } }) } else { if (i == Config.selectFloor || (i == midIndex && Config.overlap) || (i == endIndex && Config.overlap)) { Map_QM.mapArr[Config.selectBuild][i].allObj.visible = true Map_QM.mapArr[Config.selectBuild][i].labelObj.traverse(obj => { obj.visible = true }) Map_QM.mapArr[Config.selectBuild][i].iconLabel.traverse(obj => { obj.visible = true }) Map_QM.mapArr[Config.selectBuild][i].showTagObj.traverse(obj => { obj.visible = true }) } else { Map_QM.mapArr[Config.selectBuild][i].allObj.visible = false } if (i == Map_QM.mapArr[Config.selectBuild].length - 1 && !Config.showlap) { if (Config.overlap) { //叠层 if (midIndex === -1 && endIndex !== -1) { //两层 Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.position.y = Config.selectFloor < endIndex ? -1 * Config.doubleDist : Config.doubleDist Map_QM.mapArr[Config.selectBuild][endIndex].allObj.position.y = Config.selectFloor > endIndex ? -1 * Config.doubleDist : Config.doubleDist } else if (endIndex === -1) { //一层 TweenMax.fromTo( Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.position, 0.5, { y: addFloor * Config.doubleDist }, { y: 0, ease: Cubic.easeIn } ) //Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.position.y=0; } else { //三层 Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.position.y = 0 Map_QM.mapArr[Config.selectBuild][midIndex].allObj.position.y = 0 Map_QM.mapArr[Config.selectBuild][endIndex].allObj.position.y = 0 let minF = Math.min(midIndex, Config.selectFloor, endIndex) let maxF = Math.max(midIndex, Config.selectFloor, endIndex) Map_QM.mapArr[Config.selectBuild][minF].allObj.position.y = -1 * Config.doubleDist Map_QM.mapArr[Config.selectBuild][maxF].allObj.position.y = Config.doubleDist } } else { //Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.position.y=0; TweenMax.fromTo(Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.position, 0.5, { y: addFloor * Config.doubleDist }, { y: 0, ease: Cubic.easeIn }) } Map_QM.timeOutInit() } } } }, /** * @param {Object} e * 地图BOX点击 */ onMouseClickBox: function (event) { clearTimeout(oTime) oTime = setTimeout(() => { clearTimeout(oTime) let mouse = new THREE.Vector2() mouse.x = (event.offsetX / Map_QM.w) * 2 - 1 mouse.y = -(event.offsetY / Map_QM.h) * 2 + 1 Map_QM.onCallTouchORMouse(mouse) }, 200) }, onCallTouchORMouse: function (mouse) { if (!Map_QM.mapArr[Config.selectBuild][Config.selectFloor] || isPathState) { return } let raycaster = new THREE.Raycaster() raycaster.setFromCamera(mouse, Map_QM.camera) let intersects = raycaster.intersectObjects(Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.children, true) let clickShop = false for (let i = 0; i < intersects.length; i++) { //店铺BOX点击 if (intersects[i].object.userData && intersects[i].object.userData.type == 'shop' && intersects[i].object.name != 'shop' && intersects[i].object.name != '') { if (Map_QM.endModel && Map_QM.endModel.visible) { Map_QM.endModel.visible = false } clickShop = true Map_QM.mapArr[Config.selectBuild][Config.selectFloor].findPath.clearPath() Map_QM.setSelectShopMat(intersects[i].object) break } } Map_QM.dispatchEvent({ type: 'shop', data: clickShop ? Map_QM.selectShop?.userData : null }) //图标点击 for (let i = 0; i < intersects.length; i++) { //图标点击 if (intersects[i].object.userData && intersects[i].object.userData.type == 'icon') { if (Map_QM.endModel && Map_QM.endModel.visible) { Map_QM.endModel.visible = false } Map_QM.mapArr[Config.selectBuild][Config.selectFloor].findPath.clearPath() Map_QM.dispatchEvent({ type: 'icon', data: intersects[i].object.userData }) break } } //设备点击 for (let i = 0; i < intersects.length; i++) { //图标点击 if (intersects[i].object.userData && intersects[i].object.userData.type == 'device') { if (Map_QM.endModel && Map_QM.endModel.visible) { Map_QM.endModel.visible = false } Map_QM.mapArr[Config.selectBuild][Config.selectFloor].findPath.clearPath() Map_QM.dispatchEvent({ type: 'device', data: intersects[i].object.userData }) break } } }, //改变选中店铺box setSelectShopMat: function (selObject, logoW = 0, url = '') { TweenMax.killAll(true) if (Map_QM.selectShop) { Map_QM.selectShop.scale.z = 1 } Map_QM.selectShop = selObject TweenMax.to(Map_QM.selectShop.scale, 0.5, { z: 3, repeat: 4, yoyo: true, ease: Cubic.easeIn, onComplete: function () { if (Map_QM.selectShop) { TweenMax.to(Map_QM.selectShop.scale, 0.5, { z: 1 }) } } }) }, /** * 初始化后调用 */ timeOutInit: function () { if (Map_QM.callBackLoadOver) { let floorData = [] for (let i = 0; i < Config.allMap.length; i++) { let build = [] for (let j = 0; j < Config.allMap[i].buildArr.length; j++) { if (Config.allMap[i].buildArr[j]) { build.push({ order: Config.allMap[i].buildArr[j].order, name: Config.allMap[i].buildArr[j].name }) } } floorData.push(build) } if (Map_QM.backObj) { Map_QM.controls.enabled = true Map_QM.backObj.data = floorData } Map_QM.callBackLoadOver(Map_QM.backObj) //初始化完成后回调 Map_QM.callBackLoadOver = null Map_QM.backObj = null } Map_QM.dispatchEvent({ type: 'changeFloorOver', data: Map_QM.selectFloor }) Map_QM.collisionChock() }, /** * 碰撞检测 * @param {Object} 传入检测楼层下标 */ collisionChock: function () { if (!Config.collision) { return } Map_QM.dispatchEvent({ type: 'mapAngleChange', data: { hAngle: Map_QM.controls.getRotateHorizontal(), vAngle: Map_QM.controls.getRotate() } }) outTime = setTimeout(() => { clearTimeout(outTime) let checkList = [] if (Config.overlap) { for (let i = 0; i < Map_QM.mapArr[Config.selectBuild].length; i++) { Map_QM.mapArr[Config.selectBuild][i].allObj.traverse(obj => { if (obj.name == 'floor' && Map_QM.mapArr[Config.selectBuild][i].allObj.visible) { // checkList.push(obj) } }) } } if (Config.showlap) { //叠层展示 for (let i = 0; i < Map_QM.mapArr[Config.selectBuild].length; i++) { Map_QM.mapArr[Config.selectBuild][i].allObj.traverse(obj => { if (obj.name == 'floor' && Map_QM.mapArr[Config.selectBuild][i].allObj.visible && i > Config.selectFloor) { // checkList.push(obj) } }) } } if (Map_QM.mapArr[Config.selectBuild]) { for (let m = 0; m < Map_QM.mapArr[Config.selectBuild].length; m++) { if (Map_QM.mapArr[Config.selectBuild][m].allObj.visible) { let IconChilds = Map_QM.mapArr[Config.selectBuild][m].iconLabel.children IconChilds.forEach(item => { item.element.style.visibility = 'visible' }) let childs = Map_QM.mapArr[Config.selectBuild][m].labelObj.children childs.forEach(item => { item.element.style.visibility = 'visible' }) let svgChilds = Map_QM.mapArr[Config.selectBuild][m].svgObj.children let rat = Map_QM.controls.getRotateHorizontal() svgChilds.forEach(item => { if (Math.abs(item.rotation.x) < 0.5) { if (rat - item.userData.rot > 1.7 || rat - item.userData.rot < -1.7) { item.rotation.z = item.userData.rot < 0 ? item.userData.rot + 3.1415926 : item.userData.rot - 3.1415926 } else { item.rotation.z = item.userData.rot } } }) let len = childs.length for (let i = 0; i < len; i++) { let obj = childs[i].element if (obj.style.visibility == 'visible' && obj.style.transform) { let labP = obj.style.transform.split('translate')[2].split(', ') for (let j = i + 1; j < len; j++) { let pb = childs[j].element.style.transform.split('translate')[2].split(', ') let isCol = Config.isCollision( new Config.Point(labP[0].substring(1, labP[0].length - 2), labP[1].substring(0, labP[1].length - 3)), obj.clientWidth, obj.clientHeight, new Config.Point(pb[0].substring(1, pb[0].length - 2), pb[1].substring(0, pb[1].length - 3)), childs[j].element.clientWidth, childs[j].element.clientHeight ) if (isCol) { childs[j].element.style.visibility = 'hidden' } } } } let iLen = IconChilds.length for (let ii = 0; ii < iLen; ii++) { let objIcon = IconChilds[ii].element if (objIcon.style.visibility == 'visible' && objIcon.style.transform) { let labPIcon = objIcon.style.transform.split('translate')[2].split(', ') for (let jj = ii + 1; jj < iLen; jj++) { let pbe = IconChilds[jj].element.style.transform.split('translate')[2].split(', ') let isCol2 = Config.isCollision( new Config.Point(labPIcon[0].substring(1, labPIcon[0].length - 2), labPIcon[1].substring(0, labPIcon[1].length - 3)), objIcon.clientWidth, objIcon.clientHeight, new Config.Point(pbe[0].substring(1, pbe[0].length - 2), pbe[1].substring(0, pbe[1].length - 3)), IconChilds[jj].element.clientWidth, IconChilds[jj].element.clientHeight ) if (isCol2) { IconChilds[jj].element.style.visibility = 'hidden' } } } } if (Config.overlap || Config.showlap) { let renChild = Map_QM.mapArr[Config.selectBuild][m].showTagObj.children renChild.forEach(item => { if (item.element.style.visibility == 'visible') { let check = Config.blocked(item.element, checkList, m) if (check) { item.element.style.visibility = 'hidden' } } }) childs.forEach(item => { if (item.element.style.visibility == 'visible') { let check = Config.blocked(item.element, checkList, m) if (check) { item.element.style.visibility = 'hidden' } } }) IconChilds.forEach(item => { if (item.element.style.visibility == 'visible') { let check = Config.blocked(item.element, checkList, m) if (check) { item.element.style.visibility = 'hidden' } } }) } } } } }, 100) }, /** * 寻路---------------------------------------------------------------------------------------------------------------------------------------- */ /** * @api {方法} bounceIcon({type:"xsj"}) 图标弹跳 * @apiGroup 地图导航 * @apiDescription 地图图标弹跳效果 * @apiVersion 1.0.0 * @apiParam {String} iconType 设施缩写 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.bounceIcon({type:"xsj"}); * */ bounceIcon: function (iconType) { let toFloor = parseInt(Config.selectFloor) let facs = Map_QM.mapArr[Config.selectBuild][toFloor].serObj.children //交通图标 for (let i = 0; i < facs.length; i++) { if (facs[i].type == 'Sprite') { facs[i].reSetSite() if (facs[i].facCode == iconType) { facs[i].jumpIcon() } } } }, /** * @api {方法} pathIcon({type:"xsj"}) 获取最近设施 * @apiGroup 地图导航 * @apiDescription 获取离当前楼层最近的设施 * @apiVersion 1.0.0 * @apiParam {JSON} type 设施缩写 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.pathIcon({type:"xsj"}); * * @apiSuccessExample 返回示例 * { * "floor": 楼层编号, "node": 设施导航点,"typeCode":设施编号 * } * */ pathIcon: function (iconType) { let selIcon // let minS = -1 toIcon = iconType.type for (let i = 0; i < Map_QM.mapArr[parseInt(Config.deviceObj.build)].length; i++) { let sers = Map_QM.mapArr[parseInt(Config.deviceObj.build)][i].serObj.children //服务图标 for (let n = 0; n < sers.length; n++) { if (sers[n].facCode == iconType.type) { if (!selIcon) { selIcon = sers[n] minS = Math.abs(sers[n].position.x - Config.deviceObj.xaxis) + Math.abs(-1 * Config.deviceObj.yaxis - sers[n].position.y) + Math.abs(i - Config.deviceObj.floor) * 3000 } else { let s1 = Math.abs(sers[n].position.x - Config.deviceObj.xaxis) + Math.abs(-1 * Config.deviceObj.yaxis - sers[n].position.y) + Math.abs(i - Config.deviceObj.floor) * 3000 if (s1 < minS) { minS = s1 selIcon = sers[n] } } } } } if (selIcon) { return { build: selIcon.build, floor: selIcon.floor, node: selIcon.navCode, site: { x: selIcon.position.x, y: -selIcon.position.y }, typeCode: Config.getFacType(iconType.type) } } else { // 当前楼栋没有 let startNade = Config.deviceObj.build + '_' + Config.startObj.floor + '_' + Config.startObj.node for (let b = 0; b < Map_QM.mapArr.length; b++) { if (b != parseInt(Config.deviceObj.build)) { for (let i = 0; i < Map_QM.mapArr[b].length; i++) { let sers = Map_QM.mapArr[b][i].serObj.children //服务图标 for (let n = 0; n < sers.length; n++) { if (sers[n].facCode == iconType.type) { let toNade = sers[n].build + '_' + sers[n].floor + '_' + sers[n].navCode if (!selIcon) { selIcon = sers[n] let path = dijkstra.find_path(ftPath, startNade, toNade) minS = path.cost } else { let path = dijkstra.find_path(ftPath, startNade, toNade) let s1 = path.cost if (s1 < minS) { minS = s1 selIcon = sers[n] } } } } } } } } if (selIcon) { return { build: selIcon.build, floor: selIcon.floor, node: selIcon.navCode, site: { x: selIcon.position.x, y: -selIcon.position.y }, typeCode: Config.getFacType(iconType.type) } } }, /** * @api {方法} pathByStartAndOver(startObj,toObj,callBackFun) 设置地图点位 * @apiGroup 地图导航 * @apiDescription 根据传入的起、终点;直接导航 * @apiVersion 1.0.0 * @apiParam {int} floor 楼层编号 * @apiParam {String} shopNum 店铺编号/车位编号 * @apiParam {String} type "shop"/"park" * @apiParam {Function} callBackFun 回调函数 * * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.pathByStartAndOver({"floor":3,"shopNum":"L102","type":"shop"},{"floor":4,"shopNum":"L204","type":"shop"}); * */ pathByStartAndOver: function (startObj, toObj, callBackFun) { try { Config.startObj = this.shopNumToNavPoint(startObj, startObj.type) let overObj = this.shopNumToNavPoint(toObj, toObj.type) this.pathNode(overObj, callBackFun) } catch (e) { return '传入点位无法导航' } }, /** * @api {方法} changeStartPoint({type:"xsj"}) 设置起始点位 * @apiGroup 地图导航 * @apiDescription 设置起始点位 * @apiVersion 1.0.0 * @apiParam {int} floor 起点楼层编号 * @apiParam {String} shopNum 起点店铺编号 * * @apiSampleRequest off * * @apiParamExample {String} 请求示例 * * Map_QM.changeStartPoint({"floor":3,"shopNum":"6220"}); * */ changeStartPoint: function (toObj) { if (toObj) { Map_QM.clearFloor() Config.startObj = this.shopNumToNavPoint(toObj, 'shop') } else { Map_QM.clearFloor() let pathData = Config.allMap[parseInt(Config.deviceObj.build)].buildArr[parseInt(Config.deviceObj.floor)].mapData.path Config.deviceObj.xaxis = pathData.nodes[parseInt(Config.deviceObj.node)].x Config.deviceObj.yaxis = pathData.nodes[parseInt(Config.deviceObj.node)].y Config.startObj = Config.deviceObj } //Config.selectBuild = 0; }, /** * @api {方法} changeMapIPState(ipName,color) 改变POI颜色 * @apiGroup 地图交互 * @apiDescription 改变POI 颜色 * @apiVersion 1.0.0 * @apiParam {string} ipName POI名称 * @apiParam {string} color 颜色字符串 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.changeMapIPState("L1001","#ffff00"); * */ changeMapIPState: function (ipName, color) { // #775544 #AD8164 for (let i = 0; i < Map_QM.mapArr[Config.selectBuild].length; i++) { let shopArr = Map_QM.mapArr[Config.selectBuild][i].allObj.children for (let k = 0; k < shopArr.length; k++) { if (shopArr[k].name == ipName) { shopArr[k].material = Config.getMeshMaterial(color || 0xeab16e) break } } } }, setSelectShopMatByName: function (ipName) { // #775544 #AD8164 for (let i = 0; i < Map_QM.mapArr[Config.selectBuild].length; i++) { let shopArr = Map_QM.mapArr[Config.selectBuild][i].allObj.children for (let k = 0; k < shopArr.length; k++) { if (shopArr[k].name == ipName) { Map_QM.setSelectShopMat(shopArr[k]) break } } } }, /** * @api {方法} getMapIPData(ipName) 获取POI 基础数据 * @apiGroup 地图交互 * @apiDescription 获取POI 基础数据 * @apiVersion 1.0.0 * @apiParam {string} ipName POI名称 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.getMapIPData("L1001"); * * @apiSuccessExample 返回示例 * { * shopNum:店铺编号, shopName:店铺名 node:导航点, floor:楼层编号, xaxis:中心点X坐标, yaxis:中心点Y坐标, borderColor:边框色, entColor:填充色 * } */ getMapIPData: function (ipName) { for (let i = 0; i < Map_QM.mapArr[Config.selectBuild].length; i++) { let shopArr = Map_QM.mapArr[Config.selectBuild][i].allObj.children for (let k = 0; k < shopArr.length; k++) { if (shopArr[k].name == ipName) { return shopArr[k].userData } } } }, /** * @api {方法} pathNode() 地图模拟导航 * @apiGroup 地图导航 * @apiDescription 地图路径模拟导航 * @apiVersion 1.0.0 * @apiParam {int} floor 楼层编号 * @apiParam {string} node 路径点位编号 * @apiParam {function} callBackFun 回调方法 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.pathNode({floor:2,node:"53"},callBackFun); * */ pathNode: function (toObj, callBackFun) { Map_QM.clearFloor(-1, true) Map_QM.selectShop = null pathShop = '' isPathState = true if (!Config.startObj.xaxis && !Config.startObj.yaxis) { Map_QM.changeStartPoint(null) } outTime = setTimeout(() => { clearTimeout(outTime) Config.overObj = null Config.overObj = { floor: toObj.floor, node: toObj.node, shopNum: toObj.shopNum } if (Config.overObj.node != '') { Map_QM.cancelRender() this.onFindPathModel() if (Config.overlap) { //叠层导航--固定地图仰角 Map_QM.controls.maxPolarAngle = 1.17 Map_QM.controls.minPolarAngle = 1.17 } if (callBackFun) { Map_QM.parseForShopArr() const data = JSON.parse(JSON.stringify(forShopArr)) callBackFun(data) } Map_QM.startRender() } }, 200) }, parseForShopArr: function () { forShopArr = { direction: '', wayList: [] } Map_QM.forShopArr.forEach((item, index) => { if (item.hasOwnProperty('Direction')) { forShopArr.direction = item.Direction } if (item.hasOwnProperty('wayShop')) { for (let i = 0; i < item.wayShop.length; i++) { let enTlite = item.wayShop[i].shop.shopNameEn == '' ? item.wayShop[i].shop.shopName : item.wayShop[i].shop.shopNameEn forShopArr.wayList.push({ isFacility: false, shopName: '经过 ' + item.wayShop[i].shop.shopName, shopNameEn: 'PASS ' + enTlite, logoUrl: item.wayShop[i].shop.logoUrl, shopId: item.wayShop[i].shop.shopId }) } } if (item.hasOwnProperty('Facilities')) { //设施 forShopArr.wayList.push({ isFacility: true, shopName: '乘坐 ' + item.Facilities.userData.title + '到 ' + Map_QM.mapArr[Config.selectBuild][parseInt(Map_QM.forShopArr[index + 1].floor)].floorName, shopNameEn: 'BY ' + item.Facilities.userData.title + 'TO ' + Map_QM.mapArr[Config.selectBuild][parseInt(Map_QM.forShopArr[index + 1].floor)].floorName, logoUrl: item.Facilities.imgUrl, shopId: '' }) } }) }, /** * @api {方法} getGapByPathNode() 获取实际距离 * @apiGroup 地图导航 * @apiDescription 通过点位获取距离 * @apiVersion 1.0.0 * @apiParam {int} floor 楼层编号 * @apiParam {string} node 路径点位编号 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.getGapByPathNode({floor:2,node:"53"}); * * @apiSuccessExample {Object} dist 距离米数 time 步行时间 * { * dis:200,time:4 * } */ getGapByPathNode: function (toObj) { let startNade = Config.selectBuild + '_' + Config.deviceObj.floor + '_' + Config.deviceObj.node let toNade = Config.selectBuild + '_' + toObj.floor + '_' + toObj.node let path = { cost: -1 }, minTime = 1 try { path = dijkstra.find_path(graphPath, startNade, toNade) minTime = parseInt(path.cost / Config.mapScale / 50) < 0.5 ? 0.5 : parseInt(path.cost / Config.mapScale / 50) } catch (e) { console.log(e) } return { dis: parseInt(path.cost / Config.mapScale), time: minTime } }, /** * @api {方法} getAllIcon() 获取所有Icon * @apiGroup 地图数据 * @apiDescription 获取所有Icon * @apiVersion 1.0.0 * @apiParam {int} floorOrder 楼层编号(可选) 不传返回所有 * * @apiSampleRequest off * */ getAllIcon: function (floorOrder = -1, buildOrder = -1) { let icons = [] if (floorOrder != -1) { let sers = Map_QM.mapArr[Config.selectBuild][floorOrder].serObj.children //服务图标 for (let n = 0; n < sers.length; n++) { if (sers[n].type == 'Sprite') { let title = sers[n].userData.title let titleEn = Config.iconEn[title] let type = sers[n].facCode let imgUrl = sers[n].imgUrl if (sers[n].facCode == 'upft' || sers[n].facCode == 'downft' || sers[n].facCode == 'ft') { if (title == '上扶梯' || title == '下扶梯') { title = '扶梯' } titleEn = Config.iconEn['扶梯'] type = 'ft' } let icon = { type: type, floor: floorOrder, imgUrl: imgUrl, poid: sers[n].userData.poid, title: title, titleEn: titleEn } icons.push(icon) } } return icons } for (let j = 0; j < Map_QM.mapArr.length; j++) { let iconBuild = [] for (let i = 0; i < Map_QM.mapArr[j].length; i++) { let iconArr = [] if (Map_QM.mapArr[j][i].serObj) { let sers = Map_QM.mapArr[j][i].serObj.children //服务图标 for (let n = 0; n < sers.length; n++) { if (sers[n].type == 'Sprite') { let title = sers[n].userData.title let titleEn = Config.iconEn[title] let type = sers[n].facCode let imgUrl = sers[n].imgUrl if (sers[n].facCode == 'upft' || sers[n].facCode == 'downft' || sers[n].facCode == 'ft') { title = '扶梯' titleEn = Config.iconEn[title] type = 'ft' imgUrl = './static/img/ft.png' } let icon = { type: type, floor: i, imgUrl: imgUrl, poid: sers[n].userData.poid, title: title, titleEn: titleEn } iconArr.push(icon) } } } iconBuild.push(iconArr) } icons.push(iconBuild) } return icons }, /** * @api {方法} pathPark() 获取车位点位 * @apiGroup 地图数据 * @apiDescription 获取车位导航点 * @apiVersion 1.0.0 * @apiParam {String} shopNum 车位编号 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.pathPark({shopNum:"B1002"}); * * @apiSuccessExample 返回示例 * { * shopNum: 车位编号, node: 导航点, floor: 楼层编号, xaxis: 中心点X坐标, yaxis: 中心点Y坐标 * } */ pathPark: function (toObj) { return this.shopNumToNavPoint(toObj, 'park') }, /** * @api {方法} pathShopByName() 获取店铺点位 * @apiGroup 地图数据 * @apiDescription 通过店铺名称获取点位 * @apiVersion 1.0.0 * @apiParam {String} shopName 店铺名称 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.pathShopByName("金拱门"); * * @apiSuccessExample 返回示例 * { * shopNum: 店铺编号, node: 导航点, floor: 楼层编号, xaxis: 中心点X坐标, yaxis: 中心点Y坐标, comeIn:店铺多门点 * } */ pathShopByName: function (shopName) { for (let item of Config.shopData) { if (item.shopName === shopName) { let toObj = { shopNum: item.houseNumber, node: item.yaxis } for (let h = 0; h < Config.allMap.length; h++) { let sArr = Config.allMap[h].buildArr for (let i = 0; i < sArr.length; i++) { let shops = Config.allMap[h].buildArr[i].mapData.shopArea for (let k = 0; k < shops.length; k++) { if (shops[k].name == toObj.shopNum) { toObj.floor = i toObj.node = shops[k].shopNav toObj.xaxis = shops[k].xaxis toObj.yaxis = shops[k].yaxis if (shops[k].comeIn) { toObj.comeIn = shops[k].comeIn } return toObj } } } } return toObj } } }, /** * @api {方法} shopNumToNavPoint() 获取导航点位 * @apiGroup 地图导航 * @apiDescription 通过店铺编号或车位获取导航点位 * @apiVersion 1.0.0 * @apiParam {Object} object build,floor,shopNum 楼栋编号,楼层编号,店铺或车位编号 * @apiParam {String} type 店铺或车位标识 "shop" "park" * * @apiSampleRequest off * * @apiSuccessExample 返回示例 * { * shopNum: 店铺编号, node: 导航点, floor: 楼层编号, xaxis: 中心点X坐标, yaxis: 中心点Y坐标, comeIn:店铺多门点 * } */ shopNumToNavPoint: function (obj, type) { let shopArr let reObj = { node: '', xaxis: '', yaxis: '', floor: '', comeIn: '' } let buildOrder = Config.selectBuild let floorOrder = -1 if (obj.hasOwnProperty('build')) { buildOrder = parseInt(obj.build) } if (obj.hasOwnProperty('floor')) { floorOrder = parseInt(obj.floor) } let sArr = Config.allMap[buildOrder].buildArr if (floorOrder != -1) { if (type == 'shop') { shopArr = sArr[floorOrder].mapData.shopArea } else if (type == 'park') { shopArr = sArr[floorOrder].mapData.parkArea } for (let k = 0; k < shopArr.length; k++) { if (shopArr[k].name == obj.shopNum) { reObj.floor = i reObj.xaxis = shopArr[k].xaxis reObj.yaxis = shopArr[k].yaxis reObj.node = shopArr[k].shopNav reObj.shopNum = obj.shopNum if (shopArr[k].comeIn) { reObj.comeIn = shopArr[k].comeIn } break } } } else { for (let i = 0; i < sArr.length; i++) { if (type == 'shop') { shopArr = sArr[i].mapData.shopArea } else if (type == 'park') { shopArr = sArr[i].mapData.parkArea } for (let k = 0; k < shopArr.length; k++) { if (shopArr[k].name == obj.shopNum) { reObj.floor = i reObj.xaxis = shopArr[k].xaxis reObj.yaxis = shopArr[k].yaxis reObj.node = shopArr[k].shopNav reObj.shopNum = obj.shopNum if (shopArr[k].comeIn) { reObj.comeIn = shopArr[k].comeIn } break } } } } return reObj }, /** * 模拟导航获取路线 */ onFindPathModel: function (usePath = null) { Map_QM.forShopArr.length = 0 _indexPathFloor = 0 isPathPlay = true Map_QM.dispatchEvent({ type: 'startFindPath', data: '' }) if (parseInt(Config.startObj.node) == -1) { return } if (!Config.startObj.xaxis) { let pathData = Config.allMap[Config.selectBuild].buildArr[parseInt(Config.startObj.floor)].mapData.path Config.startObj.xaxis = pathData.nodes[parseInt(Config.startObj.node)].x Config.startObj.yaxis = pathData.nodes[parseInt(Config.startObj.node)].y } if (!usePath) { usePath = graphPath } Map_QM.forShopPath(usePath) }, forShopPath: function (usePath) { let startNade = Config.selectBuild + '_' + Config.startObj.floor + '_' + Config.startObj.node let toNade = Config.selectBuild + '_' + Config.overObj.floor + '_' + Config.overObj.node let path = dijkstra.find_path(usePath, startNade, toNade) let PathPoint = path.nodes let Dir = '前方', index = 0 let DirEn = 'forward' this.forShopArr = [] if (PathPoint.length > 1) { this.forShopArr.push({ floor: Config.startObj.floor, PathPoint: [] }) let pathData for (let j = 0; j < PathPoint.length; j++) { let array = PathPoint[j].split('_') pathData = Config.allMap[Config.selectBuild].buildArr[parseInt(array[1])].mapData.path if (array[1] == this.forShopArr[index].floor) { this.forShopArr[index].PathPoint.push(pathData.nodes[parseInt(array[2])]) } else { if (j > 0) { this.forShopArr[index].Facilities = this.getFacilIcon(PathPoint[j - 1].split('_')) } else { this.forShopArr[index].Facilities = null } this.forShopArr.push({ floor: parseInt(array[1]), PathPoint: [] }) index++ this.forShopArr[index].PathPoint.push(pathData.nodes[parseInt(array[2])]) } } let dy = 0, dx = 0, ang = 0 if (this.forShopArr[0].PathPoint.length > 1) { if (this.forShopArr[0].PathPoint.length == 2) { dy = this.forShopArr[0].PathPoint[1].y - this.forShopArr[0].PathPoint[0].y dx = this.forShopArr[0].PathPoint[1].x - this.forShopArr[0].PathPoint[0].x } else { dy = this.forShopArr[0].PathPoint[2].y - this.forShopArr[0].PathPoint[1].y dx = this.forShopArr[0].PathPoint[2].x - this.forShopArr[0].PathPoint[1].x } ang = (Math.atan2(dy, dx) * 180) / Math.PI - Config.deviceObj.angle ang = ang > 180 ? ang - 360 : ang ang = ang < -180 ? ang + 360 : ang if (ang < -50 && ang >= -130) { Dir = '向前出发' DirEn = 'forward' } else if (ang >= -50 && ang < 50) { Dir = '向右出发' DirEn = 'right' } else if (ang >= 50 && ang < 130) { Dir = '向后出发' DirEn = 'back' } else { Dir = '向左出发' DirEn = 'left' } } } let pLen = Map_QM.forShopArr[Map_QM.forShopArr.length - 1].PathPoint.length Config.overObj.xaxis = Map_QM.forShopArr[Map_QM.forShopArr.length - 1].PathPoint[pLen - 1].x Config.overObj.yaxis = Map_QM.forShopArr[Map_QM.forShopArr.length - 1].PathPoint[pLen - 1].y if (Map_QM.forShopArr.length > 0) { // "floor" PathPoint Direction (Facilities) Map_QM.forShopArr[0].Direction = Dir Map_QM.forShopArr[0].DirectionEn = DirEn for (let m = 0; m < Map_QM.forShopArr.length; m++) { //查找经过店铺 Map_QM.forShopArr[m].wayShop = Map_QM.foreignShop(Map_QM.forShopArr[m].PathPoint, Map_QM.forShopArr[m].floor, m) if (Config.overObj.floor == Map_QM.forShopArr[m].floor) { let len = Map_QM.forShopArr[m].wayShop.length if (Map_QM.forShopArr[m].wayShop[len - 1]?.shop.yaxis == Config.overObj.node) { Map_QM.forShopArr[m].wayShop.pop() } } } Map_QM.dispatchEvent({ type: 'initPathOver', data: Map_QM.forShopArr }) } else { console.error('无可行路径,请检查起、终点位') return } let mint = parseInt(path.cost / Config.mapScale / 55) < 0.5 ? 0.5 : parseInt(path.cost / Config.mapScale / 55) //console.log(`距离目的地 ${parseInt(path.cost / Config.mapScale)} 米,预计${mint} 分钟`); // 21 是比例尺 this.onFindPathToObj() }, /** * 途径店铺 */ foreignShop: function (pathArr, cFloor, m) { let shopList = [] let shops = Map_QM.mapArr[Config.selectBuild][parseInt(cFloor)].allObj.children for (let n = 0; n < pathArr.length; n++) { for (let i = 0; i < shops.length; i++) { if (shops[i].userData && shops[i].userData.type == 'shop' && shops[i].userData.navRecommend) { if (shops[i].userData.node == pathArr[n].id && shops[i].userData.shopData) { let data = { pathArrIn: m, pathIndex: n, shop: shops[i].userData.shopData } shopList.push(data) break } } } } return shopList }, getFacilIcon: function (fromFArr) { let childs = this.mapArr[Config.selectBuild][fromFArr[1]].serObj.children let selectEle for (let i = 0; i < childs.length; i++) { if (childs[i].navCode == fromFArr[2]) { selectEle = { imgUrl: childs[i].imgUrl, userData: childs[i].userData, position: { x: childs[i].position.x, y: childs[i].position.y } } break } } return selectEle }, /** * 寻路动画方法 */ onFindPathToObj: function () { Map_QM.dispatchEvent({ //初始化寻路界面 type: 'initPathPage', data: this.forShopArr }) outTime = -1 _indexPathFloor = 0 Map_QM.callBackLoadOver = Map_QM.callBackForPathShop //楼层初始化完成后回调 if (Config.overlap) { if (Map_QM.forShopArr.length == 2) { Map_QM.changeFloorInner(Map_QM.forShopArr[0].floor, Map_QM.forShopArr[1].floor) } else if (Map_QM.forShopArr.length == 3) { Map_QM.changeFloorInner(Map_QM.forShopArr[0].floor, Map_QM.forShopArr[1].floor, Map_QM.forShopArr[2].floor) } else { Map_QM.changeFloorInner(Map_QM.forShopArr[_indexPathFloor].floor) } } else { if (pathCameraState == '2D') { Map_QM.onShowMeDir(2) Map_QM.controls.enabled = false Map_QM.guide = Map_QM.man_2d } Map_QM.changeFloorInner(Map_QM.forShopArr[_indexPathFloor].floor) } // let shopArr = Map_QM.mapArr[Config.selectBuild][parseInt(Config.overObj.floor)].allObj.children; // if(Config.overObj.shopNum){ //匹配编号 // for (let k = 0; k < shopArr.length; k++) { // if (shopArr[k].isMesh && shopArr[k].name == Config.overObj.shopNum) { // Map_QM.overShop = shopArr[k]; // Map_QM.overShop.userData.material = shopArr[k].material; // Map_QM.overShop.material = new THREE.MeshBasicMaterial({ color: 0xA0522D}); // 0xAD8164 // break; // } // } // }else{//匹配导航点 // for (let k = 0; k < shopArr.length; k++) { // if (shopArr[k].isMesh && parseInt(shopArr[k].node) === parseInt(Config.overObj.node)) { // Map_QM.overShop = shopArr[k]; // Map_QM.overShop.userData.material = shopArr[k].material; // Map_QM.overShop.material = new THREE.MeshBasicMaterial({ color: 0xA0522D}); // break; // } // } // } }, /** * 播放楼层动画完成后 */ callBackForPathShop: function () { Map_QM.callBackLoadOver = null Map_QM.dispatchEvent({ //开始播放寻路动画 type: 'startPlayPath', data: this.forShopArr }) Map_QM.onFindPath() }, /** * 寻路方法 * @param {Object} startN * @param {Object} endN */ onFindPath: function () { TweenMax.killAll(true) if (Config.overlap) { Map_QM.mapArr[Config.selectBuild][Config.overObj.floor].setOverSite( Config.overObj.xaxis, Config.overObj.yaxis, Map_QM.mapArr[Config.selectBuild][Config.overObj.floor].allObj.position.y + parseInt(Config.shopHeight) ) } else { if (Config.overObj.floor == Config.selectFloor && Config.overObj.xaxis) { Map_QM.mapArr[Config.selectBuild][Config.overObj.floor].setOverSite( Config.overObj.xaxis, Config.overObj.yaxis, Map_QM.mapArr[Config.selectBuild][Config.overObj.floor].allObj.position.y + parseInt(Config.shopHeight) ) } } if (Map_QM.forShopArr.length > 0) { document.addEventListener('pathOver', Map_QM.onFindPathFloor, true) if (Config.overlap && _indexPathFloor == 0) { //叠层 for (let pathObj of Map_QM.forShopArr) { Map_QM.mapArr[Config.selectBuild][pathObj.floor].findPath.onFindPathAnimation(pathObj.PathPoint, pathObj.floor) //传入数组 } let extrudeSettings let vects = new THREE.CurvePath() let vects2 = new THREE.CurvePath() if (Map_QM.forShopArr.length == 2) { vects.add( new THREE.LineCurve3( new THREE.Vector3( Map_QM.forShopArr[0].PathPoint[Map_QM.forShopArr[0].PathPoint.length - 1].x, Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[0].floor].allObj.position.y, Map_QM.forShopArr[0].PathPoint[Map_QM.forShopArr[0].PathPoint.length - 1].y ), new THREE.Vector3( Map_QM.forShopArr[1].PathPoint[0].x, Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[1].floor].allObj.position.y, Map_QM.forShopArr[1].PathPoint[0].y ) ) ) extrudeSettings = { bevelEnabled: true, steps: 200, bevelSegments: 8, extrudePath: vects } let geometry = new THREE.ExtrudeGeometry(Map_QM.shape, extrudeSettings) let lineDashed = new THREE.Mesh(geometry, Config.tubeMaterial) Map_QM.dtLineGroup.add(lineDashed) } if (Map_QM.forShopArr.length == 3) { vects.add( new THREE.LineCurve3( new THREE.Vector3( Map_QM.forShopArr[0].PathPoint[Map_QM.forShopArr[0].PathPoint.length - 1].x, Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[0].floor].allObj.position.y, Map_QM.forShopArr[0].PathPoint[Map_QM.forShopArr[0].PathPoint.length - 1].y ), new THREE.Vector3( Map_QM.forShopArr[1].PathPoint[0].x, Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[1].floor].allObj.position.y, Map_QM.forShopArr[1].PathPoint[0].y ) ) ) vects2.add( new THREE.LineCurve3( new THREE.Vector3( Map_QM.forShopArr[1].PathPoint[Map_QM.forShopArr[1].PathPoint.length - 1].x, Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[1].floor].allObj.position.y, Map_QM.forShopArr[1].PathPoint[Map_QM.forShopArr[1].PathPoint.length - 1].y ), new THREE.Vector3( Map_QM.forShopArr[2].PathPoint[0].x, Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[2].floor].allObj.position.y, Map_QM.forShopArr[2].PathPoint[0].y ) ) ) extrudeSettings = { bevelEnabled: true, steps: 200, bevelSegments: 8, extrudePath: vects } extrudeSettings2 = { bevelEnabled: true, steps: 200, bevelSegments: 8, extrudePath: vects2 } let geometry = new THREE.ExtrudeGeometry(Map_QM.shape, extrudeSettings) let lineDashed = new THREE.Mesh(geometry, Config.tubeMaterial) Map_QM.dtLineGroup.add(lineDashed) let geometry2 = new THREE.ExtrudeGeometry(Map_QM.shape, extrudeSettings2) let lineDashed2 = new THREE.Mesh(geometry2, Config.tubeMaterial) Map_QM.dtLineGroup.add(lineDashed2) } } else { Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[_indexPathFloor].floor].findPath.clearPath() Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[_indexPathFloor].floor].findPath.onFindPathAnimation(Map_QM.forShopArr[_indexPathFloor].PathPoint) //传入数组 } Map_QM.mapArr[Config.selectBuild][Map_QM.forShopArr[_indexPathFloor].floor].findPath.guidePathPlay(Map_QM.forShopArr[_indexPathFloor]) Map_QM.collisionChock() } }, /** * 显示楼层 */ showNavFloor: function (fromFloor, toFloor) { let floorArr = [] let min = Math.min(fromFloor, toFloor) let max = Math.max(fromFloor, toFloor) for (let i = min; i <= max; i++) { floorArr.push(Map_QM.mapArr[Config.selectBuild][i].floorName) } return floorArr }, //导航完成事件 onFindPathFloor: function (event) { document.removeEventListener('pathOver', Map_QM.onFindPathFloor) if (Map_QM.forShopArr.length <= _indexPathFloor) { return } Map_QM.dispatchEvent({ //当前层寻路完成 type: 'floorPlayOver', data: Map_QM.forShopArr[_indexPathFloor].floor }) if (_indexPathFloor < Map_QM.forShopArr.length - 1) { let pathFloor = Map_QM.forShopArr[_indexPathFloor].floor let x0 = Map_QM.forShopArr[_indexPathFloor].Facilities.position.x + 64 let y0 = Map_QM.forShopArr[_indexPathFloor].Facilities.position.y let model = Map_QM.forShopArr[_indexPathFloor].Facilities.userData.model let box if (model && model.userData.name == 'dt') { model.traverse(function (child) { if (child.isMesh && child.name == 'zhitibox') { box = child } }) } let zo = 2, tz = 2 let add = Config.is4k ? 80 : 40 _indexPathFloor++ let toFloor = parseInt(Map_QM.forShopArr[_indexPathFloor].floor) Config.selectFloor = toFloor if (pathFloor < toFloor) { tz = (toFloor - pathFloor) * add } else { zo = (pathFloor - toFloor) * add } document.getElementById('moveFloor').style.bottom = zo + 'px' document.getElementById('moveFloor').style.width = add + 'px' document.getElementById('moveFloor').style.height = add + 'px' let floorArr = Map_QM.showNavFloor(pathFloor, toFloor) //电梯旁显示的楼层名称 Map_QM.moveFloorbg.element.style.visibility = 'visible' Map_QM.moveFloorbg.element.style.width = add + 8 + 'px' Map_QM.moveFloorbg.element.style.height = floorArr.length * add + 'px' Map_QM.moveFloorbg.position.set(x0, y0, 80) Map_QM.moveFloorbg.applyMatrix(Map_QM.mapArr[Config.selectBuild][pathFloor].allObj.matrix) Map_QM.moveFloorbg.applyMatrix(Map_QM.sceneGap.matrix) let floorBox = document.getElementById('floorBox') while (floorBox.hasChildNodes()) { floorBox.removeChild(floorBox.firstChild) } for (let i = floorArr.length - 1; i >= 0; i--) { let span2 = document.createElement('span') span2.style.cssText = 'display: block;color: #000000; text-align: center;' span2.style.fontSize = '16px' if (Config.is4k) { span2.style.fontSize = '32px' } span2.style.width = add + 'px' span2.style.height = add + 'px' span2.style.lineHeight = add + 'px' span2.innerText = floorArr[i] floorBox.appendChild(span2) } TweenMax.fromTo( '#moveFloor', 1.5, { bottom: zo }, { bottom: tz, delay: 0.1, onComplete: function () { if (!Config.overlap) { Map_QM.callBackLoadOver = Map_QM.callBackForPathShop //楼层初始化完成后回调 Map_QM.changeFloorInner(Map_QM.forShopArr[_indexPathFloor].floor) } else { if (Map_QM.forShopArr > _indexPathFloor) { Config.selectFloor = Map_QM.forShopArr[_indexPathFloor].floor } Map_QM.clearFloor(-1, false) Map_QM.callBackForPathShop() } } } ) if (box) { TweenMax.fromTo( box.position, 1.2, { y: zo / 3 }, { y: tz / 3, delay: 0.2, onComplete: function () { box.position.y = 0 } } ) } if (pathCameraState == '3D') { let xt = Map_QM.guide.position.x + event.detail.dx * 10 let yt = Map_QM.guide.position.y + event.detail.dy * 10 let s = Math.sqrt((xt - Map_QM.guide.position.x) * (xt - Map_QM.guide.position.x) + (yt - Map_QM.guide.position.y) * (yt - Map_QM.guide.position.y)) let ang = Math.acos((yt - Map_QM.guide.position.y) / s) if (xt < Map_QM.guide.position.x) { Map_QM.guide.rotation.z = -1 * ang //Math.PI-ang; } else { Map_QM.guide.rotation.z = ang //Math.PI+ang; } if (model && model.userData.name == 'upft') { Map_QM.guide.visible = true TweenMax.to(Map_QM.guide.position, 1.3, { x: xt, y: yt, z: 50, onComplete: function () { Map_QM.guide.visible = false } }) } if (model && model.userData.name == 'downft') { Map_QM.guide.visible = true TweenMax.to(Map_QM.guide.position, 1.3, { x: xt, y: yt, z: -50, onComplete: function () { Map_QM.guide.visible = false } }) } } } else { Config.startObj = Config.deviceObj //console.log("到达店铺"); Map_QM.dispatchEvent({ //寻路完成 type: 'PathPlayOver', data: 'PathPlayOver' }) } }, /** * 楼层状态清理 */ clearFloor: function (fIndex = -1, reSet = true) { if (fIndex == -1) { fIndex = Config.deviceObj.floor } if (Map_QM.selectShop) { Map_QM.selectShop.scale.z = 1 Map_QM.selectShop.material = Config.getMeshMaterial(Map_QM.selectShop.userData.shopData.formatColor || 0xeab16e) } if (Map_QM.endModel && Map_QM.endModel.visible) { Map_QM.endModel.visible = false } if (Map_QM.endIcon && Map_QM.endIcon.visible) { Map_QM.endIcon.visible = false } if (Map_QM.moveFloorbg) { Map_QM.moveFloorbg.element.style.visibility = 'hidden' } Map_QM.clearDeviceSelectState() if (reSet) { //叠层不处理 if (Map_QM.mapArr[Config.selectBuild] && Map_QM.mapArr[Config.selectBuild].length > 0) { for (let i = 0; i < Map_QM.mapArr[Config.selectBuild].length; i++) { let lines = Map_QM.mapArr[Config.selectBuild][i].lineObj for (let j = lines.children.length - 1; j >= 0; j--) { Map_QM.mapArr[Config.selectBuild][i].lineObj.remove(lines.children[j]) } let child = Map_QM.mapArr[Config.selectBuild][i].allObj for (let k = child.children.length - 1; k >= 0; k--) { if (child.children[k].name == 'lineDash') { Map_QM.mapArr[Config.selectBuild][i].allObj.remove(child.children[k]) } } Map_QM.mapArr[Config.selectBuild][i].findPath.clearPath() } } Map_QM.remove_child(Map_QM.dtLineGroup) } TweenMax.killAll(true) if (Map_QM.overShop) { Map_QM.overShop.scale.z = 1 Map_QM.overShop.material = Map_QM.overShop.userData.material } }, remove_child: function (remObj) { if (!remObj) { return } let child_elem = remObj.children for (let i = child_elem.length - 1; i >= 0; i--) { if (child_elem[i].children.length > 0) { Map_QM.remove_child(child_elem[i]) } else { if (child_elem[i] instanceof THREE.Mesh) { child_elem[i].geometry.dispose() // 删除几何体 if (child_elem[i].material !== undefined) Map_QM.removeMaterial(child_elem.material) // 删除材质 } } if (child_elem[i].name != 'light' && child_elem[i].name != 'dtline') { remObj.remove(child_elem[i]) } } }, removeMaterial: function (material) { if (Array.isArray(material)) { for (var i = 0, l = material.length; i < l; i++) { this.removeMaterialFromRefCounter(material[i]) } } else { this.removeMaterialFromRefCounter(material) } }, removeMaterialFromRefCounter: function (material) { var materialsRefCounter = this.materialsRefCounter if (materialsRefCounter) { var count = materialsRefCounter.get(material) count-- if (count === 0) { materialsRefCounter.delete(material) delete this.materials[material.uuid] } else { materialsRefCounter.set(material, count) } } }, /** * @api {方法} pathStop() 导航暂停/播放 * @apiGroup 地图导航 * @apiDescription 导航动画暂停/播放 * @apiVersion 1.0.0 * * @apiSampleRequest off * */ pathStop: function (isPlaying) { isPathPlay = isPlaying === undefined ? !isPathPlay : isPlaying Map_QM.mapArr[Config.selectBuild][Config.selectFloor].findPath.pathPlay.isPlay = isPathPlay }, /** * @api {方法} pathRePlay() 导航动画重播 * @apiGroup 地图导航 * @apiDescription 导航动画重播 * @apiVersion 1.0.0 * * @apiSampleRequest off * */ pathRePlay: function () { if (Config.overObj && Map_QM.forShopArr[0]) { clearTimeout(outTime) outTime = setTimeout(() => { clearTimeout(outTime) isPathPlay = true Map_QM.clearFloor() Map_QM.onFindPathToObj() }, 300) } }, /** * @api {方法} ChangePathByFt() 切换扶梯模式 * @apiGroup 地图导航 * @apiDescription 导航切换扶梯模式 * @apiVersion 1.0.0 * * @apiParam {function} callBack 回调函数 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.ChangePathByFt(function); * */ ChangePathByFt: function (callBack) { if (Config.overObj) { clearTimeout(outTime) outTime = setTimeout(() => { clearTimeout(outTime) Map_QM.clearFloor() Map_QM.onFindPathModel(ftPath) if (callBack) { Map_QM.parseForShopArr() const data = JSON.parse(JSON.stringify(forShopArr)) callBack(data) } }, 300) } }, /** * @api {方法} ChangePathByDt() 切换电梯模式 * @apiGroup 地图导航 * @apiDescription 导航切换电梯模式 * @apiVersion 1.0.0 * * @apiParam {function} callBack 回调函数 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.ChangePathByDt(function); * */ ChangePathByDt: function (callBack) { if (Config.overObj) { clearTimeout(outTime) outTime = setTimeout(() => { clearTimeout(outTime) Map_QM.clearFloor() Map_QM.onFindPathModel(dtPath) if (callBack) { Map_QM.parseForShopArr() const data = JSON.parse(JSON.stringify(forShopArr)) callBack(data) } }, 300) } }, /** * @api {方法} ChangePathByGood() 切换最佳模式 * @apiGroup 地图导航 * @apiDescription 导航切换最佳模式 * @apiVersion 1.0.0 * * @apiParam {function} callBack 回调函数 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.ChangePathByGood(function); * */ ChangePathByGood: function (callBack) { if (Config.overObj) { clearTimeout(outTime) outTime = setTimeout(() => { clearTimeout(outTime) Map_QM.clearFloor() Map_QM.onFindPathModel(graphPath) if (callBack) { Map_QM.parseForShopArr() const data = JSON.parse(JSON.stringify(forShopArr)) callBack(data) } }, 300) } }, /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// changeDocmentResize: function (e) { let ele = document.getElementById(options.containerId || 'mapContainer') let w = parseInt(ele.clientWidth) || parseInt(window.getComputedStyle(ele, null).getPropertyValue('width')) let h = parseInt(ele.clientHeight) || parseInt(window.getComputedStyle(ele, null).getPropertyValue('height')) console.log(w, h) Map_QM.changeWindowResize(w, h) }, /** * @api {方法} changeWindowResize() 窗口变化 * @apiGroup 地图交互 * @apiDescription 窗口变化 * @apiVersion 1.0.0 * * @apiParam {int} width 窗口宽 * @apiParam {int} height 窗口高 * * @apiSampleRequest off * * @apiParamExample 请求示例 * * Map_QM.changeWindowResize(1280,1080); */ changeWindowResize: function (width = -1, height = -1) { Map_QM.aspect = width / height Map_QM.cameraPerspective.aspect = Map_QM.aspect Map_QM.cameraPerspective.updateProjectionMatrix() Map_QM.cameraOrtho.left = (340 * Map_QM.aspect) / -2 Map_QM.cameraOrtho.right = (340 * Map_QM.aspect) / 2 Map_QM.cameraOrtho.top = 340 / 2 Map_QM.cameraOrtho.bottom = 340 / -2 Map_QM.cameraOrtho.updateProjectionMatrix() Map_QM.renderer.setSize(width, height) Map_QM.labelRenderer.setSize(width, height) Map_QM.w = width Map_QM.h = height } } Object.assign(MainMap_QM.prototype, THREE.EventDispatcher.prototype) //////////////////////////////////////-------------------------------FloorMap FloorMap_QM = function (bIndex, fIndex, floorName) { this.Model_QM = new MyModel_QM() this.facUtil = new Facilities_QM() this.findPath = new FindPath_QM() this.logoUtil = new ShopLogo_QM() this.allObj = new THREE.Group() this.allObj.name = floorName this.allObj.rotation.x = Math.PI / -2 this.labelObj = new THREE.Group() this.labelObj.renderOrder = 100 this.iconLabel = new THREE.Group() this.iconLabel.renderOrder = 100 this.floorOrder = fIndex this.buildOrder = bIndex this.floorName = floorName //存放设施图标 this.serObj = new THREE.Object3D() //存放车位box this.parkObj = new THREE.Group() //标签 this.tagObj = new THREE.Group() this.tagObj.renderOrder = 100 this.devObj = new THREE.Group() //存放设施图标 this.logoObj = new THREE.Object3D() this.allObj.add(this.logoObj) this.svgObj = new THREE.Group() this.allObj.add(this.svgObj) this.allObj.add(this.serObj) this.allObj.add(this.devObj) this.allObj.add(this.labelObj) this.allObj.add(this.iconLabel) this.allObj.add(this.tagObj) //标签 this.showTagObj = new THREE.Group() this.showTagObj.renderOrder = 100 this.allObj.add(this.showTagObj) //线条 this.lineObj = new THREE.Group() this.lineObj.renderOrder = 100 this.allObj.add(this.lineObj) this.startIcon } FloorMap_QM.prototype = { initDraw: function () { this.initFloor() this.initFacilitie() this.initDevice() //初始化设备 this.initStairs() this.initTextArea() this.initWall() this.initDecos() this.initPark() this.initModel() this.initTree() //植树 }, initModel: function () { let sopce = this if (Config.modelArr) { for (let i = 0; i < Config.modelArr.length; i++) { if (Config.modelArr[i].floor == this.floorOrder) { new THREE.GLTFLoader().load(Config.modelArr[i].url, function (object) { //加载路径fbx文件 let mod = object.scene mod.traverse(function (child) { if (child.isMesh) { child.receiveShadow = true child.castShadow = true if (child.material.map) { child.material.map.encoding = THREE.LinearEncoding //贴图需要转换成 线性编码 } child.material.color.convertGammaToLinear(0.7) } }) for (let t = 0; t < Config.modelArr[i].list.length; t++) { let obj = mod.clone() obj.position.set(Config.modelArr[i].list[t].site.x, -1 * Config.modelArr[i].list[t].site.y, Config.modelArr[i].list[t].site.z) obj.scale.set(Config.modelArr[i].list[t].size.x, Config.modelArr[i].list[t].size.y, Config.modelArr[i].list[t].size.z) obj.rotateX(Config.modelArr[i].list[t].rot.x) obj.rotateY(Config.modelArr[i].list[t].rot.y) obj.rotateZ(Config.modelArr[i].list[t].rot.z) sopce.allObj.add(obj) } }) } } } if (Config.labelIconArr) { for (let i = 0; i < Config.labelIconArr.length; i++) { if (Config.labelIconArr[i].floor == this.floorOrder) { let SpriteDiv = document.createElement('div') SpriteDiv.className = Config.labelIconArr[i].className SpriteDiv.innerHTML = Config.labelIconArr[i].title SpriteDiv.dataset.id = Config.labelIconArr[i].data.id SpriteDiv.dataset.x = Config.labelIconArr[i].site.x SpriteDiv.dataset.y = Config.labelIconArr[i].site.y SpriteDiv.dataset.z = Config.labelIconArr[i].site.z let pointLabel = new THREE.CSS2DObject(SpriteDiv) pointLabel.position.set(Config.labelIconArr[i].site.x, -1 * Config.labelIconArr[i].site.y, Config.labelIconArr[i].site.z) pointLabel.name = Config.labelIconArr[i].title pointLabel.userData = Config.labelIconArr[i].data pointLabel.userData.site = Config.labelIconArr[i].site if (Config.labelIconArr[i].click) { //可点击 SpriteDiv.addEventListener( 'click', event => { Map_QM.dispatchEvent({ type: 'labelIcon', data: event.target.dataset }) }, false ) } pointLabel.userData.floor = Config.labelIconArr[i].floor sopce.showTagObj.add(pointLabel) pointLabel.userData.show = Config.labelIconArr[i].data.show if (Config.labelIconArr[i].data.show != language) { pointLabel.element.style.opacity = 0 pointLabel.element.style.pointerEvents = 'none' } } } } }, initTree: function () { let mapData = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData if (mapData.models) { for (let t = 0; t < mapData.models.length; t++) { for (let i = 0; i < Config.fbxModels.length; i++) { if (mapData.models[t].type == Config.fbxModels[i].key) { let obj = Config.fbxModels[i].obj.clone() obj.position.set(mapData.models[t].x, -1 * mapData.models[t].y, mapData.models[t].site) obj.scale.set(mapData.models[t].scale, mapData.models[t].scale, mapData.models[t].scale) obj.rotateX(Config.fbxModels[i].operation.rot.x) obj.rotateY((-1 * mapData.models[t].angle * Math.PI) / 180) this.allObj.add(obj) } } } } }, //初始化单楼层 initFloor: function () { let floor let mapData = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData let entColor, borderColor if (mapData.floorArea) { floor = Config.changeAreaToString(mapData.floorArea) } let bLen = mapData.buildArea.length let hLen = mapData.hollowArea.length let hows_f = [] for (let m = 0; m < hLen; m++) { hows_f.push(Config.changeAreaToString(mapData.hollowArea[m])) } if (floor) { entColor = parseInt(mapData.floorArea.entColor.replace('#', '0x'), 16) borderColor = parseInt(mapData.floorArea.borderColor.replace('#', '0x'), 16) floorH = parseInt(mapData.floorArea.toHeight) let mash = this.Model_QM.MyModelShape(floor, hows_f, mapData.floorArea, entColor, borderColor, 10) mash.receiveShadow = true mash.castShadow = false mash.userData = { type: 'floor', order: this.floorOrder } mash.name = 'floor' this.allObj.add(mash) } /////////////////////初始化楼栋 for (let i = 0; i < bLen; i++) { let build = Config.changeAreaToString(mapData.buildArea[i]) let hows = [] for (let t = 0; t < hLen; t++) { let isIn = Config.checkAreaInArea(mapData.hollowArea[t], mapData.buildArea[i]) if (isIn) { hows.push(Config.changeAreaToString(mapData.hollowArea[t])) } } entColor = parseInt(mapData.buildArea[i].entColor.replace('#', '0x'), 16) borderColor = parseInt(mapData.buildArea[i].borderColor.replace('#', '0x'), 16) Config.buildHeight = Math.max(parseInt(mapData.buildArea[i].toHeight), Config.buildHeight) buildH = parseInt(mapData.buildArea[i].toHeight) let mash = this.Model_QM.MyModelShape(build, hows, mapData.buildArea[i], entColor, borderColor, 30) mash.receiveShadow = true mash.castShadow = false mash.userData = { type: 'build', order: this.floorOrder } mash.name = 'floor' this.allObj.add(mash) } //店铺 let sLen = mapData.shopArea.length for (let i = 0; i < sLen; i++) { if (Config.changeAreaToString(mapData.shopArea[i]) != '') { let arr = Config.changeShopLinesToString(mapData.shopArea[i]) entColor = mapData.shopArea[i].entColor || '#b79266' borderColor = mapData.shopArea[i].borderColor || '#b79266' let show = (showE = '') let logo = '', navRecommend = false, shopD = {} Config.shopHeight = parseInt(mapData.shopArea[i].toHeight) let shopData = Config.shopData if (shopData) { for (let h = 0; h < shopData.length; h++) { if (shopData[h].buildingOrder == this.buildOrder && shopData[h].floorOrder == this.floorOrder) { if (shopData[h].houseNumber == mapData.shopArea[i].name) { show = shopData[h].shopName showE = shopData[h].shopNameEn logo = shopData[h].logoUrl if (shopData[h].isPass == undefined) { navRecommend = true } else { navRecommend = shopData[h].isPass } shopD = shopData[h] if (shopData[h].isNewShop) { //新店 mapData.shopArea[i].type = 'new-shop' this.addTagLabel(mapData.shopArea[i], { node: mapData.shopArea[i].shopNav, floor: this.floorOrder, build: this.buildOrder }) } if (shopData[h].activityList && shopData[h].activityList.length > 0) { //促销 mapData.shopArea[i].type = 'promotion' this.addTagLabel(mapData.shopArea[i], { node: mapData.shopArea[i].shopNav, floor: this.floorOrder, build: this.buildOrder }) } if (shopData[h].formatColor) { entColor = parseInt(shopData[h].formatColor.replace('#', '0x'), 16) } else { entColor = '#eab16e' } if (shopData[h].borderColor) { borderColor = parseInt(shopData[h].borderColor.replace('#', '0x'), 16) } else { borderColor = '#eab16e' } break } } } } if (mapData.shopArea[i].logoUrl && mapData.shopArea[i].isLabel == 0) { //添加logo this.logoUtil.renderIcon(mapData.shopArea[i], this, parseInt(mapData.shopArea[i].toHeight) + 1) } else { //添加文字 if (show != '') { let shopDiv = document.createElement('div') shopDiv.className = 'shopLabel' shopDiv.innerText = show shopDiv.dataset.name = show shopDiv.dataset.nameEn = showE let shopLabel = new THREE.CSS2DObject(shopDiv) shopLabel.position.set(mapData.shopArea[i].xaxis >> 0, (-1 * mapData.shopArea[i].yaxis) >> 0, parseInt(mapData.shopArea[i].toHeight)) this.labelObj.add(shopLabel) shopLabel.element.style.pointerEvents = 'none' } } let mahc = this.Model_QM.MyModelShape(arr, null, mapData.shopArea[i], entColor, borderColor, 100) mahc.node = mapData.shopArea[i].shopNav mahc.userData = { shopData: shopD, xaxis: mapData.shopArea[i].xaxis >> 0, yaxis: mapData.shopArea[i].yaxis >> 0, node: mahc.node, floor: this.floorOrder, navRecommend: navRecommend, type: 'shop', entColor: entColor, shopNum: mapData.shopArea[i].name, shopName: show, logo: logo, borderColor: borderColor } show = show == 'shop' ? '' : show show = show == 'floor' ? '' : show show = show == 'build' ? '' : show mahc.shopName = show mahc.xaxis = mapData.shopArea[i].xaxis >> 0 mahc.yaxis = mapData.shopArea[i].yaxis >> 0 this.allObj.add(mahc) } } }, addTagLabel: function (obj, userData) { let shopDiv = document.createElement('img') shopDiv.src = './static/img/' + obj.type + '.png' shopDiv.style.zIndex = 200 let shopLabel = new THREE.CSS2DObject(shopDiv) shopLabel.userData = userData shopLabel.position.set(obj.xaxis >> 0, (-1 * obj.yaxis) >> 0, Config.shopHeight) this.tagObj.add(shopLabel) }, //初始化服务图标 initFacilitie: function () { let serArr = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData.icons for (let i = 0; i < serArr.length; i++) { serArr[i].floorOrder = this.floorOrder this.facUtil.renderIcon(serArr[i], this) } }, //初始化设备图标 initDevice: function () { let mapData = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData if (deviceShow && deviceJSON && deviceJSON[this.buildOrder]) { for (item of deviceJSON[this.buildOrder].floors) { if (item.floorOrder == this.floorOrder) { let devices = item.devices for (let i = 0; i < devices.length; i++) { if (devices[i].yaxis != '' && mapData.path.nodes[parseInt(devices[i].yaxis)]) { devices[i].x = mapData.path.nodes[parseInt(devices[i].yaxis)].x devices[i].y = mapData.path.nodes[parseInt(devices[i].yaxis)].y devices[i].node = parseInt(devices[i].yaxis) this.facUtil.renderDeviceIcon(devices[i], this) } } break } } } }, //初始化电梯图标 initStairs: function () { let facArr = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData.stairs for (let i = 0; i < facArr.length; i++) { if (facArr[i].facCode == 'upft') { if (elevator) { let ex = elevator.clone() ex.position.set(facArr[i].x, -1 * facArr[i].y, facArr[i].site) ex.rotation.y = (facArr[i].angle * Math.PI) / -180 ex.userData.type = 'icon' ex.userData.use = '3d' ex.userData.name = 'upft' this.serObj.add(ex) facArr[i].facCode = 'ft' this.facUtil.renderIcon(facArr[i], this, false, ex) } else { facArr[i].facCode = 'ft' this.facUtil.renderIcon(facArr[i], this, true) } } else if (facArr[i].facCode == 'downft') { if (elevatorDown) { let ex = elevatorDown.clone() ex.position.set(facArr[i].x, -1 * facArr[i].y, facArr[i].site) ex.rotation.y = (facArr[i].angle * Math.PI) / -180 ex.userData.type = 'icon' ex.userData.use = '3d' ex.userData.name = 'downft' this.serObj.add(ex) facArr[i].facCode = 'ft' this.facUtil.renderIcon(facArr[i], this, false, ex) } else { facArr[i].facCode = 'ft' this.facUtil.renderIcon(facArr[i], this, true) } } else if (facArr[i].facCode == 'dt') { if (straight) { let stra = straight.clone() stra.position.set(facArr[i].x, -1 * facArr[i].y, facArr[i].site) stra.rotation.y = (facArr[i].angle * Math.PI) / -180 stra.userData.type = 'icon' stra.userData.use = '3d' stra.userData.name = 'dt' this.serObj.add(stra) this.facUtil.renderIcon(facArr[i], this, false, stra) } else { this.facUtil.renderIcon(facArr[i], this, true) } } else { this.facUtil.renderIcon(facArr[i], this, true) } } }, //初始化装饰图标 initDecos: function () { let mapData = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData let sLen = mapData.decos.length let entColor, borderColor if (mapData.groupArea) { for (let k = 0; k < mapData.groupArea.length; k++) { var gp = new THREE.Group() gp.rotateX((mapData.groupArea[k].angleX * Math.PI) / 180) gp.rotateY((mapData.groupArea[k].angleY * Math.PI) / 180) gp.rotateZ((mapData.groupArea[k].angleZ * Math.PI) / 180) for (let i = 0; i < sLen; i++) { if (mapData.decos[i].gid && mapData.decos[i].gid == mapData.groupArea[k]._name && Config.changeAreaToString(mapData.decos[i]) != '') { let arr = Config.changeAreaToString(mapData.decos[i]) entColor = parseInt(mapData.decos[i].entColor.replace('#', '0x'), 16) borderColor = parseInt(mapData.decos[i].borderColor.replace('#', '0x'), 16) let show = mapData.decos[i].name == 'deco' ? '' : mapData.decos[i].name let mahc = this.Model_QM.MyModelShape(arr, null, mapData.decos[i], entColor, borderColor, 240) mahc.xaxis = mapData.decos[i].xaxis >> 0 mahc.yaxis = mapData.decos[i].yaxis >> 0 mahc.node = mapData.decos[i].shopNav mahc.userData = { type: 'deco', name: show } gp.add(mahc) mahc.position.x = -1 * mapData.groupArea[k].xaxis mahc.position.y = mapData.groupArea[k].yaxis } } this.allObj.add(gp) gp.position.x = mapData.groupArea[k].xaxis gp.position.y = -1 * mapData.groupArea[k].yaxis gp.position.z = mapData.groupArea[k].site gp.scale.set(mapData.groupArea[k].scale, mapData.groupArea[k].scale, mapData.groupArea[k].scale) } } for (let i = 0; i < sLen; i++) { if ((!mapData.decos[i].gid || mapData.decos[i].gid == '') && Config.changeAreaToString(mapData.decos[i]) != '') { let arr = Config.changeAreaToString(mapData.decos[i]) entColor = parseInt(mapData.decos[i].entColor.replace('#', '0x'), 16) borderColor = parseInt(mapData.decos[i].borderColor.replace('#', '0x'), 16) let show = mapData.decos[i].name == 'deco' ? '' : mapData.decos[i].name let mahc = this.Model_QM.MyModelShape(arr, null, mapData.decos[i], entColor, borderColor, 240) mahc.xaxis = mapData.decos[i].xaxis >> 0 mahc.yaxis = mapData.decos[i].yaxis >> 0 mahc.node = mapData.decos[i].shopNav mahc.userData = { type: 'deco', name: show } this.allObj.add(mahc) } } }, /** * 渲染墙体 */ initWall: function () { let mapData = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData //渲染 if (mapData.wallArea) { let sLen = mapData.wallArea.length for (let i = 0; i < sLen; i++) { let arr = Config.changeWallToString(mapData.wallArea[i]) let mahc = this.Model_QM.MyModelShape( arr, null, mapData.wallArea[i], mapData.wallArea[i].entColor || '#eaeaea', mapData.wallArea[i].borderColor || '#eaeaea', 300 ) mahc.userData = { type: 'wall' } this.allObj.add(mahc) } } }, /** * 渲染文本 */ initTextArea: function () { let mapData = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData //渲染 if (mapData.svgArea) { let sLen = mapData.svgArea.length for (let i = 0; i < sLen; i++) { let mahc = this.Model_QM.MyModelText(mapData.svgArea[i]) mahc.userData = { type: 'svg', rot: mahc.rotation.z, shopNum: mapData.svgArea[i].name } this.svgObj.add(mahc) } } }, /** * 设置终点图标 */ setOverSite: function (shopX, shopY, shopZ) { if (Map_QM.endModel) { Map_QM.endModel.visible = pathCameraState == '2D' ? false : true Map_QM.endModel.scale.x = Map_QM.endModel.scale.y = Map_QM.endModel.scale.z = 100 Map_QM.endModel.position.set(shopX, shopZ, shopY) //x,z,y Map_QM.endModel.applyMatrix(Map_QM.sceneGap.matrix) if (Map_QM.endIcon) { Map_QM.endIcon.visible = pathCameraState == '2D' ? true : false Map_QM.endIcon.scale.x = Map_QM.endIcon.scale.y = Map_QM.endIcon.scale.z = 100 Map_QM.endIcon.position.set(shopX, shopZ, shopY) //x,z,y Map_QM.endIcon.applyMatrix(Map_QM.sceneGap.matrix) } } else { let loader2 = new THREE.GLTFLoader() loader2.load('./static/img/zhong.gltf', function (collada2) { collada2.scene.traverse(function (child2) { if ( child2.name == 'object_1' || child2.name == 'object_2' || child2.name == 'object_3' || child2.name == 'object_4' || child2.name == 'object_6' || child2.name == 'object_11' || child2.name == 'object_21' || child2.name == 'object_31' || child2.name == 'object_41' ) { child2.material = new THREE.MeshBasicMaterial({ color: 0xff464e, transparent: true }) } if (child2.name == 'object_5' || child2.name == 'object_51') { child2.material = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true }) } }) collada2.scene.scale.x = collada2.scene.scale.y = collada2.scene.scale.z = 100 if (shopX != 0 && shopY != 0) { collada2.scene.position.set(shopX, shopZ, shopY) //x,z,y } collada2.scene.applyMatrix(Map_QM.sceneGap.matrix) collada2.scene.renderOrder = 200 Map_QM.endModel = collada2.scene Map_QM.scene.add(collada2.scene) Map_QM.endModel.visible = pathCameraState == '2D' ? false : true }) let spriteMap = new THREE.TextureLoader().load('./static/img/Z.png') let spriteMaterial = new THREE.SpriteMaterial({ //sizeAttenuation: false 禁止跟随鼠标缩放 map: spriteMap, depthTest: true, transparent: true, alphaTest: 0.5 }) Map_QM.endIcon = new MySprite_QM(spriteMaterial) Map_QM.endIcon.scale.set(80, 80, 1) Map_QM.endIcon.center = new THREE.Vector2(0.5, 0) Map_QM.endIcon.position.set(shopX, shopZ, shopY) Map_QM.endIcon.applyMatrix(Map_QM.sceneGap.matrix) Map_QM.endIcon.renderOrder = 300 Map_QM.endIcon.visible = pathCameraState == '2D' ? true : false Map_QM.scene.add(Map_QM.endIcon) } }, /** * 设置起点图标 */ setStartSite: function (shopX, shopY, shopZ) { let _this = this new THREE.GLTFLoader().load('./static/img/qi.gltf', function (collada) { //console.log(collada); collada.scene.traverse(function (child) { if (child.name == 'object_1' || child.name == 'object_3' || child.name == 'object_4' || child.name == 'object_31' || child.name == 'object_41') { child.material = new THREE.MeshBasicMaterial({ color: 0xffae43, transparent: true }) } if (child.name == 'object_2' || child.name == 'object_21') { child.material = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true }) } }) collada.scene.scale.x = collada.scene.scale.y = collada.scene.scale.z = 100 collada.scene.rotateX(Math.PI / 2) if (shopX != 0 && shopY != 0) { collada.scene.position.set(shopX, -1 * shopY, shopZ) //x,z,y } collada.scene.renderOrder = 200 Map_QM.qiModel = collada.scene if (Config.cameraDist.state == '2D') { Map_QM.qiModel.visible = false } _this.allObj.add(collada.scene) //-------------------------------------------------------------- let spriteMap = new THREE.TextureLoader().load('./static/img/Q.png') let spriteMaterial = new THREE.SpriteMaterial({ //sizeAttenuation: false 禁止跟随鼠标缩放 map: spriteMap, depthTest: true, transparent: true, alphaTest: 0.5 }) Map_QM.qiIcon = new MySprite_QM(spriteMaterial) Map_QM.qiIcon.scale.set(80, 80, 1) Map_QM.qiIcon.center = new THREE.Vector2(0.5, 0) Map_QM.qiIcon.position.set(shopX, -1 * shopY, shopZ) Map_QM.qiIcon.renderOrder = 300 Map_QM.qiIcon.visible = false _this.allObj.add(Map_QM.qiIcon) let spriteMap2 = new THREE.TextureLoader().load('./static/img/site.png') let spriteMaterial2 = new THREE.SpriteMaterial({ //sizeAttenuation: false 禁止跟随鼠标缩放 map: spriteMap2, depthTest: true, transparent: true, alphaTest: 0.5 }) Map_QM.dirIcon = new MySprite_QM(spriteMaterial2) Map_QM.dirIcon.scale.set(160, 160, 1) Map_QM.dirIcon.center = new THREE.Vector2(0.5, 0.5) Map_QM.dirIcon.position.set(shopX, -1 * shopY, shopZ) Map_QM.dirIcon.renderOrder = 300 if (Config.cameraDist.state == '3D') { Map_QM.dirIcon.visible = false } _this.allObj.add(Map_QM.dirIcon) }) }, /** * 查找线 * @param {Object} startNode * @param {Object} endNode */ isNoFindLine: function (startNode, endNode) { let no = false for (let i = 0; i < startNode.lineArr.length; i++) { if (startNode.lineArr[i].nextNode.id == endNode.id || startNode.lineArr[i].selfNode.id == endNode.id) { no = true break } } return no }, //初始化停车位 initPark: function () { let mapData = Config.allMap[this.buildOrder].buildArr[this.floorOrder].mapData //渲染车位 if (mapData.parkArea) { let sLen = mapData.parkArea.length if (sLen > 0) { this.allObj.add(this.parkObj) } for (let i = 0; i < sLen; i++) { if (Config.changeParkToString(mapData.parkArea[i]) != '') { let arr = Config.changeParkToString(mapData.parkArea[i]) mapData.parkArea[i].alphaModle = 95 let mahc = this.Model_QM.MyParkShape(arr, mapData.parkArea[i], mapData.parkArea[i].entColor, mapData.parkArea[i].borderColor, 80) this.parkObj.add(mahc) mahc.xaxis = mapData.parkArea[i].xaxis >> 0 mahc.yaxis = mapData.parkArea[i].yaxis >> 0 mahc.node = mapData.parkArea[i].shopNav mahc.userData = { xaxis: mahc.xaxis, yaxis: mahc.yaxis, node: mahc.node, floor: this.floorOrder, shopNum: mahc.name, type: 'park' } } } } } } /** * 渲染3D模型类 * 传入区域点list * 镂空点 howllowArr * 模型对象 options */ MyModel_QM = function () { this.xaxis = 0 this.yaxis = 0 this.node = 0 } //渲染平面车位 MyModel_QM.prototype.MyParkShape = function (areaArr, opObj, entityColor = 0xdadada, lineColor = 0xeeeeee, indexOrder = 1) { let len = areaArr.length if (len == 0) { return } // 实例化shape对象 let shape = new THREE.Shape() // 设置开始点的位置 shape.moveTo(areaArr[0][0], -1 * areaArr[0][1]) for (let i = 0; i < areaArr.length; i++) { if (areaArr[i].length == 4) { shape.lineTo(areaArr[i][2], -1 * areaArr[i][3]) } else { shape.bezierCurveTo(areaArr[i][2], -1 * areaArr[i][3], areaArr[i][4], -1 * areaArr[i][5], areaArr[i][6], -1 * areaArr[i][7]) } } let material for (let k = 0; k < Config.lineBasicMaterialArr.length; k++) { let color2 = new THREE.Color(lineColor) if (Config.lineBasicMaterialArr[k].color.equals(color2)) { material = Config.lineBasicMaterialArr[k] } } if (!material) { material = new THREE.LineBasicMaterial({ color: lineColor }) //材质对象lineColor Config.lineBasicMaterialArr.push(material) } let scanGeometry = new THREE.ShapeBufferGeometry(shape, 1) if (opObj.angleY || opObj.angleZ) { Config.rotateYZ(scanGeometry, (opObj.angleY * Math.PI) / 180, (opObj.angleZ * Math.PI) / 180) } // 创建模型 let mesh = new THREE.Mesh(scanGeometry, Config.parkMaterial) let cubeEdges = new THREE.EdgesGeometry(scanGeometry, 1) mesh.add(new THREE.Line(cubeEdges, material)) mesh.position.z = opObj.site || 3 mesh.renderOrder = indexOrder mesh.name = opObj.name || '' return mesh } MyModel_QM.prototype.MyModelShape = function (areaArr, howllowArr, opObj, entityColor = 0xdadada, lineColor = 0xeeeeee, indexOrder = 1) { let len = areaArr.length if (len == 0) { return } let alphaModle = opObj.alphaModle / 100 || 0 // 实例化shape对象 let shape = new THREE.Shape() // 设置开始点的位置 shape.moveTo(areaArr[0][0], -1 * areaArr[0][1]) for (let i = 0; i < areaArr.length; i++) { if (areaArr[i].length == 4) { shape.lineTo(areaArr[i][2], -1 * areaArr[i][3]) } else { shape.bezierCurveTo(areaArr[i][2], -1 * areaArr[i][3], areaArr[i][4], -1 * areaArr[i][5], areaArr[i][6], -1 * areaArr[i][7]) } } let material for (let k = 0; k < Config.lineBasicMaterialArr.length; k++) { let color2 = new THREE.Color(lineColor) if (Config.lineBasicMaterialArr[k].color.equals(color2)) { material = Config.lineBasicMaterialArr[k] } } if (!material) { material = new THREE.LineBasicMaterial({ color: lineColor }) //材质对象lineColor Config.lineBasicMaterialArr.push(material) } if (howllowArr && howllowArr.length > 0) { for (let n = 0; n < howllowArr.length; n++) { let hole = new THREE.Path() // 添加孔洞 hole.moveTo(howllowArr[n][0][0], -1 * howllowArr[n][0][1]) for (let k = 0; k < howllowArr[n].length; k++) { if (howllowArr[n][k].length == 4) { hole.lineTo(howllowArr[n][k][2], -1 * howllowArr[n][k][3]) } else { hole.bezierCurveTo(howllowArr[n][k][2], -1 * howllowArr[n][k][3], howllowArr[n][k][4], -1 * howllowArr[n][k][5], howllowArr[n][k][6], -1 * howllowArr[n][k][7]) } } shape.holes.push(hole) } } let scanGeometry, meshMaterial, options = { depth: parseInt(opObj.toHeight), bevelEnabled: false, curveSegments: 24 } scanGeometry = new THREE.ExtrudeGeometry(shape, options) for (let e = 0; e < Config.meshMaterialArr.length; e++) { let color2 = new THREE.Color(entityColor) if (Config.meshMaterialArr[e].color.equals(color2) && Config.meshMaterialArr[e].opacity == alphaModle) { meshMaterial = Config.meshMaterialArr[e] } } if (!meshMaterial) { meshMaterial = new THREE.MeshPhongMaterial({ //MeshStandardMaterial MeshPhongMaterial color: entityColor, transparent: true, opacity: alphaModle, side: THREE.DoubleSide, depthTest: true }) //meshMaterial.color.convertLinearToGamma(0.4); Config.meshMaterialArr.push(meshMaterial) } if (opObj.angleY || opObj.angleZ) { Config.rotateYZ(scanGeometry, (opObj.angleY * Math.PI) / 180, (opObj.angleZ * Math.PI) / 180) } // 创建模型 let mesh = new THREE.Mesh(scanGeometry, meshMaterial) if (!Config.toMapModel && opObj.type != 'wall') { let cubeEdges = new THREE.EdgesGeometry(scanGeometry, 60) let cubeLine = new THREE.LineSegments(cubeEdges, material) cubeLine.renderOrder = indexOrder - 5 mesh.add(cubeLine) } if (opObj.name != 'floor') { mesh.position.z = opObj.site || 0 } else { mesh.position.z = -1 * parseInt(opObj.toHeight) - 1 } mesh.castShadow = true mesh.renderOrder = indexOrder mesh.name = opObj.name || '' return mesh } MyModel_QM.prototype.MyModelText = function (svgArea) { let text = svgArea.data const paths = new THREE.SVGLoader().parse(text).paths const group = new THREE.Group() group.rotateX((parseInt(svgArea.angleZ) * Math.PI) / 180) group.rotateY((parseInt(svgArea.angleY) * Math.PI) / 180) group.rotateZ((parseInt(svgArea.angle) * Math.PI) / 180) group.scale.multiplyScalar(svgArea.scale) group.position.x = parseInt(svgArea.xaxis) group.position.y = -1 * parseInt(svgArea.yaxis) group.position.z = parseInt(svgArea.site) group.scale.y *= -1 let meshMaterial for (let e = 0; e < Config.meshMaterialArr.length; e++) { let color2 = new THREE.Color().setHex(svgArea.entColor) if (Config.meshMaterialArr[e].color.equals(color2) && Config.meshMaterialArr[e].opacity == svgArea.alphaModle) { meshMaterial = Config.meshMaterialArr[e] } } if (!meshMaterial) { meshMaterial = new THREE.MeshStandardMaterial({ color: svgArea.entColor, opacity: parseInt(svgArea.alphaModle) / 100 }) Config.meshMaterialArr.push(meshMaterial) } for (let i = 0; i < paths.length; i++) { const path = paths[i] let shapes = path.toShapes(true) for (let j = 0; j < shapes.length; j++) { const shape = shapes[j] const geometry = new THREE.ExtrudeBufferGeometry(shape, { depth: svgArea.toHeight, bevelEnabled: false, curveSegments: 24 }) const mesh = new THREE.Mesh(geometry, meshMaterial) mesh.renderOrder = 50 mesh.name = svgArea.name || '' mesh.position.set(-svgArea.width / 2, -svgArea.height / 2, 0) group.add(mesh) } } return group } /** * 公共设施 */ MySprite_QM = function (spriteMaterial, obj = null) { THREE.Sprite.call(this) if (obj) { this.navCode = obj.navCode this.no = obj.no this.facCode = obj.facCode this.floor = obj.floorOrder this.site = parseInt(obj.site) || Config.shopHeight + 32 } this.imgUrl this.material = spriteMaterial !== undefined ? spriteMaterial : new SpriteMaterial() //图标跳动 this.jumpIcon = function () { let oldZ = this.site let self = this TweenMax.fromTo( self.position, 0.5, { z: oldZ }, { z: oldZ + 20, repeat: 1, onComplete: function () { TweenMax.to(self.position, 0.2, { z: oldZ }) } } ) } this.reSetSite = function () { //重置位置 this.position.z = this.site } } MySprite_QM.prototype = Object.create(THREE.Sprite.prototype) MySprite_QM.prototype.constructor = MySprite_QM /** * 渲染公共设施 */ Facilities_QM = function () {} Facilities_QM.prototype.renderIcon = function (obj, _this, isShow = true, ele = null) { if (obj) { let spriteMaterial let url = './static/img/' + obj.facCode + '.png' for (let m = 0; m < Config.spriteMaterialArr.length; m++) { if (Config.spriteMaterialArr[m].name == obj.facCode) { spriteMaterial = Config.spriteMaterialArr[m] } } if (!spriteMaterial) { let spriteMap = new THREE.TextureLoader().load(url) spriteMaterial = new THREE.SpriteMaterial({ //sizeAttenuation: false 禁止跟随鼠标缩放 map: spriteMap, depthTest: true, transparent: true }) spriteMaterial.name = obj.facCode Config.spriteMaterialArr.push(spriteMaterial) } let sprite = new MySprite_QM(spriteMaterial, obj) sprite.scale.set(64, 64, 1) sprite.imgUrl = url sprite.center = new THREE.Vector2(0.5, 0) sprite.userData = obj sprite.userData.type = 'icon' sprite.userData.use = ele ? '2d' : 'all' sprite.userData.model = ele sprite.position.set(obj.x, -1 * obj.y, obj.site || Config.shopHeight + 1) sprite.renderOrder = 300 sprite.visible = isShow _this.serObj.add(sprite) if (iconNameShow) { let shopDiv = document.createElement('div') shopDiv.className = 'shopLabel' shopDiv.style.fontSize = '12px' shopDiv.style.height = '14px' shopDiv.innerText = obj.title shopDiv.dataset.name = obj.title shopDiv.dataset.nameEn = Config.iconEn[obj.title] let shopLabel = new THREE.CSS2DObject(shopDiv) shopLabel.position.set(obj.x, -1 * obj.y, 30) _this.iconLabel.add(shopLabel) shopLabel.element.style.pointerEvents = 'none' } } } Facilities_QM.prototype.renderDeviceIcon = function (obj, _this) { if (obj) { let spriteMaterial let url = './static/img/deviceDir.png' for (let m = 0; m < Config.spriteMaterialArr.length; m++) { if (Config.spriteMaterialArr[m].name == 'deviceDir') { spriteMaterial = Config.spriteMaterialArr[m] } } if (!spriteMaterial) { let spriteMap = new THREE.TextureLoader().load(url) spriteMaterial = new THREE.MeshPhongMaterial({ //sizeAttenuation: false 禁止跟随鼠标缩放 map: spriteMap, depthTest: false, transparent: true }) spriteMaterial.name = 'deviceDir' Config.spriteMaterialArr.push(spriteMaterial) } let planeGeometry = new THREE.PlaneGeometry(100, 100) let plane = new THREE.Mesh(planeGeometry, spriteMaterial) plane.center = new THREE.Vector2(0, 0) plane.userData = JSON.parse(JSON.stringify(obj)) plane.userData.type = 'device' plane.position.set(obj.x, -1 * obj.y, 20) plane.rotateZ((parseInt(obj.angle) * Math.PI) / 180) plane.renderOrder = 410 _this.devObj.add(plane) let SpriteDiv = document.createElement('div') SpriteDiv.className = 'other-pop' SpriteDiv.style.background = '#777777' SpriteDiv.innerText = obj.devNum let pointLabel = new THREE.CSS2DObject(SpriteDiv) pointLabel.position.set(obj.x, -1 * obj.y, 0) pointLabel.element.style.pointerEvents = 'none' pointLabel.userData.type = 'dev_IP' pointLabel.userData.code = obj.code pointLabel.userData.name = obj.devNum _this.showTagObj.add(pointLabel) pointLabel.element.style.pointerEvents = 'none' } } /** * 店铺LOGO地图展示类 */ ShopLogo_QM = function () { this.renderIcon = function (obj, _this) { if (obj) { let x = obj.xaxis >> 0 let y = (-1 * obj.yaxis) >> 0 let z = parseInt(Config.shopHeight) + 0.1 let imgW = obj.imgW >> 0 let imgH = obj.imgH >> 0 new THREE.TextureLoader().load(obj.logoUrl, textu => { let planeMaterial = new THREE.MeshPhongMaterial({ map: textu, depthTest: false, transparent: true }) let planeGeometry = new THREE.PlaneGeometry(imgW, imgH) let plane = new THREE.Mesh(planeGeometry, planeMaterial) plane.center = new THREE.Vector2(0, 0) plane.position.set(x, y, z) plane.renderOrder = 70 _this.logoObj.add(plane) }) } } } MySprite_QM.prototype.constructor = MySprite_QM var _selfFindPath FindPath_QM = function () { this.pathArr = [] this.lineDashed //路线指引箭头皮肤 this.planeGeometry = new THREE.PlaneGeometry(128, 128) this.stop = null this._index = 0 //寻路用 this.pathState = 'init' this.pathPlay = { _isPlay: false, get isPlay() { return this._isPlay }, set isPlay(val) { this._isPlay = val if (_selfFindPath && _selfFindPath.stop) { window.cancelAnimationFrame(_selfFindPath.stop) _selfFindPath.stop = null } if (_selfFindPath && _selfFindPath.pathState == 'isPlay' && _selfFindPath._index < _selfFindPath.pathArr.length) { _selfFindPath.playMoveGuide() } } } } FindPath_QM.prototype.clearPath = function () { this.pathArr = [] if (this.lineDashed) { if (Map_QM.mapArr[Config.selectBuild][Config.selectFloor]) { Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.remove(this.lineDashed) } this.lineDashed.destroy() this.lineDashed = null } if (Map_QM.guide) { Map_QM.guide.visible = false if (Map_QM.mapArr[Config.selectBuild][Config.selectFloor]) { Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.remove(Map_QM.man_2d) Map_QM.mapArr[Config.selectBuild][Config.selectFloor].allObj.remove(Map_QM.man_3d) } } if (_selfFindPath && _selfFindPath.stop) { window.cancelAnimationFrame(_selfFindPath.stop) } this._index = 0 this.pathState = 'init' } /** * @param {Object} startNade * @param {Object} toNade * @param {Object} callBack 回调函数 */ FindPath_QM.prototype.onFindPathAnimation = function (pathArray, floorOrder = -1) { if (pathArray && pathArray.length > 0) { this.pathArr = [] for (let j = 0; j < pathArray.length; j++) { this.pathArr.push(pathArray[j]) } this.pathState = 'isPlay' this._index = 0 if (floorOrder === -1) { floorOrder = Config.selectFloor } this.drawPath(floorOrder) } } /** * */ FindPath_QM.prototype.drawPath = function (floorOrder) { let linePath = [] for (let i = 0; i < this.pathArr.length; i++) { if (i < this.pathArr.length) { linePath.push([this.pathArr[i].x, -1 * this.pathArr[i].y]) } } this.lineDashed = new PathLine(12, linePath, parseInt(Config.buildHeight) + 1) this.lineDashed.name = 'lineDash' this.lineDashed.renderOrder = 128 Map_QM.mapArr[Config.selectBuild][floorOrder].allObj.add(this.lineDashed) //////////////////////////////////////////////////////// } FindPath_QM.prototype.guidePathPlay = function (paths) { if (Map_QM.guide) { Map_QM.guide.renderOrder = 99 if (paths.PathPoint) { Map_QM.guide.position.x = paths.PathPoint[0].x Map_QM.guide.position.y = -1 * paths.PathPoint[0].y Map_QM.moveCameraBy2D(paths.PathPoint[0]) Map_QM.man_2d.position.z = Config.shopHeight + 2 Map_QM.man_3d.position.z = Config.buildHeight + 2 Map_QM.guide.visible = true Map_QM.mapArr[Config.selectBuild][paths.floor].allObj.add(Map_QM.man_2d) Map_QM.mapArr[Config.selectBuild][paths.floor].allObj.add(Map_QM.man_3d) } } this.pathArr = paths.PathPoint _selfFindPath = this if (isPathPlay) { this.pathPlay.isPlay = true } } /** * 播放图标指引动画 */ FindPath_QM.prototype.playMoveGuide = function () { if (!Map_QM.guide) { return } let px = Map_QM.guide.position.x let py = Map_QM.guide.position.y let targetX = _selfFindPath.pathArr[_selfFindPath._index].x - px let targetY = -1 * _selfFindPath.pathArr[_selfFindPath._index].y - py let dist = Math.sqrt(targetX * targetX + targetY * targetY) let df = Math.ceil(dist / Config.playSpeed) let dx = (_selfFindPath.pathArr[_selfFindPath._index].x - px) / df let dy = (-1 * _selfFindPath.pathArr[_selfFindPath._index].y - py) / df let ang = 0 if (df < 2) { Map_QM.guide.position.x = _selfFindPath.pathArr[_selfFindPath._index].x Map_QM.guide.position.y = -1 * _selfFindPath.pathArr[_selfFindPath._index].y Map_QM.moveCameraBy2D({ x: _selfFindPath.pathArr[_selfFindPath._index].x, y: _selfFindPath.pathArr[_selfFindPath._index].y }) Map_QM.man_3d.rotation.z = Map_QM.man_2d.rotation.z = 0 //180; _selfFindPath._index++ if (_selfFindPath._index > 0 && _selfFindPath._index < _selfFindPath.pathArr.length) { let s = Math.sqrt( (_selfFindPath.pathArr[_selfFindPath._index].x - _selfFindPath.pathArr[_selfFindPath._index - 1].x) * (_selfFindPath.pathArr[_selfFindPath._index].x - _selfFindPath.pathArr[_selfFindPath._index - 1].x) + (_selfFindPath.pathArr[_selfFindPath._index].y - _selfFindPath.pathArr[_selfFindPath._index - 1].y) * (_selfFindPath.pathArr[_selfFindPath._index].y - _selfFindPath.pathArr[_selfFindPath._index - 1].y) ) ang = Math.acos((_selfFindPath.pathArr[_selfFindPath._index].y - _selfFindPath.pathArr[_selfFindPath._index - 1].y) / s) if (_selfFindPath.pathArr[_selfFindPath._index].x < _selfFindPath.pathArr[_selfFindPath._index - 1].x) { Map_QM.man_3d.rotation.z = Map_QM.man_2d.rotation.z = Math.PI - ang } else { Map_QM.man_3d.rotation.z = Map_QM.man_2d.rotation.z = Math.PI + ang } } for (let t = 0; t < Map_QM.forShopArr[_indexPathFloor].wayShop.length; t++) { if (Map_QM.forShopArr[_indexPathFloor].wayShop[t].pathIndex == _selfFindPath._index) { pathShop = Map_QM.forShopArr[_indexPathFloor].wayShop[t].shop.houseNumber let shops = Map_QM.mapArr[Config.selectBuild][parseInt(Map_QM.forShopArr[_indexPathFloor].wayShop[t].shop.floorOrder)].allObj.children for (let i = 0; i < shops.length; i++) { if (shops[i].userData.shopData && shops[i].userData.shopData.houseNumber === pathShop) { let shopModel = shops[i] let material = shopModel.material shopModel.material = new THREE.MeshBasicMaterial({ color: Config.navColor }) TweenMax.to(shopModel.scale, 0.8, { z: 3, yoyo: true, ease: Cubic.easeIn, onComplete: function () { if (shopModel) { TweenMax.to(shopModel.scale, 0.5, { z: 1 }) shopModel.material = material } } }) break } } break } } Map_QM.dispatchEvent({ //寻路中返回小人当前所在点位 type: 'PathPlaying', data: { pathArrIn: _indexPathFloor, pathIndex: _selfFindPath._index, shopNum: pathShop } }) } else { px += dx py += dy Map_QM.guide.position.x = px Map_QM.guide.position.y = py Map_QM.moveCameraBy2D({ x: px, y: -1 * py }) } if (_selfFindPath.pathPlay.isPlay && Map_QM.guide) { if (_selfFindPath._index < _selfFindPath.pathArr.length) { _selfFindPath.stop = requestAnimationFrame(_selfFindPath.playMoveGuide) } else { if (_selfFindPath && _selfFindPath.stop) { window.cancelAnimationFrame(_selfFindPath.stop) _selfFindPath.stop = null } _selfFindPath._index = 0 if (Map_QM.guide) { Map_QM.guide.visible = false } let myEvent = new CustomEvent('pathOver', { detail: { dx: dx, dy: dy } }) document.dispatchEvent(myEvent) //触发导航完成事件 _selfFindPath.pathState = 'pathOver' } } }