import * as VPASSDK from "./VPASSDK-1.2.5.js"; import { getLocation, getProjectBasicInfo, stopBeaconScan, } from "ais-h5-location"; import { createScopedThreejs } from "threejs-miniprogram"; import { registerGLTFLoader } from "../../loader/gltfLoader"; var THREE; import { STATES, promptStateMap } from "./index-helper"; import { getMapData, post } from "../../getMapData"; let configData; var _canvas; let map; const icons = { left: "/pages/index/left.png", straight: "/pages/index/straight.png", right: "/pages/index/right.png", end: "/pages/index/end.png", escalatorUp: "/pages/index/escalator-up.png", escalatorDown: "/pages/index/escalator-down.png", elevatorUp: "/pages/index/elevator-up.png", elevatorDown: "/pages/index/elevator-down.png", }; let locationOption; let initFloorOrder; Page({ /** * 页面的初始数据 */ data: { inited: false, locX: 0, locY: 0, floors: [], floorOrder: null, floorId: null, floorName: null, windowHeight: 812, states: STATES, state: STATES.selectFloor, promptStateMap, map: null, e: null, lastLoc: null, tab: 1, showTabs: false, shop: null, nextFloor: null, nextNextFloor: null, navIcon: icons.straight, distanceToNextPoint: null, msgTop: "", nextPointType: null, msgBottomLeft: "", msgBottomRight: "", totalDistance: 0, distance: 0, arriveEnd: false, theta: null, flash: "off", parkTop: 0, parkLeft: 0, flash_isFilter: "true", parkWidth: 0, parkHeight: 0, session: null, filteredMallFloors: [], filteredParkFloors: [], selectMall: true, safeTop: 54, frameSize: "medium", leftYaw: true, rightYaw: true, searchType: 0, point: null, resolution: null, poiMap: {}, warningCount: 0, bluetoothOpened: false, relocateCount: 0, isIOS: false, mode: "bluetooth", doLoad: false, }, setSelectMall() { this.setData({ selectMall: true }); }, setSelectPark() { this.setData({ selectMall: false }); }, /** * 生命周期函数--监听页面加载 */ async onLoad({ e, searchType = 0, floorOrder }) { const res = wx.getSystemInfoSync(); const isIOS = res.brand === "iPhone"; // console.log(isIOS); // if (!isIOS) return wx.redirectTo({ url: `/pages/bluetooth/index?e=${e}&searchType=${searchType}&floorOrder=${floorOrder}`, }); this.setData({ doLoad: true }); initFloorOrder = floorOrder; const { config, poiMap } = await getMapData(); configData = config; this.poiMap = poiMap; this.frameIndex = 0; this.getSystemInfo(); wx.setKeepScreenOn({ keepScreenOn: true, }); const { globalData: { floors }, } = getApp(); const { windowHeight } = wx.getSystemInfoSync(); this.setData({ floors, filteredMallFloors: floors.filter( ({ url, isPark }) => url !== null && !isPark ), filteredParkFloors: floors.filter( ({ url, isPark }) => url !== null && isPark ), windowHeight, e: decodeURIComponent(e), searchType: Number(searchType), }); this.initSDK(config); this.openBluetooth(); }, openBluetooth() { return new Promise((resolve, reject) => { wx.openBluetoothAdapter({ success: () => { this.setData({ bluetoothOpened: true }); console.log("bluetoothOpened"); resolve(); }, fail: ({ errno, errCode }) => { this.setData({ bluetoothOpened: false }); console.warn("蓝牙未开启", errCode || errno); reject(errCode || errno); }, }); }); }, async startBlueTooth(currentFloor) { const ak = configData.map.find(({ floor_id }) => floor_id == currentFloor) .ak; console.log("ak:", ak); if (!ak) return console.warn("no ak"); let projectInfo; try { projectInfo = await getProjectBasicInfo(ak); } catch (error) { console.log("getProjectBasicInfo", error); } try { if (this.data.bluetoothOpened !== true) await this.openBluetooth(); } catch (err) { return this.setState( err === 103 ? STATES.bluetoothRequired : STATES.systemBluetoothRequired ); } if (!this.sdkInited) { try { this.SDK.initAR( { rgba2JpegCanvas: "#capture", // 压缩jpeg的Canvas,必填 webGLCanvas: "#webgl", // 绘制AR内容canvas,必填 }, THREE, _canvas ); } catch (error) { console.log(error); } } this.sdkInited = true; this.SDK.requestVPASLocation(locationOption); getLocation({ projectInfo, intervalTime: 1000, // 定时器(请求频率时长) successCb: (result) => { // console.log("getLocation -> successCb", result); const { location, floor = "", indoor } = result.body && result.body[0]; const sameFloor = (floor && floor.toUpperCase()) == currentFloor; // 判断是否为同一楼层(避免获取同临楼层定位信息) if (!sameFloor) { this.finishARNavigation(); return this.setState(STATES.fail8s); } // pointX、pointY格式处理 const splitArray = location && location.split(","); const pointX = parseFloat(splitArray[0]); const pointY = parseFloat(splitArray[1]); // 过滤蓝牙location 无效值 let isValidLocation; if (splitArray && splitArray.length) { isValidLocation = pointX > 1 && pointY > 1; } if (sameFloor && indoor === "1") { isValidLocation && this.SDK.updateBluetoothLocation(pointX, pointY); } }, failCb: (e) => { console.log("failCb", e); try { const { errCode, errno } = e; if (errCode === 11000) { console.log("系统蓝牙未开启"); return this.setState(STATES.systemBluetoothRequired); } else if (errno && errno === 1) { console.log("未扫描到附近蓝牙信息"); this.finishARNavigation(); return this.setState(STATES.fail8s); } } catch (error) { console.log("failCb error", error); } }, }); }, initSDK(config) { console.log(config); this.SDK = new VPASSDK.SDKWrapper(config); wx.createSelectorQuery() .select("#webgl") .node() .exec((res) => { _canvas = res[0].node; // todo 初始化引擎放到我们系统初始化中 // 创建一个与 canvas 绑定的 three.js THREE = createScopedThreejs(_canvas); // SXC registerGLTFLoader(THREE); }); }, requestLocation(stateReset = true) { this.ignoreArriveEnd = false; if (!(this.data.inited && this.data.floorOrder !== null)) return; if (!this.sdkInited) { try { this.SDK.initAR( { rgba2JpegCanvas: "#capture", // 压缩jpeg的Canvas,必填 webGLCanvas: "#webgl", // 绘制AR内容canvas,必填 }, THREE, _canvas ); } catch (error) { console.log(error); } } this.sdkInited = true; locationOption = { floor_id: this.data.floorId, onLocateEvent: (res) => { console.log("onLocateEvent", this.data.relocateCount); if (!res.locStatus) this.setData({ relocateCount: this.data.relocateCount + 1, }); if (this.data.relocateCount >= 8) { this.setData({ relocateCount: 0, }); this.SDK.finishARNavigation(); this.requestLocation(false); } }, distanceOfNextPoint: () => {}, onVerticalYaw: () => { this.setData({ warningCount: this.data.warningCount + 1 }); }, onSaveViewmatrix: (res) => {}, onSaveFusion_pose: (res) => {}, distanceOfNextPoint: (res) => { // console.log("distanceOfNextPoint", res); }, onFirstSuccess: (res) => { stopBeaconScan(); this.firstSuccess(res); this.setData({ totalDistance: 0, lastLoc: [res.locX, res.locY], state: STATES.successPrompt, flash: "off", }); }, onBluetoothAndScanStatus: (res) => { console.log("关于蓝牙和扫描框状态:", res.type); // if (res.type === true) { // if (this.data.state === STATES.bluetoothRequired) { // this.requestLocation(); // } // } else if (res.type === 99) { // this.setState(STATES.bluetoothRequired); // this.finishARNavigation(); // } }, onError: (e) => { const { type } = e; if (type !== 10010) console.log("error:", e); if (type === 10005) return this.setState(STATES.fail5s); if (type === 10007) { this.finishARNavigation(); return this.setState(STATES.fail8s); } if (type === 10008) { return wx.showModal({ title: "提示", content: "断网了,重新链接?", showCancel: false, success: (res) => { if (res.confirm) { console.log("用户点击确定"); // 获取网络信号 wx.getNetworkType({ success: (res) => { locationOption.onNetWork({ type: res.networkType, msg: "获取网络类型", }); if (res.networkType !== "none") { this.SDK.requestVPASLocation(locationOption); } else { locationOption.onError({ type: 10008, msg: "监听网络信号失败", }); } }, fail: (err) => { locationOption.onError({ type: 10008, msg: "监听网络信号失败", }); }, }); } }, }); } if (type === 10009) { return wx.showToast({ icon: "none", title: "车位号无效\n换个试试吧", }); } if (type === 10010) { return wx.showToast({ icon: "none", title: "请以垂直水平角度手持手机", }); } wx.showToast({ icon: "none", title: "定位失败\n错误码:" + type, }); }, onEverySuccess: (res) => { // 跟踪回调 const lastLoc = [res.locX, res.locY]; const { distance, distanceToNextPoint, type: nextPointType, orientation: theta, } = res; if (!this.data.totalDistance) this.setData({ totalDistance: distance }); const remainMinutes = Math.floor(distance / 1.4 / 60); this.setData({ theta, distance, distanceToNextPoint, nextPointType, msgBottomLeft: "剩余" + Math.floor(distance) + "米", msgBottomRight: remainMinutes ? remainMinutes + "分钟" : "< 1 分钟", }); this.setMsgTopAndIcon(); if ( this.data.lastLoc && this.data.lastLoc[0] === lastLoc[0] && this.data.lastLoc[1] === lastLoc[1] ) return; this.setData({ lastLoc, }); }, onNetWork: (status) => { console.log("network", status); }, onArrive: (res) => { // console.log("onArrive", res); if (res.type === 3 || res.type === 4) { this.finishARNavigation(); this.setState(STATES.arriveElevator); } else if (res.type === 5 || res.type === 6) { this.finishARNavigation(); this.setState(STATES.arriveEscalator); } else if (res.type === 8 || res.type === 9) { this.finishARNavigation(); this.setState(res.type === 8 ? STATES.arrivePark : STATES.arriveMall); } else if (res.type === 7) { if (this.ignoreArriveEnd) return; this.setData({ arriveEnd: true }); this.setMsgTopAndIcon(); this.setState(STATES.arriveEnd); this.ignoreArriveEnd = true; } }, onResourceLoaded: (res) => { console.log("AR资源导航加载完成"); }, onStep: () => {}, onYaw: ({ type }) => { this.setData({ leftYaw: type === 1 || type === 2, rightYaw: type === 0 || type === 2, }); }, onPoiEvent: async ({ eventUrl }) => { if (eventUrl.includes("***")) { let [appId, path] = eventUrl.split("***"); wx.navigateToMiniProgram({ appId, path, }); return; } const poi = this.poiMap[eventUrl]; console.log(eventUrl, poi); if (poi && !this.locked) { this.locked = true; setTimeout(() => { this.locked = false; }, 5000); const memberID = getApp().globalData.memberID; if (poi.responseType === "活动" && memberID) { const { msg } = await post("/api/ar/v1/applet/GetUserAward", { memberID, poiCode: poi.code, }); return wx.showModal({ content: msg, }); } if (poi.appId) { return wx.navigateToMiniProgram({ appId: poi.appId, path: poi.path, }); } if (poi.webUrl) { wx.navigateTo({ url: `/pages/detail/index?url=${encodeURIComponent(poi.webUrl)}`, }); } } }, }; console.log(locationOption); this.startBlueTooth(locationOption.floor_id); if (stateReset) this.setData({ state: STATES.startScan, arriveEnd: false, showTabs: false, }); }, setMsgTopAndIcon() { const { distanceToNextPoint, nextPointType, arriveEnd, nextFloor, floors, } = this.data; if ( distanceToNextPoint !== null && nextPointType !== null && nextPointType !== -1 ) { const flooredDistance = Math.floor(distanceToNextPoint); const nextFloorName = nextFloor ? nextFloor.name : ""; this.setData({ msgTop: arriveEnd ? "已到达" : distanceToNextPoint > 20 ? `直行${flooredDistance}米` : nextPointType === 7 ? `${flooredDistance}米后 到达终点` : nextPointType === 8 ? `${ distanceToNextPoint < 5 ? "" : `${flooredDistance}米后 ` }到达停车位入口` : nextPointType === 9 ? `${ distanceToNextPoint < 5 ? "" : `${flooredDistance}米后 ` }到达商场入口` : [3, 4, 5, 6].includes(nextPointType) ? `${ distanceToNextPoint < 5 ? "" : `${flooredDistance}米后 ` }乘梯至${nextFloorName}` : [1, 2].includes(nextPointType) ? `${ distanceToNextPoint < 5 ? nextPointType === 1 ? "左转" : "右转" : `${flooredDistance}米后 ${ nextPointType === 1 ? "左转" : "右转" }` }` : distanceToNextPoint < 5 ? `${nextPointType === 1 ? "左转" : "右转"}` : "", navIcon: arriveEnd ? icons.end : distanceToNextPoint > 20 ? icons.straight : { 1: icons.left, 2: icons.right, 3: icons.elevatorUp, 4: icons.elevatorDown, 5: icons.escalatorUp, 6: icons.escalatorDown, 7: icons.end, }[nextPointType], }); } }, setFloor({ currentTarget: { id } }) { const floor = this.data[ this.data.selectMall ? "filteredMallFloors" : "filteredParkFloors" ][id]; this.setData({ floorOrder: floor.floorOrder, point: null, floorId: floor.floorId, floorName: floor.name, }); }, firstSuccess(res) { console.log("firstSuccess---", this.data.e); if (!map) return; let shop = map.shopMap[this.data.e] ? map.shopMap[this.data.e] : map.facilityMap[this.data.e] ? map.facilityMap[this.data.e] : map.pMap[this.data.e]; if (!shop) { const endParamList = this.data.e.split("_"); if (endParamList.length === 3) { let [efloororder, epoint, ename] = endParamList; efloororder = Number(efloororder); epoint = Number(epoint); shop = { isDevice: true, name: ename, navPoint: epoint, yaxis: epoint, floorOrder: efloororder, floorName: map.mall.floors[efloororder][1], }; } } console.log("end", shop); if (!shop) return wx.showToast({ icon: "none", title: `未匹配到该终点:${this.data.e}`, }); this.setData({ shop: { ...shop, logoPath: shop.logoPath ? shop.logoPath.replaceAll("\\", "/") : null, }, }); const { naviData, nextFloorId, nextNextFloorId } = map.requestRoute( [res.locX, res.locY, this.data.floorId], { floorOrder: shop.floorOrder, navPoint: shop.yaxis } ); if (!naviData) return; let nextFloor = this.data.floors.find( ({ floorId }) => nextFloorId == floorId ); let nextNextFloor = this.data.floors.find( ({ floorId }) => nextNextFloorId == floorId ); this.setData({ nextFloor, nextNextFloor }); let final = new THREE.Vector3( naviData[naviData.length - 1].x, naviData[naviData.length - 1].y, 0 ); if (!nextFloor) { naviData.pop(); if (naviData.length === 1) { naviData.push({ ...naviData[0], pointType: 1, x: (naviData[0].x + final.x) / 2, y: (naviData[0].y + final.y) / 2, }); } } console.log("naviData", naviData, "final", final); this.SDK.setNavigationData( naviData, final, res.wasmFunction, locationOption ); this.SDK.startARNavigation(); setTimeout(() => { this.setState(STATES.inNav); this.setData({ showTabs: true, tab: 1, }); }, 1000); }, exit() { wx.redirectTo({ url: "/pages/h5map/index", }); }, handleToMap() { this.setState(STATES.tomap); }, handlePromptBtn({ detail: { btn } }) { const { state } = this.data; if (state === STATES.bluetoothRequired) { if (btn === "1") return wx.openSetting({ withSubscriptions: true, success: ({ authSetting }) => { if (authSetting["scope.bluetooth"]) { this.setState(STATES.startScan); this.startBlueTooth(locationOption.floor_id); } }, }); if (btn === "2") return this.exit(); } if (state === STATES.systemBluetoothRequired) { if (btn === "1") return this.exit(); } if (state === STATES.fail8s) { if (btn === "1") return this.requestLocation(); if (btn === "2") return this.setState(STATES.selectFloor); if (btn === "3") return this.exit(); } if (state === STATES.wrongLocation) return this.exit(); if (state === STATES.badConnection) return this.requestLocation(); if ( state === STATES.arriveEscalator || state === STATES.arriveElevator || state === STATES.arriveMall || state === STATES.arrivePark ) { const nextFloor = this.data.nextNextFloor ? this.data.nextNextFloor : this.data.nextFloor; if (nextFloor) this.setData({ floorOrder: nextFloor.floorOrder, point: null, floorId: nextFloor.floorId, floorName: nextFloor.name, }); return this.requestLocation(); } if (state === STATES.requestEnd || state === STATES.arriveEnd) { if (btn === "1") return this.exit(); if (btn === "2") return this.setState(STATES.inNav); } if (state === STATES.noCamera) { if (btn === "1") return wx.openSetting({ withSubscriptions: true, success: ({ authSetting }) => { if (authSetting["scope.camera"]) { wx.redirectTo({ url: `/pages/index/index?e=${this.data.e}`, }); } }, }); if (btn === "2") return this.exit(); } if (state === STATES.tomap) { if (btn === "1") return wx.redirectTo({ url: `/pages/h5map/index?e=${this.data.e}`, }); if (btn === "2") return this.setState(STATES.inNav); } }, setState(state) { if ( (this.data.state === STATES.arriveEscalator || this.data.state === STATES.arriveElevator || this.data.state === STATES.arriveMall || this.data.state === STATES.arrivePark) && state === STATES.inNav ) return console.log("到达弹窗出现忽略导航中状态变更触发"); if ( state === STATES.systemBluetoothRequired && this.data.state !== STATES.systemBluetoothRequired ) { systemBluetoothInterval = setInterval(() => { this.openBluetooth() .then(() => { if (systemBluetoothInterval) { clearInterval(systemBluetoothInterval); systemBluetoothInterval = null; } this.setState(STATES.startScan); this.startBlueTooth(locationOption.floor_id); }) .catch((err) => { this.setState( err === 103 ? STATES.bluetoothRequired : STATES.systemBluetoothRequired ); }); }, 1000); } if ( this.data.state === STATES.systemBluetoothRequired && state === STATES.bluetoothRequired ) return; if (this.data.state === STATES.inNav && state === STATES.fail8s) { this.setData({ relocateCount: 0, }); this.SDK.finishARNavigation(); this.requestLocation(false); return; } if ( this.data.state === STATES.inNav && (state === STATES.fail5s || state === STATES.successPrompt) ) return; this.setData({ state, }); }, handleMap({ detail }) { map = detail; this.setData({ inited: true, }); const { floors } = this.data; const floor = floors[initFloorOrder]; initFloorOrder = undefined; if (floor) { this.setData({ floorOrder: floor.floorOrder, point: null, floorId: floor.floorId, floorName: floor.name, }); this.requestLocation(); } }, handleTabChange({ detail }) { this.setData({ tab: detail, }); if (this.SDK) { this.SDK.updateOnScreenArrowPosition( detail == 0 ? new THREE.Vector3(0, -0.015, -0.25) : new THREE.Vector3(0, -0.06, -0.25) ); } }, handleRelocate() { this.SDK.finishARNavigation(); this.requestLocation(); }, handleNavExit() { this.setState(STATES.requestEnd); }, handleNoCamera() { this.setState(STATES.noCamera); }, flashlightBtn() { if (this.data.flash === "off") { this.setData({ flash: "on", flash_isFilter: "true", }); } else { this.setData({ flash: "off", }); } }, getSystemInfo() { const res = wx.getSystemInfoSync(); const isIOS = res.brand === "iPhone"; console.log("设备信息:", res); let parkWidth = 248; let parkHeight = 70; let parkTop = res.windowHeight - parkHeight; let parkLeft = res.windowWidth - parkWidth; this.setData({ parkWidth: parkWidth, parkHeight: parkHeight, parkTop: parkTop / 2, parkLeft: parkLeft / 2, safeTop: res.safeArea.top, isIOS: isIOS, frameSize: isIOS ? "medium" : "small", resolution: isIOS ? "high" : "small", }); console.log( "宽高左上:", this.data.parkWidth, this.data.parkHeight, this.data.parkTop, this.data.parkLeft ); }, clickScreen(e) { let detail = e.detail; this.SDK.getScreenCoordinate({ x: detail.x, y: detail.y }); console.log("点击屏幕x, y:", detail.x, detail.y); }, touchMove(event) { this.SDK.getScreenCoordinate(event.touches[0]); }, touchCancel(event) { this.SDK.touchCancel(); }, touchStart() { console.log("touch Start"); this.SDK.touchStart(); }, touchEnd() { console.log("touch End"); this.SDK.touchCancel(); }, /** * 生命周期函数--监听页面显示 */ onShow: function () { this.getSystemInfo(); }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () {}, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { this.finishARNavigation(true); if (this.SDK) this.SDK.releaseRender(); }, finishARNavigation(isUnload = false) { if (this.SDK) this.SDK.finishARNavigation(isUnload); try { if (systemBluetoothInterval) { clearInterval(systemBluetoothInterval); systemBluetoothInterval = null; } stopBeaconScan(); } catch (error) {} this.setData({ totalDistance: 0, relocateCount: 0 }); }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () {}, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () {}, /** * 用户点击右上角分享 */ onShareAppMessage({ from }) { const e = from === "button" && this.data.point !== null ? `${this.data.floorOrder}_${this.data.point}_来自分享的位置` : null; return e === null ? { title: "", path: "/pages/h5map/index", imageUrl: "/pages/h5map/share.png", } : { title: "来自分享的位置", path: `/pages/h5map/index?e=${e}`, imageUrl: "/pages/h5map/share.png", }; }, handlePoint({ detail }) { this.setData({ point: detail }); }, });