手把手教你封装高德地图组件

2023-11-05

背景

最近的一个项目中需要用到地图功能,经过一番调研,决定对于国内用户采用高德地图API,对于国外用户采用谷歌地图API。本期讲讲如何在vue项目中封装高德地图组件,下一期讲述如何封装谷歌地图组件。
本次组件所满足的大致需求是:

  1. 传入经纬度数据,转换为一个标记点显示在地图上;
  2. 鼠标点击地图组件将产生一个新的标记点,并且地图上只保留一个最新的标记点;
  3. 提供一个输入框,可模糊搜索地点。

效果如下:
在这里插入图片描述

前期准备

到高德开放平台注册一个开发者账号,登录后创建key。具体操作可以参考该连接:https://lbs.amap.com/api/javascript-api-v2/prerequisites

开发地图组件

1. Yarn/NPM 安装 Loader
yarn add @amap/amap-jsapi-loader 或者 npm i @amap/amap-jsapi-loader --save

2. 新建 CustomAmap.vue 文件
在项目中新建 CustomAmap.vue 文件,用作地图组件

3. 创建地图容器
CustomAmap.vue地图组件中创建<div>标签作为地图容器,并设置地图容器的id属性为container

<template>
    <div class="plug-custom-amap" :style="cssVars">
        <div class="map-container" id="container"></div>
    </div>
</template>

4. 设置地图容器样式
我通过变量的方式来控制地图的高度,这样就可以在父组件传递属性值来控制地图的高度了。

<style lang="less" scoped>
.plug-custom-amap {
    position: relative;
    .map-container {
        padding:0;
        margin: 0;
        width: 100%;
        height: var(--mapHeight);
    }
}
</style>

5. 引入JS API Loader
在地图组件 CustomAmap.vue 中引入 amap-jsapi-loader

import AMapLoader from '@amap/amap-jsapi-loader'

6. 初始化map对象
注意:声明map对象时,在vue2和vue3中是有区别的,本项目用的是vue2。在vue2中使用,在 data 函数中不声明map对象,可以直接使用this.map赋值或者采用非响应式的普通对象来存储。

7. 创建地图
created钩子函数中使用安全密钥(申请的时候会有一个key和一个安全密钥,注意区分),官方介绍了两种使用方式,一种是通过代理服务器转发(安全,推荐在生产环境中使用),一种是明文方式设置(不安全,推荐在开发环境中使用)。

生产环境中:
根据高德地图开发的建议,自定义一个代理服务器。https://lbs.amap.com/api/jsapi-v2/guide/abc/prepare

created() {
    window._AMapSecurityConfig = {
        serviceHost: '代理服务器域名或地址/_AMapService'
    }
}

开发环境中:

created() {
    window._AMapSecurityConfig = {
        securityJsCode:'「申请的安全密钥」'
    }
}

地图初始化函数initMap

mounted() {
    this.initMap()
}
/* 初始化地图实例 */
methods:{
    initMap() {
        AMapLoader.load({
            key: '申请的key',  // 申请好的Web端开发者Key,首次调用 load 时必填
            version:"2.0",  // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
            plugins:['AMap.Scale', 'AMap.ToolBar'],  // 需要使用的的插件列表,如比例尺'AMap.Scale'等
        }).then((AMap)=>{
            this.map = new AMap.Map("container",{  // 设置地图容器id
                viewMode:"3D",  // 是否为3D地图模式
                zoom: 11,  // 初始化地图级别
                resizeEnable: true
            })
            const scale = new AMap.Scale()
            const toolBar = new AMap.ToolBar()
            this.map.addControl(scale) // 添加比例尺
            this.map.addControl(toolBar) // 添加简单的缩放按钮
        }).catch(e=>{
            console.log(e)
        })
    }
}

至此,一个基本的地图组件就成型了。

8. 功能1:传入经纬度数据,转换为一个标记点显示在地图上

/* 初始化标记点 */
initMarker() {
    const marker = new AMap.Marker({
        icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
        position: [this.longitude, this.latitude]
        offset: new AMap.Pixel(-13, -30)
    })
    marker.setMap(this.map) // 将标记点放置到地图实例上
    this.map.setCenter([this.longitude, this.latitude], true) // 设置地图中心点
}

9. 功能2:鼠标点击地图组件将产生一个新的标记点,并且地图上只保留一个最新的标记点

/* 初始化左键添加标记事件 */
initAddMarker() {
    this.map.on('click', (e) => {
        this.map.clearMap() // 清理地图上的标记点
        const longitude = e.lnglat.getLng()
        const latitude = e.lnglat.getLat()
        const marker = new AMap.Marker({
            icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
            position: [longitude, latitude],
            offset: new AMap.Pixel(-13, -30)
        })
        marker.setMap(this.map) // 设置最新的标记点
        this.newMarker = [longitude, latitude] // 将最新标记点保存一下,用于点击确定后,传递给父组件
    })
},

10. 功能3:提供一个输入框,可模糊搜索地点

<template>
    <div class="plug-custom-amap" :style="cssVars">
        <div class="map-container" id="container"></div>
        <div class="select-container">
            <input class="pac-input" id="pacInput" ref="pacInput" type="text" placeholder="请输入关键字" />
            <v-btn class="ml-2" small depressed @click="clear">清除</v-btn>
        </div>
    </div>
</template>
/* 搜索框:输入提示后查询 */
initSearchPlace() {
    const autoOptions = { input: 'pacInput' }
    const autoComplete = new AMap.AutoComplete(autoOptions)
    const placeSearch = new AMap.PlaceSearch({ map: this.map }) // 构造地点查询类
    autoComplete.on("select", function (e) {
        placeSearch.setCity(e.poi.adcode)
        placeSearch.search(e.poi.name)  //关键字查询
    }) // 注册监听,当选中某条记录时会触发
}

贴上完整代码(仅供参考)

<template>
    <div class="plug-custom-amap" :style="cssVars">
        <div class="map-container" id="container"></div>
        <div class="select-container" v-if="placeSearchAble">
            <input class="pac-input" id="pacInput" ref="pacInput" type="text" placeholder="请输入关键字" />
            <v-btn class="ml-2" small depressed @click="clear">清除</v-btn>
        </div>
    </div>
</template>

<script>
import AMapLoader from '@amap/amap-jsapi-loader'

export default {
    name: "PlugCustomAmap",
    props: {
        height: { // 地图高度
            type: String,
            default: '500px'
        },
        placeSearchAble: { // 地点搜索框
            type: Boolean,
            default: false
        },
        addMarkerAble: { // 允许标记
            type: Boolean,
            default: false
        },
        zoom: { // 地图缩放级别
            type: Number,
            default: 11
        },
        latitude: { // 纬度
            type: Number,
            default: 0
        },
        longitude: { // 经度
            type: Number,
            default: 0
        }
    },
    data() {
        return {
          newMarker: [this.longitude, this.latitude] // 新标记点
        }
    },
    computed: {
        cssVars() {
            return {
                '--mapHeight': this.height // 地图高度
            }
        },
        /* 高德地图的key */
        mapKey() {
            return 'xxxxx' // 我的项目中在首次加载时调用接口获取的地图的key,然后存入了vuex,然后在这调用
        }
    },
	created() {
	    window._AMapSecurityConfig = {
	        serviceHost: '代理服务器域名或地址/_AMapService'
	    }
	},
    mounted() {
        this.initMap()
    },
    beforeDestroy() {
        this.map.destroy()
        this.map = null
    },
    methods:{
        /* 初始化标记点 */
        initMarker() {
            const marker = new AMap.Marker({
                icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
                position: [this.longitude, this.latitude],
                offset: new AMap.Pixel(-13, -30)
            })
            marker.setMap(this.map) // 将标记点放置到地图实例上
            this.map.setCenter([this.longitude, this.latitude], true) // 设置地图中心点
        },
        /* 初始化左键添加标记事件 */
        initAddMarker() {
            this.map.on('click', (e) => {
                this.map.clearMap() // 清理地图上的标记点
                const longitude = e.lnglat.getLng()
                const latitude = e.lnglat.getLat()
                const marker = new AMap.Marker({
                    icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
                    position: [longitude, latitude],
                    offset: new AMap.Pixel(-13, -30)
                })
                marker.setMap(this.map) // 设置最新的标记点
                this.newMarker = [longitude, latitude] // 将最新标记点保存一下,用于点击确定后,传递给父组件
            })
        },
        /* 搜索框:输入提示后查询 */
        initSearchPlace() {
            const autoOptions = { input: 'pacInput' }
            const autoComplete = new AMap.AutoComplete(autoOptions)
            const placeSearch = new AMap.PlaceSearch({ map: this.map }) // 构造地点查询类
            autoComplete.on("select", function (e) {
                placeSearch.setCity(e.poi.adcode)
                placeSearch.search(e.poi.name)  //关键字查询
            }) // 注册监听,当选中某条记录时会触发
        },
        /* 初始化地图实例 */
        initMap() {
            AMapLoader.load({
                key:this.mapKey,  // 申请好的Web端开发者Key,首次调用 load 时必填
                version:"2.0",  // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
                plugins:['AMap.Scale', 'AMap.AutoComplete', 'AMap.PlaceSearch', 'AMap.ToolBar'],       // 需要使用的的插件列表,如比例尺'AMap.Scale'等
            }).then((AMap)=>{
                this.map = new AMap.Map("container",{  //设置地图容器id
                    viewMode:"3D",    //是否为3D地图模式
                    zoom: this.zoom,           //初始化地图级别
                    resizeEnable: true
                })
                const scale = new AMap.Scale()
                const toolBar = new AMap.ToolBar()
                this.map.addControl(scale) // 添加比例尺
                this.map.addControl(toolBar) // 添加简单的缩放按钮

                this.placeSearchAble && this.initSearchPlace() // 如果placeSearchAble为true,则初始化搜索框功能
                this.addMarkerAble && this.initAddMarker() // 如果addMarkerAble为true,则初始化左键添加标记功能
                const initMarkerAble = this.latitude > 0 && this.longitude > 0
                initMarkerAble && this.initMarker() // 如果props中的longitude、latitude属性有值,则初始化标记点
            }).catch(e=>{
                console.log(e)
            })
        },
        /* 点击确认按钮,将标记点的经纬度值传递给父组件 */
        confirm() {
            const longitude = this.newMarker[0]
            const latitude = this.newMarker[1]
            this.$emit('setLongitudeLatitude', { longitude, latitude })
        },
        /* 清除地图上的标记 */
        clear() {
            this.map && this.map.clearMap() // 清空标记
            this.$refs.pacInput.value = '' // 将输入框的关键字清空
            this.newMarker = [0, 0]
        }
    }
}
</script>

<style lang="less" scoped>
.plug-custom-amap {
    position: relative;
    .map-container {
        padding:0;
        margin: 0;
        width: 100%;
        height: var(--mapHeight);
    }
    .select-container {
        position: absolute;
        top: 5px;
        left: 10px;
        background: #fff none repeat scroll 0 0;
        border: 1px solid #ccc;
        margin: 10px auto;
        padding: 6px;
        font-size: 14px;
        .pac-input {
            border: 1px solid #ccc;
            width: 350px;
            padding-left: 5px;
        }
    }
    /deep/ .amap-icon img {
        width: 25px;
        height: 34px;
    }
    /deep/ .amap-menu-outer {
        padding-left: 0 !important;
    }
}
</style>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

手把手教你封装高德地图组件 的相关文章

随机推荐