首页>>前端>>Vue->【每日一拳】打在 vue+高德地图api 封装选址组件实现上的

【每日一拳】打在 vue+高德地图api 封装选址组件实现上的

时间:2023-11-30 本站 点击:2

起因

之前公司有个调用地图选址组件来实现快速定位的需求。

原本是一个很简单的需求,之前调用的是腾讯地图的选点组件,通过 iframe 内嵌调用,具体可以查看 腾讯地图 的官网,代码如下。一开始都是可以正常使用的,但是最近发现腾讯地图 api 会疯狂报错,提示 “您已关闭GPS,请在设置>隐私>位置里打开”,目前还没有找到解决方案。

<iframe id="mapView" width="100%" height="100%" frameborder=0    src="https://apis.map.qq.com/tools/locpicker?search=1&type=1&key=your key&referer=myapp"></iframe><script>    window.addEventListener('message', function(event) {        // 接收位置信息,用户选择确认位置点后选点组件会触发该事件,回传用户的位置信息        var loc = event.data;        if (loc && loc.module == 'locationPicker') {//防止其他应用也会向该页面post信息,需判断module是否为'locationPicker'          console.log('location', loc);        }    }, false);</script>

于是转战高德地图的选址组件,高德同样封装了一个组件可以供我们使用 iframe 内嵌调用,具体可以查看 高德地图 的官网,代码如下。但是这个选址组件功能过于简易,定位不会根据选址列表的选中而移动,由于是通过 iframe 内嵌使用,还不易修改代码功能,还是不满足既定需求。

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>

(function(){    var iframe = document.getElementById('test').contentWindow;    document.getElementById('test').onload = function(){      iframe.postMessage('hello','https://m.amap.com/picker/');    };    window.addEventListener("message", function(e){      alert('您选择了:' + e.data.name + ',' + e.data.location)    }, false);}())

最终决定,使用零碎的高德地图 api 来自己封装一个简易的选址组件。就目前而言,对于高德地图 api 的使用看到过三种方式:

引入 @amap/amap-jsapi-loader

引入原生的高德地图 api

引入 vue-amap

由于 vue-amap 是一套基于 Vue 2.0 和高德地图的地图组件且已经很久不维护了,所以不予考虑。

对于高德地图 api 三种方式的基本使用,我之前有一片文章 《vue+高德地图api基础实践》 已经非常详细讲解了,大家有兴趣可以参考一下,接下来就直接开始封装。

@amap/amap-jsapi-loader

@amap/amap-jsapi-loader 这个依赖是目前高德地图主流的一种使用方式。

1. 安装依赖并引入

pnpm install @amap/amap-jsapi-loader

然后在 components 下创建 Amap 组件并且引入之

import AMapLoader from "@amap/amap-jsapi-loader";

2. 初始化地图

使用 AMapLoader.load 来初始化渲染地图组件,使用 AMap.Map 类创建和展示地图对象。

let map;const mapConfigure = {  amapKey: "", // 申请好的Web端开发者Key  options: {    resizeEnable: true,  // 是否监控地图容器尺寸变化    center: [121.553958, 29.869472],  // 初始地图中心点    zoom: 14, // 初始地图级别  }};onBeforeMount(() => {  if (!instance) return;  let { options } = MapConfigure;  AMapLoader.load({    key: mapConfigure.amapKey,    version: "2.0",    plugins: [],    AMapUI: {      version: "1.1",      plugins: []    }  })    .then(AMap => {      // 创建地图实例      map = new AMap.Map(instance.refs.mapView, options);    })    .catch(() => {      throw "地图加载失败,请重新加载";    });});

<template>  <div class="map-container">    <div ref="mapView" class="map-view"></div>  </div></template><style lang="scss" scoped>.map-container {  background-color: #fff;  width: 100%;  height: 100vh;  .map-view {    position: relative;    width: 100%;    height: 50vh;    position: fixed !important;    top: 0;  }}</style>

3. 使用 Geolocation 实现定位

初始化地图以后,可以使用高德地图提供的 Geolocation 插件来实现定位功能。

AMapLoader.load({  key: mapConfigure.amapKey,  version: "2.0",  plugins: [],  AMapUI: {    version: "1.1",    plugins: []  }})  .then(AMap => {    // 创建地图实例    map = new AMap.Map(instance.refs.mapView, options);    map.plugin(["AMap.Geolocation"], () => {      let geolocation = new AMap.Geolocation({      // 是否使用高精度定位,默认:true          enableHighAccuracy: true,          // 设置定位超时时间,默认:无穷大          timeout: 10000,          // 定位按钮的停靠位置的偏移量,默认:Pixel(10, 20)          buttonOffset: new AMap.Pixel(10, 20),          //  定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false          zoomToAccuracy: true,          //  定位按钮的排放位置,  RB表示右下          buttonPosition: "RB"      });      map.addControl(geolocation);      geolocation.getCurrentPosition(function (status, result) {        if (status == "complete") {          onComplete(result);        } else {          onError(result);        }      });    });    function onComplete(data) {      map.setCenter(data.position);    }    function onError(error) {      console.log("error", error);    }  })  .catch(() => {    throw "地图加载失败,请重新加载";  });

4. 使用 PositionPicker 拖拽选址创建列表

PositionPicker(拖拽选址),用于在地图上选取位置,并获取所选位置的地址信息,以及周边POI、周边道路、周边路口等信息。

加载 PositionPicker(模块名:ui/misc/PositionPicker

AMapLoader.load({  key: mapConfigure.amapKey,  version: "2.0",  AMapUI: {    version: "1.1",    plugins: ["misc/PositionPicker"]  // 需要加载的 AMapUI ui插件  }})

创建 PositionPicker 实例

let positionPicker = new AMapUI.PositionPicker({  mode: "dragMap", // 设定为拖拽地图模式,可选'dragMap'、'dragMarker',默认为'dragMap'  map: map // 依赖地图对象});

绑定事件处理函数,获取选址列表(默认展示30条数据)

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>0

开启拖拽选址

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>1

渲染选址列表

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>2

注意:如果控制台报错:INVALID_USER_SCODE,只需要添加一下高德安全密钥,安全密钥是和 key 一起申请的

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>3

5. 使用 PoiPicker 实现搜索功能

PoiPicker(POI选点)在给定的输入框上集成输入提示和关键字搜索功能,方便用户选取特定地点(即POI)。

加载 PoiPicker(模块名:ui/misc/PoiPicker

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>4

创建 PoiPicker 实例,参考官方文档:https://developer.amap.com/api/amap-ui/reference-amap-ui/other/poipicker

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>5

渲染搜索框组件

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>6

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>7

7. 完整代码

<script setup lang="ts">import AMapLoader from "@amap/amap-jsapi-loader";import { getCurrentInstance, onBeforeMount, onUnmounted, ref } from "vue";interface MapConfigOption {  resizeEnable?: boolean;  center?: number[];  zoom?: number;}interface MapConfigure {  amapKey: string;  options: MapConfigOption;}interface MapConfigureInter {  on?: Fn;  destroy?: Fn;  clearEvents?: Fn;  addControl?: Fn;  getCenter?: Fn;  setCenter?: Fn;  setZoom?: Fn;  plugin?: Fn;}let map;const mapConfigure = {  amapKey: "key",  options: {    resizeEnable: true,    // center: [121.553958, 29.869472],    zoom: 16  }};window._AMapSecurityConfig = {  securityJsCode: "安全密钥"};const instance = getCurrentInstance();const addressList = ref<any[]>([]);const sureAddress = (data: any) => {  map.setCenter(data.location);  addressList.value = [];};onBeforeMount(() => {  if (!instance) return;  let { options } = mapConfigure;  AMapLoader.load({    key: mapConfigure.amapKey,    version: "2.0",    plugins: [],    AMapUI: {      version: "1.1",      plugins: ["misc/PositionPicker", "misc/PoiPicker"]    }  })    .then(AMap => {      map = new AMap.Map(instance.refs.mapView, options);      map.plugin(["AMap.Geolocation"], () => {        let geolocation = new AMap.Geolocation({          enableHighAccuracy: true,          timeout: 10000,          buttonOffset: new AMap.Pixel(10, 20),          zoomToAccuracy: true,          buttonPosition: "RB"        });        map.addControl(geolocation);        geolocation.getCurrentPosition(function (status: any, result: unknown) {          if (status == "complete") {            onComplete(result);          } else {            onError(result);          }        });      });      function onComplete(data: any) {        map.setCenter(data.position);      }      function onError(error: unknown) {        console.log("error", error);      }      let positionPicker = new AMapUI.PositionPicker({        mode: "dragMap",        map: map      });      positionPicker.on("success", function (positionResult: any) {        addressList.value = positionResult.regeocode.pois;      });      positionPicker.on("fail", function (positionResult: any) {        console.log("positionResult", positionResult);      });      <iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>1      let poiPicker = new AMapUI.PoiPicker({        input: instance.refs.pickerInput      });      poiPicker.on("poiPicked", function (poiResult: any) {        map.setCenter(poiResult.item.location);      });    })    .catch(() => {      throw "地图加载失败,请重新加载";    });});onUnmounted(() => {  if (map) {    // 销毁地图实例    map.destroy() && map.clearEvents("click");  }});</script>(function(){    var iframe = document.getElementById('test').contentWindow;    document.getElementById('test').onload = function(){      iframe.postMessage('hello','https://m.amap.com/picker/');    };    window.addEventListener("message", function(e){      alert('您选择了:' + e.data.name + ',' + e.data.location)    }, false);}())2<style lang="scss" scoped>.map-container {  background-color: #fff;  width: 100%;  height: 100vh;  .map-view {    position: relative;    width: 100%;    height: 50vh;    position: fixed !important;    top: 0;  }  input {    width: 100%;    height: 47px;    padding: 8px 15px;    position: fixed;    z-index: 10;    top: 0;    outline: none;    border: 1px solid #d3d3d3;    border-radius: 5px;    background-color: #fff;  }  .address-wrapper {    padding-top: 50vh;    .address-list {      position: relative;      height: 50vh;      overflow: auto;      &-item {        font-size: 12px;        padding: 6px 12px;        border-bottom: 1px solid #e8e8e8;        p:first-child {          color: #333;          font-size: 13px;        }        p:last-child {          color: #666;        }      }    }  }}</style>

引入原生的高德 api

接下来使用原生的高德地图 api + vue2 也来浅试一下封装一个选址组件,代码其实也大同小异。

1. 初始化地图

在 components 下创建 Amap 组件,使用 \<script> 标签导入高德地图 api。

<iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>9

2. 使用 Geolocation 实现定位

(function(){    var iframe = document.getElementById('test').contentWindow;    document.getElementById('test').onload = function(){      iframe.postMessage('hello','https://m.amap.com/picker/');    };    window.addEventListener("message", function(e){      alert('您选择了:' + e.data.name + ',' + e.data.location)    }, false);}())0

3. 使用 PoiPicker 和 PositionPicker

initMap() {  map = new AMap.Map(this.$refs.mapView, {    resizeEnable: true,    zoom: 16,  });  this.loadPositionPicker();  this.loadPoiPicker();  this.getLocation();},loadPositionPicker() {  let that = this;  AMapUI.loadUI(["misc/PositionPicker"], function (PositionPicker) {    let positionPicker = new PositionPicker({      mode: "dragMap",      map: map,    });    positionPicker.on("success", function (positionResult) {      console.log("success", positionResult);      that.addressList = positionResult.regeocode.pois;    });    positionPicker.on("fail", function (positionResult) {      console.log("fail", positionResult);    });    <iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>1  });},loadPoiPicker() {  let that = this;  AMapUI.loadUI(["misc/PoiPicker"], function (PoiPicker) {    let poiPicker = new PoiPicker({      input: that.$refs.pickerInput,    });    poiPicker.on("poiPicked", function (poiResult) {      map.setCenter(poiResult.item.location);    });  });},sureAddress(data) {  map.setCenter(data.location);  this.addressList = [];},

(function(){    var iframe = document.getElementById('test').contentWindow;    document.getElementById('test').onload = function(){      iframe.postMessage('hello','https://m.amap.com/picker/');    };    window.addEventListener("message", function(e){      alert('您选择了:' + e.data.name + ',' + e.data.location)    }, false);}())2

4. 完整代码

(function(){    var iframe = document.getElementById('test').contentWindow;    document.getElementById('test').onload = function(){      iframe.postMessage('hello','https://m.amap.com/picker/');    };    window.addEventListener("message", function(e){      alert('您选择了:' + e.data.name + ',' + e.data.location)    }, false);}())2<script>let map = null;export default {  name: "map-view",  data() {    return {      addressList: [],    };  },  mounted() {    const amap_key = '', cb = "amap_callback";    const scriptUrl = `https://webapi.amap.com/maps?v=1.4.18&key=${amap_key}&callback=${cb}`;    const mapuiUrl = "https://webapi.amap.com/ui/1.0/main.js?v=1.0.11";    // 导入script    importScript(scriptUrl);    importScript(mapuiUrl);    window[cb] = () => {      this.initMap();    };  },  methods: {    initMap() {      map = new AMap.Map(this.$refs.mapView, {        resizeEnable: true,        zoom: 15,        // center: [121.553958, 29.869472],      });      this.loadPositionPicker();      this.loadPoiPicker();      this.getLocation();    },    loadPositionPicker() {      let that = this;      AMapUI.loadUI(["misc/PositionPicker"], function (PositionPicker) {        let positionPicker = new PositionPicker({          mode: "dragMap",          map: map,        });        positionPicker.on("success", function (positionResult) {          console.log("success", positionResult);          that.addressList = positionResult.regeocode.pois;        });        positionPicker.on("fail", function (positionResult) {          console.log("fail", positionResult);        });        <iframe  id="mapView"  width="100%"  height="100%"  src="https://m.amap.com/picker/?key=your key"></iframe>1      });    },    loadPoiPicker() {      let that = this;      AMapUI.loadUI(["misc/PoiPicker"], function (PoiPicker) {        let poiPicker = new PoiPicker({          input: that.$refs.pickerInput,        });        poiPicker.on("poiPicked", function (poiResult) {          map.setCenter(poiResult.item.location);        });      });    },    getLocation() {      map.plugin("AMap.Geolocation", function () {        let geolocation = new AMap.Geolocation({          enableHighAccuracy: true,          timeout: 10000,          buttonOffset: new AMap.Pixel(10, 20),          zoomToAccuracy: true,          buttonPosition: "RB",        });        map.addControl(geolocation);        geolocation.getCurrentPosition(function (status, result) {          if (status == "complete") {            onComplete(result);          } else {            onError(result);          }        });      });      function onComplete(data) {        map.setCenter(data.position);      }      function onError(error) {        console.log("error", error);      }    },    sureAddress(data) {      map.setCenter(data.location);      this.addressList = [];    },  },};function importScript(sSrc, success) {  function loadError(err) {    throw new URIError("The script " + err.target.src + " is not accessible.");  }  var oScript = document.createElement("script");  oScript.type = "text\/javascript";  oScript.onerror = loadError;  if (success) oScript.onload = success;  document.body.appendChild(oScript);  oScript.src = sSrc;}</script><style lang="scss" scoped>.map-container {  background-color: #fff;  width: 100%;  height: 100vh;  .map-view {    position: relative;    width: 100%;    height: 50vh;    position: fixed !important;    top: 0;  }  input {    width: 100%;    height: 47px;    padding: 8px 15px;    position: fixed;    z-index: 10;    top: 0;    outline: none;    border: 1px solid #d3d3d3;    border-radius: 5px;    background-color: #fff;  }  .address-wrapper {    padding-top: 50vh;    .address-list {      position: relative;      height: 50vh;      overflow: auto;      &-item {        font-size: 12px;        padding: 6px 12px;        border-bottom: 1px solid #e8e8e8;        p:first-child {          color: #333;          font-size: 13px;        }        p:last-child {          color: #666;        }      }    }  }}</style>

写在最后

其实地图组件的坑实在是太多了,还是需要继续学习不断的探索。

如果你也对地图组件有兴趣的话,欢迎大家一起来交流学习。

原文:https://juejin.cn/post/7099272711239073822


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Vue/3803.html