文章目录
- 前言
- 一、地图加载
- 1.本文准备环境
- 2.引入库
- 3.加载地图
- 4.加载地图控件
- 二、POI搜索
- 1.什么是poi搜索
- 2.如何使用
- 三、绘制点标记与信息窗体
- 1.场景描述
- 2.案例
- 3.信息窗体-链接路由跳转
- 4.进阶-通过Marker自动触发标记点(非鼠标手动点击)
- 四、jsApi地图事件
- 1.官方解释
- 2.用法
前言
本文主要由地图加载、POI检索、绘制点标记、信息窗体、信息窗体中链接的路由跳转、相关事件的使用几部分组成。
目前网上关于高德js API的文章大多数是vue2.0版本的,这篇文章是基于vue3.0的,我的写法不一定是主流写法,如果有更好的写法,请评论中指正,感谢🙏🙏🙏
一、地图加载
1.本文准备环境
- node 和 git - 项目开发环境
- pnpm - 速度快、节省磁盘空间的软件包管理器
- Vite
- Vue3
- TypeScript
- Es6+
- Vue-Route
- 高德开发者-获取key和安全密钥
- 高德JSApi手册
- 高德JSApi教程
- 高德JSApi示例
2.引入库
代码如下(示例):
npm i @amap/amap-jsapi-loader --save
3.加载地图
<!-- 创建地图容器,js api会根据id将地图加载到容器中 -->
<!-- 注意:需要设置地图容器的大小 -->
<template>
<div id="container"></div>
</template>
<component :is="'script'" type="text/javascript">
<!-- 使用明文设置安全密钥(不安全),https://lbs.amap.com/api/javascript-api-v2/guide/abc/jscode -->
window._AMapSecurityConfig = {securityJsCode: '准备环境阶段拿到的的安全密钥'}
</component>
<!-- 本文中使用script-setup,非setup需要自行替换写法 -->
<script lang="ts" setup>
// 引入jsApi 地图Loader
import AMapLoader from '@amap/amap-jsapi-loader';
let map = null;
onMounted(() => {
AMapLoader.load({
key: "", // 申请好的Web端开发者Key,首次调用 load 时必填
version: "2.0", // 指定要加载的 JSAPI 的版本
plugins: ["AMap.Scale", "AMap.ToolBar"], //(按需引用,pc端建议加入AMap.ToolBar,便于操作)需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['...','...']
})
.then((AMap) => {
// 加载地图到id为container的容器中
map = new AMap.Map("container", {
viewMode: "2D", // 设置地图视图类型,2D / 3D
zoom: 11, // 初始化地图级别
center: [116.397428, 39.90923], // 初始化地图中心点位置
});
})
.catch((e) => {
console.log(e);
});
});
onUnmounted(() => {
map?.destroy();
});
</script>
<style scoped>
// 设置地图容器的大小
#container{
padding:0px;
margin: 0px;
width: 100%;
height: 800px;
}
</style>
4.加载地图控件
<!-- 本文中使用script-setup,非setup需要自行替换写法 -->
<script lang="ts" setup>
// ··· 相关代码已省略
let map = null;
onMounted(() => {
AMapLoader.load({
key: "",
version: "2.0",
plugins: ["AMap.Scale", "AMap.ToolBar"], //(按需引用,pc端建议加入AMap.ToolBar,便于操作)需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['...','...']
})
.then((AMap) => {
// 加载地图到id为container的容器中
map = new AMap.Map("container", {
viewMode: "2D", // 设置地图视图类型,2D / 3D
zoom: 11, // 初始化地图级别
center: [116.397428, 39.90923], // 初始化地图中心点位置
});
// 地图加载后,可以加载地图控件。
// 如:创建工具条插件实例。需要在AMapLoader.plugins中引入控件,并map.addControl添加到地图中
var toolbar = new AMap.ToolBar();
map.addControl(toolbar);
})
.catch((e) => {
console.log(e);
});
});
onUnmounted(() => {
// 页面关闭后及时销毁资源
map?.destroy();
});
</script>
二、POI搜索
1.什么是poi搜索
poi检索是高德的地理信息查询服务,即通过关键字获取目标地理信息(如坐标,城市信息等)。其中包含输入提示插件AMap.AutoComplete和POI 搜索插件 AMap.PlaceSearch,如图显示,右上角通过JAC获取多个地点提示信息即输入提示插件,选择匹配信息后,POI 搜索插件会返回多个相关的地理信息,并在地图上添加标记点。
2.如何使用
<template>
<div id="container"></div>
<div style="position: absolute; top: 20px; left: 20px">
<!-- 用来触发输入提示插件的容器 -->
<input type="text" id="keyword" style="width: 300px; height: 32px; padding-left: 10px" placeholder="请输入地址" />
</div>
</template>
<!-- 本文中使用script-setup,非setup需要自行替换写法 -->
<script lang="ts" setup>
// ··· 相关代码已省略
let map = null;
onMounted(() => {
AMapLoader.load({
key: "",
version: "2.0",
})
.then((AMap) => {
// ···
// map = ···
// 输入提示,
var autoOptions = {
// 可选参数,你的搜索输入框dom的id(多数场景下只需要这一个参数即可)
input: 'keyword',
// 可选参数,你的搜索输出框dom的id,用来展示提示的结果(默认的只有图里的文字,如果需要图片等,需要指定output)
output: 'your_out_put_id',
// 是否强制指定城市,设定city后有效
citylimit: false,
// 输入提示时限定城市。可选值:城市名(中文或中文全拼)、citycode、adcode;默认值:“全国”
city: '北京',
// 默认为true,表示是否在input位于页面较下方的时候自动将输入面板显示在input上方以避免被遮挡
outPutDirAuto:true,
// ···其他参数请翻阅文档
};
AMap.plugin(['AMap.PlaceSearch', 'AMap.AutoComplete'], function () {
//无需再手动执行 search 方法,autoComplete 会根据传 input 对应的 DOM 动态触发 search
var autoComplete = new AMap.AutoComplete(autoOptions);
var placeSearch = new AMap.PlaceSearch({
map: map,
type: '', // 数据类别
pageIndex: 1,
pageSize: 10,
});
// 注册监听,当选中某条记录时会触发
autoComplete.on('select', select);
function select(e) {
// 获取到检索提示信息后,根据提示信息搜索精确地理位置
// 设置查询城市(防止查提示信息所在城市之外的城市)
placeSearch.setCity(e.poi.adcode);
// 检索地理信息
placeSearch.search(e.poi.name, function (status, result) {
// 获取查询到的结果
if (status == 'complete' ){
// 精确地理信息为多条,placeSearch会将地理位置全部添加为地图Marker,如果需要触发点击时获取指定Marker的地理信息,可以添加markerClick事件
console.log(result.poiList.pois);
placeSearch.on('markerClick', function(e) {
// e.data中即为poi信息(=result.poiList.pois[i])
console.log(e.data)
// 当前点击的地图的marker
console.log(e.marker)
})
}
});
}
});
})
.catch((e) => {
console.log(e);
});
});
onUnmounted(() => {
// 页面关闭后及时销毁资源
map?.destroy();
});
</script>
一些进阶功能:如指定区域内搜索,指定圆范围搜索,请翻阅高德JSApi教程
三、绘制点标记与信息窗体
1.场景描述
如需要在地图上展示营业网点位置,查看所有设备的定位位置等,需要根据定位物件的坐标,在地图上添加一个标记。
仅有标记仅仅只能看到所在位置,但是用户是看不到具体这个位置是哪个营业网点(或哪个设备),则需要添加信息窗体来展示当前定位的相关信息。
2.案例
注意:绘制点标记与信息窗体都需要指定锚点,具体锚点设置请翻阅高德JSApi教程
<template>
<div id="container"></div>
</template>
<!-- 本文中使用script-setup,非setup需要自行替换写法 -->
<script lang="ts" setup>
// ··· 相关代码已省略
let map = null;
onMounted(() => {
AMapLoader.load({
key: "",
version: "2.0",
})
.then((AMap) => {
// ···
// map = ···
// 通过mock数据or后端接口获取到的地理位置信息(如营业网点信息,设备定位信息)
const mapInfos = await getMapIfo();
// 信息窗体
var infoWindow = new AMap.InfoWindow({
anchor: 'bottom-center', // 信息窗体锚点
offset: new AMap.Pixel(0, -32), //设置点标记偏移量(设置锚点)
// 点击地图关闭
closeWhenClickMap: true,
// 是否自定义窗体(默认窗体即示例图中效果,底色与边框样式是固定的)
isCustom: false,
});
// 通用 icon(即示例图中Marker红色的样式)
const icon = new AMap.Icon({
size: new AMap.Size(25, 34), //图标尺寸
image: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png', //Icon 的图像
imageSize: new AMap.Size(25, 34), //根据所设置的大小拉伸或压缩图片
});
// 编辑mapInfos,绘制点标记
mapInfos.forEach((mapInfo) => {
// 拿不到完整的坐标信息,则不绘制
if (mapInfo.lng == 0 && mapInfo.lat == 0) {
return;
}
// 创建点标记对象
const marker = new AMap.Marker({
position: new AMap.LngLat(mapInfo.lng, mapInfo.lat), //经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9]
icon: icon,
offset: new AMap.Pixel(-12.5, -34), //设置点标记偏移量(锚点)
});
// 给点标记添加点击事件,点击Marker打开信息窗体
marker.on('click', function (e) {
// 点击时获取逆地理编码(由于后端/Mock中拿到的数据为lnglat坐标,用户更想看到的是具体地理位置信息,所以每次点击的时候获取逆地理编码)
AMap.plugin('AMap.Geocoder', async function () {
var geocoder = new AMap.Geocoder();
var lnglat = [mapInfo.lng, mapInfo.lat];
await geocoder.getAddress(lnglat, function (status, result) {
let region = '';
if (status === 'complete' && result.info === 'OK') {
// result为对应的地理位置详细信息
region = result.regeocode.formattedAddress;
}
//实例化信息窗体
const content =
`<div style="width: 500px;height: 130px;display: flex;justify-content:space-between;align-items:center;">` +
region +
`</div>`;
// 设置信息窗体的内容(dom)
infoWindow.setContent(content);
// 展示面板,将相关的信息加载到面板中
infoWindow.open(map, [mapInfo.lng, mapInfo.lat]);
});
});
});
// 把marker加载到map中
map.add(marker);
});
})
.catch((e) => {
console.log(e);
});
});
onUnmounted(() => {
// 页面关闭后及时销毁资源
map?.destroy();
});
</script>
3.信息窗体-链接路由跳转
上节案例中,可见content的写法是没办法使用vue路由的,vue2.0时还可以使用Vue.extend()去实现,但是vue3.0移除了这个方法,所以考虑在template中写好这个窗体,并加载到marker中,上案例:
<template>
<div id="container"></div>
<!-- vue渲染的自定义信息窗体,可以用到vue的特性 -->
<div ref="content" style="padding-bottom: 8px">
<div style="padding: 10px 10px 10px 10px;background: #fff;position: relative;line-height: 1.4;">
<div style="width: 500px;height: 130px;display: flex;align-items:center;">
{{contentParam.region}} <a v-if="contentParam.detailNo != ''" @click="toDetail(contentParam.detailNo)" >查看详情</a>-->
</div>
<div class="amap-info-sharp">
</div>
</div>
</div>
</template>
<!-- 本文中使用script-setup,非setup需要自行替换写法 -->
<script lang="ts" setup>
// 获取当前组件的实例对象
const {proxy} = getCurrentInstance();
const contentParam = reactive({
region: '',
})
// ··· 相关代码已省略
let map = null;
onMounted(() => {
AMapLoader.load({
key: "",
version: "2.0",
})
.then((AMap) => {
// ···
// map = ···
// 信息窗体
var infoWindow = new AMap.InfoWindow({
offset: new AMap.Pixel(0, -32), //设置点标记偏移量
// 点击地图关闭
closeWhenClickMap: true,
// 当前例子中,重新设计了info窗体,去掉了官方默认窗体的关闭按钮,所以需要开启自定义窗体
isCustom: true,
});
// mapInfos.forEach((mapInfo)
// const marker = ···
// 将页面当前组件实例中ref='content'的实例绑定到marker.content
marker.content = proxy.$refs.content;
marker.on('click', function (e) {
// 点击时获取逆地理编码
AMap.plugin('AMap.Geocoder', async function () {
// 信息框加载出来
var geocoder = new AMap.Geocoder();
var lnglat = [equipmentInfo.lng, equipmentInfo.lat];
await geocoder.getAddress(lnglat, function (status, result) {
let region = '';
if (status === 'complete' && result.info === 'OK') {
// result为对应的地理位置详细信息
region = result.regeocode.formattedAddress;
}
contentParam.region = region;
contentParam.detailNo = mapInfo.detailNo;
// content信息从marker.content中获取
infoWindow.setContent(e.target.content);
// 展示面板,将相关的信息加载到面板中
infoWindow.open(map, e.target.getPosition());
});
});
});
})
.catch((e) => {
console.log(e);
});
});
onUnmounted(() => {
// 页面关闭后及时销毁资源
map?.destroy();
});
function toDetail(detailNo) {
router.push({ path: '/detail', query: { detailNo: detailNo } });
}
</script>
<style scoped>
.amap-info-sharp, .amap-info-sharp:after {
margin-left: -8px;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
}
.amap-info-sharp {
bottom: 0;
left: 50%;
border-top: 8px solid #fff;
}
</style>
4.进阶-通过Marker自动触发标记点(非鼠标手动点击)
在第三节案例中,实现了点击窗体中的链接,通过路由跳转到其他vue页面。
如果打开页面的时候,携带了某个设备信息(or营业网点),想主动打开对应Marker的信息窗体,该怎么做?
以下为实现案例:
<template>
<div id="container"></div>
<!-- 假设场景为:地图展示的营业点/设备 想要可以通过下拉选直接搜索到,并打开他的信息窗体 -->
<!-- 场景是基于第三节案例中的代码,第三节中的代码以···省略 -->
<n-select :options="options" @update-value="selectDetail"/>
</template>
<!-- 本文中使用script-setup,非setup需要自行替换写法 -->
<script lang="ts" setup>
const options = ref([]);
// ··· 相关代码已省略
let map = null;
onMounted(() => {
AMapLoader.load({
key: "",
version: "2.0",
})
.then((AMap) => {
// ···
// map = ···
mapInfos.forEach((mapInfo) => {
// 拿不到完整的坐标信息,则不绘制
if (mapInfo.lng == 0 && mapInfo.lat == 0) {
return;
}
// 创建点标记对象
// const marker = ···
// 把marker加载到map中
options.value.push({ label: mapInfo.detailNo, value: marker });
map.add(marker);
});
})
.catch((e) => {
console.log(e);
});
});
onUnmounted(() => {
// 页面关闭后及时销毁资源
map?.destroy();
});
function selectDetail(marker) {
if(marker) {
// 主动触发marker的click事件
marker.emit('click',{
target: marker
});
}
}
</script>
<style scoped>
.amap-info-sharp, .amap-info-sharp:after {
margin-left: -8px;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
}
.amap-info-sharp {
bottom: 0;
left: 50%;
border-top: 8px solid #fff;
}
</style>
四、jsApi地图事件
1.官方解释
jsApi地图事件是对Map底图操作后触发的事件,事件回调中返回MapsEvent对象。该对象包含触发的对象目标、触发所在经纬度等信息。
2.用法
<script>
// 在 2.0 版本中所有类型的实例均使用 on/off 方法进行事件的绑定和移除
// 给地图绑定click事件
map.on("click", function (ev) {
//触发事件的对象
var target = ev.target;
//触发事件的地理坐标,AMap.LngLat 类型
var lnglat = ev.lnglat;
//触发事件的像素坐标,AMap.Pixel 类型
var pixel = ev.pixel;
//触发事件类型
var type = ev.type;
});
</script>
高德地图大部分实例都拥有不少事件,如前三章中用到了以下三种事件,来丰富我们的业务,让地图操作更合理。
<script>
// poi输入提示插件绑定select事件,选中某项提示后触发的事件
autoComplete.on('select', function(e) {});
// poi搜索插件绑定markerClick事件,点击地图中placeSearch生成的Marker触发的事件
placeSearch.on('markerClick', function(e) {});
// 为绘制的点标记绑定click事件,点击点标志触发的事件(我们用来打开信息窗体)
marker.on('click', function(e) {});
</script>
而有一些特殊的场景下,可能需要我们主动触发事件,则需要用到emit函数。如上章末位,主动触发Marker的click事件,来模拟点击Marker。
<script>
// 第一个参数为事件的类型, 第二个参数为事件回调时返回的数据
// 比如案例中要主动触发主动触发Marker的click事件,第一个参数即为click,第二个参数为marker本身,用来获取绑定在marker上的content的
marker.emit('click',{target: marker});
// 回顾一下marker的click事件的内容
marker.content = proxy.$refs.content;
marker.on('click', function (e) {
// ···
// 这里,e.target即对应第二个参数的target
infoWindow.setContent(e.target.content);
infoWindow.open(map, e.target.getPosition());
});
</script>
高德地图的事件,我们可以从高德JSApi手册中查找。