当前位置: 首页 > article >正文

使用Vue3+TS玩转高德地图

不知道大家在开发的时候,是否遇到过需要利用地图去展示一些地理位置信息,而又不知道从哪里下手,下面我将使用高德地图带大家完成地图相关内容的开发

内容主要有:

地图的初始化,

地图内置插件的使用(鹰眼,图层切换,放大缩小,比例尺,视图位置),

地点搜索(自定义,不使用内置组件,不方便),

搜索中心地点方圆公里内容并展示内容(可配置:1公里,2公里,n公里),

地图打点(自定义打点内容),

获取个人当前位置坐标,

自定义弹窗信息展示,

行驶路径的轨迹回放(包括开始,结束,暂停,继续)

1.地图的初始化(需要去高德地图申请开发者的秘钥,然后再进行开发,这里默认已经拥有了开发者秘钥)
//下包
npm i amap/amap-jsapi-loader
2.初始化地图(plugins其实就是和webpack一样的插件,我们引入可以实现多种不同的功能)
<template>
  <div id="Map"></div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import AMapLoader from "@amap/amap-jsapi-loader";
// 挂载秘钥
window._AMapSecurityConfig = {
  securityJsCode: "xxxxx",
};
const initMap = async () => {
  const AMap = await AMapLoader.load({
    key: "xxxxxx", //申请好的 Web 端开发者 Key,首次调用 load 时必填
    version: "2.0", //指定要加载的 JS API 的版本,缺省时默认为 1.4.15
    plugins: [
      "AMap.Scale",
      "AMap.ToolBar",
      "AMap.ControlBar",
      "AMap.HawkEye",
      "AMap.MapType",
      "AMap.PlaceSearch",
      "AMap.AutoComplete",
      "AMap.Geolocation",
      "AMap.CitySearch",
      "AMap.MarkerCluster",
      "AMap.MoveAnimation",
    ], //需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['AMap.Scale','...','...']
  });
  map = new AMap.Map("Map", {
    zoom: 17, //地图放大程度
    resizeEnable: true,
    viewMode: "3D", //设置地图模式
    center: [116.397428, 39.90923], // 初始化地图中心点位置
  });
};
onMounted(() => {
  initMap();
});
</script>
<style scoped lang="less">
#Map {
  width: 100%;
  height: 100%;
}
</style>

此时就可以看到一个地图在页面上显示了

下面可以进行深度开发,实现我描述的功能(这里直接贴全部代码,粘贴即用,这里我就不进行详细解析了,后续会在B站出一个视频,大家可以结合在这里慢慢看)

<template>
  <div id="Map"></div>
  <div class="control">
    <a-checkbox-group direction="vertical" @change="change" v-model="control">
      <a-checkbox value="1">比例尺</a-checkbox>
      <a-checkbox value="2">工具条</a-checkbox>
      <a-checkbox value="3">工具条方向盘</a-checkbox>
      <a-checkbox value="4">鹰眼</a-checkbox>
      <a-button type="primary" @click="getMyAddress"> 获取当前地址 </a-button>
      <div class="circleSize">
        <a-button type="primary" @click="updateRadius(1000)"> 1000 </a-button>
        <a-button type="primary" @click="updateRadius(2000)"> 2000 </a-button>
        <a-button type="primary" @click="updateRadius(3000)"> 3000 </a-button>
      </div>
      <div class="circleSize">
        <a-button type="primary" @click="beginTrace"> 开始轨迹 </a-button>
        <a-button type="primary" @click="pauseAnimation"> 暂停轨迹 </a-button>
        <a-button type="primary" @click="resumeAnimation"> 继续轨迹 </a-button>
        <a-button type="primary" @click="stopAnimation"> 停止轨迹 </a-button>
      </div>
    </a-checkbox-group>
  </div>
  <div class="search">
    <a-select v-model="location" @search="getAddressInfo" allow-search>
      <a-option
        v-for="item in addressinfo"
        :value="item.id"
        @click="goAddress(item)"
      >
        <img :src="item?.photos[0]?.url" alt="" class="imgs" />
        {{ `${item.cityname}-${item.address}-${item.name}` }}
      </a-option>
    </a-select>
  </div>

  <!-- <div id="panel"></div> -->
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
import { Message } from "@arco-design/web-vue";

import AMapLoader from "@amap/amap-jsapi-loader";
// 挂载秘钥
window._AMapSecurityConfig = {
  securityJsCode: "xxxxx",
};

const control = ref(["1", "2", "3", "4"]);
const location = ref("");
const addressinfo = ref<any>([]);
const change = (val: any) => {
  control.value = val;
  toggle();
};

const toggle = () => {
  const controls: any = {
    scale: { instance: scale.value, identifier: "1" },
    toolbar: { instance: toolbar.value, identifier: "2" },
    controlBar: { instance: controlBar.value, identifier: "3" },
    hawEye: { instance: hawEye.value, identifier: "4" },
  };

  for (const key in controls) {
    const controlInstance = controls[key].instance;
    const identifier = controls[key].identifier;
    if (control.value.includes(identifier)) {
      controlInstance.show();
    } else {
      controlInstance.hide();
    }
  }
};

// 自动聚集往中心点
const updateCenter = (newLocation) => {
  const lnglat = new AMapOutside.LngLat(newLocation.lng, newLocation.lat);
  map.setCenter(lnglat);
};

const addressCenter = ref<any>([]);
// 去往当前gps点位
const goAddress = (item) => {
  map.clearMap();
  const { photos, cityname, address, name, location } = item;
  addressCenter.value = [location.lng, location.lat];
  const markerContent = `<div class='casemark''>
    案发点位
    <img src="${photos[0]?.url}" style='width: 50px;
    height: 50px;'>
    <div>
     <div>${cityname}</div>
     <div>${address}</div>
     <div>${name}</div>
    </div>
  </div>`;

  const position = new AMapOutside.LngLat(location.lng, location.lat); //Marker 经纬度
  const marker = new AMapOutside.Marker({
    position: position,
    content: markerContent, //将 html 传给 content
    offset: new AMapOutside.Pixel(-13, -30), //以 icon 的 [center bottom] 为原点
  });
  map.add(marker);
  getCaseAddress();
  setCircle(1000);
  updateCenter(location);
};

// 获取周边信息
const getCaseAddress = () => {
  // const point = []
  addressinfo.value.forEach((item, index) => {
    const { photos, cityname, address, name, location } = item;
    const position = new AMapOutside.LngLat(location.lng, location.lat); //Marker 经纬度
    // 证据点位
    // point.push({weight: 8, lnglat: [location.lng, location.lat]})
    const marker = new AMapOutside.Marker({
      position: position,
      icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
      anchor: "bottom-center",
      offset: new AMapOutside.Pixel(0, 0),
    });
    marker.setMap(map);
    // 设置鼠标划过点标记显示的文字提示
    marker.setTitle("我是marker的title");
    // 设置label标签
    // label默认蓝框白底左上角显示,样式className为:amap-marker-label
    marker.setLabel({
      direction: "right",
      offset: new AMapOutside.Pixel(10, 0), //设置文本标注偏移量
      content: `<div class='info'>证据${index}${cityname}-${address}-${name}</div>`, //设置文本标注内容
    });

    const markerContent = `<div class="evi"'> 
      <img src="${photos[0]?.url}">
      <div class='eviMarks'>
       <div>证据点位</div>
       <div>${cityname}</div>
       <div>${address}</div>
       <div>${name}</div>
        </div>
       </div>`;
    const infoWindow = new AMapOutside.InfoWindow({
      position,
      content: markerContent,
      offset: new AMapOutside.Pixel(16, -45),
    });
    marker.on("click", () => {
      infoWindow.open(map);
    });
  });
  // MarkerCluster.setData(point);
};
// 搜索范围
const Circle = ref();
const updateRadius = (number) => {
  Circle.value.setRadius(number);
};
const setCircle = (radius = 20) => {
  if (addressCenter.value.length !== 2) {
    return;
  }
  Circle.value = new AMapOutside.Circle({
    center: addressCenter.value, //圆心
    radius: radius, //半径
    strokeColor: "white", //轮廓线颜色
    strokeWeight: 2, //轮廓线宽度
    strokeOpacity: 0.5, //轮廓线透明度
    fillColor: "rgba(0,0,255,1)", //圆点填充颜色
    fillOpacity: 0.5, //圆点填充透明度
    zIndex: 10, //圆点覆盖物的叠加顺序
    cursor: "pointer", //鼠标悬停时的鼠标样式
  });
  //圆形 Circle 对象添加到 Map
  map.add(Circle.value);
  map.setFitView();
};

const getAddressInfo = (val) => {
  placeSearch.value.search(val, (status, result) => {
    if (status !== "error") {
      addressinfo.value = result?.poiList?.pois;
    }
  });
};

const getMyAddress = () => {
  geolocation.value.getCurrentPosition(function (status, result) {
    Message.success(result);
  });
  // citySearch.value.getLocalCity(function (status, result) {
  //   if (status === "complete" && result.info === "OK") {
  //     // 查询成功,result即为当前所在城市信息
  //     console.log(result);
  //     Message.success(result.city);
  //   }
  // });
};

// 轨迹回放
const traceMarker = ref();
let lineArr = [
  [116.478935, 39.997761],
  [116.478939, 39.997825],
  [116.478912, 39.998549],
  [116.478912, 39.998549],
  [116.478998, 39.998555],
  [116.478998, 39.998555],
  [116.479282, 39.99856],
  [116.479658, 39.998528],
  [116.480151, 39.998453],
  [116.480784, 39.998302],
  [116.480784, 39.998302],
  [116.481149, 39.998184],
  [116.481573, 39.997997],
  [116.481863, 39.997846],
  [116.482072, 39.997718],
  [116.482362, 39.997718],
  [116.483633, 39.998935],
  [116.48367, 39.998968],
  [116.484648, 39.999861],
];
const TOTAL_DURATION = 20 * 1000; // 总播放时间为20秒,转换为毫秒
const SEGMENTS_COUNT = lineArr.length - 1; // 总段数(最后一个点不需要移动)
const beginTrace = () => {
  const durationPerSegment = Math.floor(TOTAL_DURATION / SEGMENTS_COUNT); // 每一段的平均时长
  traceMarker.value.moveAlong(lineArr, {
    duration: durationPerSegment, // 根据总时长和段数计算的时长
    autoRotation: true,
  });
};
// 
const pauseAnimation = () => {
  traceMarker.value.pauseMove();
}
const resumeAnimation = () => {
  traceMarker.value.resumeMove();
}
const stopAnimation = () => {
  traceMarker.value.stopMove();
}
const drawTrace = () => {
  traceMarker.value = new AMapOutside.Marker({
    map: map,
    position: lineArr[0],
    icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
    offset: new AMapOutside.Pixel(-13, -26),
  });
  // 绘制轨迹
  new AMapOutside.Polyline({
    map: map,
    path: lineArr,
    showDir: true,
    strokeColor: "#28F", //线颜色
    // strokeOpacity: 1,     //线透明度
    strokeWeight: 6, //线宽
    // strokeStyle: "solid"  //线样式
  });
  // 行走过的颜色和设置
  const passedPolyline = new AMapOutside.Polyline({
    map: map,
    strokeColor: "#AF5", //线颜色
    strokeWeight: 6, //线宽
  });
  traceMarker.value.on("moving", function (e) {
    passedPolyline.setPath(e.passedPath);
    map.setCenter(e.target.getPosition(), true);
  });
  map.setFitView();
}

let map: any = null;
let AMapOutside: any = null;
let MarkerCluster: any = null; //点聚合
const toolbar = ref();
const scale = ref();
const controlBar = ref();
const hawEye = ref();
const placeSearch = ref();
const geolocation = ref();
const citySearch = ref();

const initMap = async () => {
  const AMap = await AMapLoader.load({
    key: "xxxxx", //申请好的 Web 端开发者 Key,首次调用 load 时必填
    version: "2.0", //指定要加载的 JS API 的版本,缺省时默认为 1.4.15
    plugins: [
      "AMap.Scale",
      "AMap.ToolBar",
      "AMap.ControlBar",
      "AMap.HawkEye",
      "AMap.MapType",
      "AMap.PlaceSearch",
      "AMap.AutoComplete",
      "AMap.Geolocation",
      "AMap.CitySearch",
      "AMap.MarkerCluster",
      "AMap.MoveAnimation",
    ], //需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['AMap.Scale','...','...']
  });
  AMapOutside = AMap;
  map = new AMap.Map("Map", {
    zoom: 17,
    resizeEnable: true,
    viewMode: "3D", //设置地图模式
    center: [116.397428, 39.90923], // 初始化地图中心点位置
  });
  drawTrace()
  // 图层切换
  const mapType = new AMap.MapType();
  map.addControl(mapType); // 图层切换

  toolbar.value = new AMap.ToolBar({
    position: {
      bottom: "110px",
      left: "40px",
    },
  }); //放大缩小插件
  map.addControl(toolbar.value);

  scale.value = new AMap.Scale(); //比例尺
  map.addControl(scale.value);

  controlBar.value = new AMap.ControlBar({
    position: {
      bottom: "110px",
      left: "100px",
    },
  });
  map.addControl(controlBar.value); //视图位置

  hawEye.value = new AMap.HawkEye({
    opened: false,
  });
  map.addControl(hawEye.value); //鹰眼

  geolocation.value = new AMap.Geolocation({
    enableHighAccuracy: true, //是否使用高精度定位,默认:true
    maximumAge: 0, //定位结果缓存0毫秒,默认:0
    convert: true, //自动偏移坐标,偏移后的坐标为高德坐标,默认:true
    buttonPosition: "LT", //定位按钮停靠位置,默认:'LB',左下角
    buttonOffset: new AMap.Pixel(10, 20), //定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
    showMarker: true, //定位成功后在定位到的位置显示点标记,默认:true
    showCircle: true, //定位成功后用圆圈表示定位精度范围,默认:true
    panToLocation: true, //定位成功后将定位到的位置作为地图中心点,默认:true
    zoomToAccuracy: true, //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
  });
  map.addControl(geolocation.value); // 定位组件
  // 不那么精确的定位
  citySearch.value = new AMap.CitySearch();
  // 地址搜索
  placeSearch.value = new AMap.PlaceSearch({
    pageSize: 15, // 单页显示结果条数
    pageIndex: 1, // 页码
    city: "", // 兴趣点城市
    citylimit: false, //是否强制限制在设置的城市内搜索
    map: map, // 展现结果的地图实例
    // panel: "panel", // 结果列表将在此容器中进行展示。
    autoFitView: false, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见
  });
  MarkerCluster = new AMap.MarkerCluster(map, [], {
    gridSize: 20, // 聚合网格大小
    maxClusterRadius: 50, // 聚合半径
    zoomToClusterThreshold: 12, // 缩放到多少层级时开始聚合
  });
};

onMounted(() => {
  initMap();
});
</script>
<style scoped lang="less">
#Map {
  width: 100%;
  height: 100%;
}
.control {
  top: 0;
  position: absolute;
}
.mapType {
  top: 0;
  right: 200px;
  position: absolute;
}
.search {
  top: 10px;
  left: 300px;
  position: absolute;
  width: 500px;
  :deep(.arco-select-view-single) {
    background-color: #fff;
  }
}
:deep(.arco-select-option-content) {
  display: flex;
  align-items: center;
}
.imgs {
  width: 50px;
  height: 50px;
}
#panel {
  top: 10px;
  position: absolute;
  right: 50px;
}
</style>


http://www.kler.cn/a/304306.html

相关文章:

  • 35.3K+ Star!PhotoPrism:一款基于AI的开源照片管理工具
  • 2024年11月10日系统架构设计师考试题目回顾
  • 国标GB28181视频平台EasyCVR私有化部署视频平台对接监控录像机NVR时,录像机“资源不足”是什么原因?
  • 爬虫如何解决短效代理被封的问题?
  • SwiftUI开发教程系列 - 第1章:简介与环境配置
  • Flume学习
  • 前端表单验证的常见问题与解决方案
  • helm一键化部署pod
  • 基于Linux和C++实现的RabbitMQ风格消息队列:设计与实现
  • 什么是智慧箱变动环辅控系统?箱式变电站动环监控@卓振思众
  • SpringBoot 项目 Jar 包加密,防止反编译
  • CCF推荐A类会议和期刊总结:计算机体系结构/并行与分布计算/存储系统领域
  • 【Oracle APEX开发小技巧 8 】图片回显及多图片URL在页面回显点击放大
  • 性能测试-jmeter的控制器(十六)
  • 【Linux】多线程:线程互斥、互斥锁
  • Leetcode 寻找重复数
  • 提升工作效率:寻找编程工具的秘密武器
  • 使用Docker快速安装和运行Elasticsearch
  • 内蒙古优质农畜产品天津推介会成功举办
  • 【C++学习笔记】逻辑判断语句与循环语句(二)
  • 算法41:位1的个数
  • 赎金信--力扣383
  • 『功能项目』战士的伤害型技能【45】
  • ubuntu安装containerd,取代docker
  • Java面试题——第七篇(Java Web)
  • Redis 篇-深入了解基于 Redis 实现消息队列(比较基于 List 实现消息队列、基于 PubSub 发布订阅模型之间的区别)