分享uniapp移动端和h5项目如何使用谷歌地图

发布时间 2024-01-13 14:17:01作者: MosterSeven

项目背景

使用 uniapp + vue2 开发的国外类地图 APP,原本是打算使用 uniapp 打安卓包的方式,最后由于种种原因变成在安卓壳子里使用 webview 的套壳 APP,所以最终效果其实是跟 h5 的一致。

两种路子都试了,中间踩了非常非常非常多的坑!但是不管怎样,最终能上线成功,所以汇总一下我的血泪经验。

注意:使用google map的时候需要使用科学的方式上网,大家自备哈

谷歌地图google map官方文档:文档地址

npm 包加载谷歌地图并初始化挂载

文档里提供了三种加载 Maps JavaScript API 的方法,我这里使用的是第二种:使用官方提供的 npm 包 @googlemaps/js-api-loader 来加载谷歌地图。

先 npm 导包,然后在需要的页面进行局部注册。(没采用全局注册是因为担心全局注册可能会导致在不需要使用谷歌地图的页面加载缓慢,不过本人并没有试过全局注册是否会导致这种问题。)

<template>

<div id="map"></div>

注意:需要在 css 里面设置 div 的高度

<script>

import {
    Loader
} from "@googlemaps/js-api-loader";

export default{
	data(){
	    return {
	        // 当前地图实例
	        map: null, 
	        // 渲染地图缩放等级,具体数字详见google map文档
	        zoom: 15, 
	        // 渲染地图中心坐标
	        center: { lat: null, lng: null }, 
	    },
	},
	async mounted(){
	    const loader = new Loader({
	       apiKey: "AIzaSyAjGAXtZ9bnk9H2CaLy9C6YsinMOAI4fVk",// 密钥
	       version: "weekly",// 版本
	       libraries: ["places"],
	    });
	    
	    await this.initMountedMap();
	}
}

methods 里的初始化函数

async initMountedMap(zoom, center) {
    const {
        Map
    } = await google.maps.importLibrary("maps");
    // 获取dom节点
     let map = new Map(document.getElementById("map"), {
      // 地图缩放等级
        zoom: zoom || 15,
        // 地图中心点
        center: center || { lat: -34.397, lng: 150.844 },
        // gestureHandling: "greedy",
        // 禁用默认控件ui
        disableDefaultUI: true,
     });
    this.map = map;
    
	// 地图缩放相关代码,不是重点
    this.zoom = map.getZoom();
    window.google.maps.event.addListener(map, "zoom_changed", () => {
        this.zoom = map.getZoom();
    });
},

初始化函数是异步函数。

注意点:

这里把 Loader 和初始化函数放在 mounted 周期里是为了谷歌地图在加载时 Map 需要挂载的 dom 节点已经生成,让谷歌地图能顺利获取到 dom 节点并加载出来。
如果说你需要从后端接口获取这个 API key 的话,那么放 mounted 里从接口获取 API key 也可以。

但是,因为页面 mounted 周期只执行一次,如果你的页面在还未获取到接口返回的 key 并挂载成功之前就跳转到其他页面的话,会导致谷歌地图的 js 代码加载不全,无法在页面显示时成功显示地图。

解决方法:

把从接口获取谷歌 key 的函数放到全局 js 里面,然后在 App.vue 里的 onLaunch 周期里调用函数并获取 key ,把获取到的内容存 localStorage 内供全局获取。然后在需要调取谷歌地图的页面里,从 localStorage 里获取到 key,然后把 Loader 放在 onLoad 周期里,初始化在 onShow 周期里。

需要注意,所有对地图的操作都需要在初始化完成之后(也就是说需要注意函数的执行顺序!)

代码如下:

onLoad() {
	const loader = new Loader({
		apiKey: uni.getStorageSync('googleKey'),
		version: "weekly",
		libraries: ["places"],
	});
	// 导入google map地图类
	loader.importLibrary("maps");
	// 导入google map地点类
	loader.importLibrary("geocoding");
},
onShow() {
	if (this.map === null) {
		this.initMountedMap();
	}
},

注意: 如果你的地图遇到 Only secure origins are allowed 问题,是因为谷歌地图需要 https 或者使用 localhost 才能访问到。本地测试用 localhost 地址,线上的话使用 https 服务就能正常显示了。

真机使用地图

使用了地图 api 的 uniapp 需要打自定义基座之后才能用第三方 sdk

uniapp 使用谷歌地图想要在真机显示,如果不采用安卓 apk 套壳 webview 方法的话,还得是用它提供的 <map> 组件和获取位置等函数。(用直接获取 dom 节点挂载地图只能在 h5 和 webview 里面正常显示,很怪)

  • 本人尝试过使用 nvue 页面和 uniapp 套 webview 加载谷歌地图,但是这两种方法在 h5 都正常,在真机却都无法正常使用,一直报 “vue warn:TypeError: Cannot read property 'google' of undefined ”。估计应该是 Loader 和谷歌相关 js 根本就没加载成功的问题,当然也有可能是因为测试的手机没有谷歌框架,但是我拿自己的 pixel4 测也是这个问题,不知道到底是异步加载问题还是啥,服了。

使用 uniapp 提供的 <map> 组件的话,搭配官方提供的获取位置/设置标记等 API 的使用基本可以实现谷歌地图的大部分功能。

需要注意的点:

  1. 使用 uniapp 的 uni.getLocation ,默认是获取 wgs84 编码格式的坐标。如果想用 gcj02 编码格式的经纬度,需要配置第三方 sdk 的 API key。

谷歌地图用的就是 wgs84 的坐标,不用额外配东西。

  1. 使用 <map> 组件里面的 markers 的时候,经纬度不能设为 null ,否则会导致自定义基座闪退报错。

基座闪退报错的 log 位置可以在 这里 看官方文档获取

  1. 使用 <map> 组件的谷歌地图没法使用反向地理编码(官方 API 不支持)

使用 <map> 组件的具体实现代码参考 uniapp 官方文档内的写法即可。