useAMap.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. import AMapLoader from '@amap/amap-jsapi-loader';
  2. import {onMounted} from "vue";
  3. export function useAMap(options) {
  4. let AMap, map, nowLayer, labelsLayer, scale, cluster;
  5. const markers = {
  6. point: []
  7. };
  8. let clickMarker = null;
  9. let addPoints = [];
  10. // 初始化事件
  11. const initMap = (options) => {
  12. AMapLoader.load({
  13. key: options.key, // 申请好的Web端开发者Key,首次调用 load 时必填
  14. version: !!options.version ? options.version : '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
  15. plugins: options.plugins
  16. ? options.plugins
  17. : ['AMap.Scale', 'AMap.RangingTool', 'AMap.MouseTool', 'AMap.PolygonEditor', 'AMap.MarkerCluster', 'AMap.DistrictSearch', 'AMap.MoveAnimation']
  18. }).then((res) => {
  19. AMap = res;
  20. map = new AMap.Map(options.el ? options.el : 'aMap', {
  21. WebGLParams: {
  22. preserveDrawingBuffer: true
  23. },
  24. // 是否为3D地图模式
  25. viewMode: '3D',
  26. pitch: options.pitch,
  27. // 初始化地图级别
  28. zoom: options.zoom ? options.zoom : 11,
  29. // 初始化地图中心点位置
  30. center: options.center,
  31. // 是否可拖拽
  32. dragEnable: options.dragEnable,
  33. // 是否允许通过鼠标滚轮来缩放
  34. scrollWheel: options.scrollWheel
  35. });
  36. // 初始化比例尺
  37. if (options.showScale) {
  38. scale = new AMap.Scale();
  39. map.addControl(scale);
  40. }
  41. if (typeof options.onLoadCompleted === 'function') {
  42. options.onLoadCompleted(AMap, map);
  43. }
  44. });
  45. };
  46. const getAMap = () => {
  47. return AMap;
  48. };
  49. const getMap = () => {
  50. return map;
  51. };
  52. const getScale = () => {
  53. return scale;
  54. };
  55. // 切换地图
  56. const switchMap = (type: string) => {
  57. if (type === 'vectorgraph') {
  58. map.removeLayer(nowLayer);
  59. } else if (type === 'satellite') {
  60. const satellite = new AMap.TileLayer.Satellite();
  61. map.addLayer(satellite);
  62. nowLayer = satellite;
  63. }
  64. };
  65. // 添加搜索的标记的
  66. const addSearchMarker = (item) => {
  67. map.setZoom(18);
  68. map.setCenter(item.lnglat);
  69. addMarker([item], true);
  70. options.onMarkerClick(item);
  71. };
  72. const addMarker = (points, isSearchItem?: boolean) => {
  73. clearMarker('point');
  74. addPoints = points;
  75. const count = points.length;
  76. const _renderClusterMarker = function (context) {
  77. // 聚合中点个数
  78. const clusterCount = context.count;
  79. const div = document.createElement('div');
  80. div.style.backgroundColor = 'rgba(78,179,211,.5)';
  81. const size = Math.round(25 + Math.pow(clusterCount / count, 1 / 5) * 20);
  82. div.style.width = div.style.height = size + 'px';
  83. div.style.border = 'solid 1px rgba(78,179,211,1)';
  84. div.style.borderRadius = size / 2 + 'px';
  85. div.innerHTML = context.count;
  86. div.style.lineHeight = size + 'px';
  87. div.style.color = '#ffffff';
  88. div.style.fontSize = '12px';
  89. div.style.textAlign = 'center';
  90. context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
  91. context.marker.setContent(div);
  92. context.marker.on('click', (e) => {
  93. const bounds = e.target.getBounds();
  94. map.setZoomAndCenter(map.getZoom() + 1, bounds.getCenter());
  95. });
  96. };
  97. const _renderMarker = function (context) {
  98. const content =
  99. '<div style="display: flex;flex-direction: column;align-items: center;justify-content: center">' +
  100. '<div style="background: url(' +
  101. context.data[0].icon +
  102. ') no-repeat; width: ' +
  103. context.data[0].size[0] +
  104. 'px;height: ' +
  105. context.data[0].size[1] +
  106. 'px;cursor: pointer; background-size: cover"></div>' +
  107. // '<div style="font-size: 36px;white-space: nowrap">'+ context.data[0].name +'</div>' +
  108. '</div>';
  109. const offset = new AMap.Pixel(-9, -9);
  110. context.marker.setContent(content);
  111. context.marker.setOffset(offset);
  112. context.marker.setExtData(context.data[0]);
  113. context.marker.on('click', function (e) {
  114. const extData = e.target.getExtData();
  115. let index = 0;
  116. let index2 = 0;
  117. for (let i = 0; i < addPoints.length; i++) {
  118. if (addPoints[i].id === extData.id && addPoints[i].imageHover) {
  119. addPoints[i].icon = addPoints[i].imageHover;
  120. e.target.setContent(content);
  121. index++;
  122. } else if (!!clickMarker) {
  123. const extData2 = clickMarker.getExtData();
  124. if (addPoints[i].id === extData2.id) {
  125. addPoints[i].icon = addPoints[i].image;
  126. clickMarker.setContent(content);
  127. index2++;
  128. }
  129. }
  130. if ((!!clickMarker && index === 1 && index2 === 1) || (!clickMarker && index === 1)) {
  131. break;
  132. }
  133. }
  134. addMarker(addPoints);
  135. clickMarker = e.target;
  136. options.onMarkerClick(extData);
  137. });
  138. };
  139. cluster = new AMap.MarkerCluster(
  140. map, //地图实例
  141. points, //海量点数据,数据中需包含经纬度信息字段 lnglat
  142. {
  143. gridSize: 30, //数据聚合计算时网格的像素大小
  144. renderClusterMarker: _renderClusterMarker, //上述步骤的自定义聚合点样式
  145. renderMarker: _renderMarker //上述步骤的自定义非聚合点样式
  146. }
  147. );
  148. points.forEach((item) => {
  149. markers['point'].push(item);
  150. });
  151. };
  152. // 清除所有标加
  153. const clearMarker = (id) => {
  154. if (!cluster || !markers[id]) return;
  155. cluster.setMap(null);
  156. markers[id] = [];
  157. };
  158. const handleHover = (extData, dataType) => {
  159. map.setZoom(18);
  160. map.setCenter([extData.lng, extData.lat]);
  161. setTimeout(() => {
  162. let index = 0;
  163. let index2 = 0;
  164. let data = {};
  165. for (let i = 0; i < addPoints.length; i++) {
  166. if (addPoints[i].id === extData.id.toString() && addPoints[i].dataType === dataType) {
  167. addPoints[i].icon = addPoints[i].imageHover;
  168. index++;
  169. data = addPoints[i];
  170. } else if (!!clickMarker) {
  171. const extData2 = clickMarker.getExtData();
  172. if (addPoints[i].id === extData2.id) {
  173. addPoints[i].icon = addPoints[i].image;
  174. index2++;
  175. }
  176. }
  177. if ((!!clickMarker && index === 1 && index2 === 1) || (!clickMarker && index === 1)) {
  178. break;
  179. }
  180. }
  181. addMarker(addPoints);
  182. options.onMarkerClick(data);
  183. }, 2000);
  184. };
  185. const getMarkers = () => {
  186. return markers;
  187. };
  188. // 显示信息框
  189. let infoWindow;
  190. const showInfo = (content, position, isCustom) => {
  191. hideInfo();
  192. // 实例化InfoWindow
  193. infoWindow = new AMap.InfoWindow({
  194. // 完全自定义
  195. isCustom: isCustom,
  196. autoMove: false,
  197. offset: new AMap.Pixel(0, -20) // 信息窗体的偏移量
  198. // 可以根据需要设置其他InfoWindow的属性
  199. });
  200. const lnglat = new AMap.LngLat(position[0], position[1]);
  201. // 打开InfoWindow,并设置其内容和位置
  202. infoWindow.setContent(content);
  203. infoWindow.open(map, lnglat);
  204. // 解决2.0版本无法滚动问题
  205. infoWindow.on('mouseover', () => map.setStatus({ zoomEnable: false }));
  206. infoWindow.on('mouseout', () => map.setStatus({ zoomEnable: true }));
  207. };
  208. const hideInfo = (e) => {
  209. map.setStatus({ zoomEnable: true });
  210. if (!!infoWindow) {
  211. infoWindow.close();
  212. if (!!clickMarker && e) {
  213. const extData = clickMarker.getExtData();
  214. for (let i = 0; i < addPoints.length; i++) {
  215. if (addPoints[i].id === extData.id) {
  216. addPoints[i].icon = addPoints[i].image;
  217. clickMarker = null;
  218. addMarker(addPoints);
  219. break;
  220. }
  221. }
  222. }
  223. }
  224. };
  225. const creatMask = (options, name = '茂名市') => {
  226. new AMap.DistrictSearch({
  227. extensions: 'all',
  228. subdistrict: 0
  229. }).search(name, function (status, result) {
  230. // 外多边形坐标数组和内多边形坐标数组
  231. const outer = [
  232. new AMap.LngLat(-360, 90, true),
  233. new AMap.LngLat(-360, -90, true),
  234. new AMap.LngLat(360, -90, true),
  235. new AMap.LngLat(360, 90, true)
  236. ];
  237. options.forEach((option) => {
  238. const holes = result.districtList[0].boundaries;
  239. // if (option.offset) {
  240. //
  241. // holes.forEach((items) => {
  242. //
  243. // })
  244. // }
  245. let pathArray = [outer];
  246. pathArray.push.apply(pathArray, holes);
  247. const polygon = new AMap.Polygon({
  248. pathL: pathArray,
  249. strokeColor: option.strokeColor ? option.strokeColor : '#268ab9',
  250. strokeOpacity: option.strokeOpacity ? option.strokeOpacity : 1,
  251. strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
  252. fillColor: option.fillColor ? option.fillColor : '#10243b',
  253. fillOpacity: option.fillOpacity ? option.fillOpacity : 0.65
  254. });
  255. polygon.setPath(pathArray);
  256. map.add(polygon);
  257. });
  258. });
  259. };
  260. let moveMarker, movePolyline, movePassedPolyline, timerId;
  261. const trackPlayback = (lineArr) => {
  262. if (timerId) {
  263. clearTimeout(timerId);
  264. }
  265. movePolyline?.remove();
  266. movePassedPolyline?.remove();
  267. moveMarker?.remove();
  268. moveMarker = new AMap.Marker({
  269. map: map,
  270. position: [116.478935, 39.997761],
  271. icon: 'https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png',
  272. offset: new AMap.Pixel(-13, -26)
  273. });
  274. // 绘制轨迹
  275. movePolyline = new AMap.Polyline({
  276. map: map,
  277. path: lineArr,
  278. showDir: true,
  279. strokeColor: '#28F', //线颜色
  280. // strokeOpacity: 1, //线透明度
  281. strokeWeight: 6 //线宽
  282. // strokeStyle: "solid" //线样式
  283. });
  284. movePassedPolyline = new AMap.Polyline({
  285. map: map,
  286. strokeColor: '#AF5', //线颜色
  287. strokeWeight: 6 //线宽
  288. });
  289. moveMarker.on('moving', function (e) {
  290. movePassedPolyline.setPath(e.passedPath);
  291. map.setCenter(e.target.getPosition(), true);
  292. });
  293. moveMarker.on('moveend', function (e) {
  294. timerId = setTimeout(() => {
  295. movePolyline.remove();
  296. movePassedPolyline.remove();
  297. moveMarker.remove();
  298. }, 2000);
  299. });
  300. moveMarker.moveAlong(lineArr, {
  301. // 每一段的时长
  302. duration: 1000, //可根据实际采集时间间隔设置
  303. // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
  304. autoRotation: true
  305. });
  306. };
  307. onMounted(() => {
  308. initMap(options);
  309. });
  310. return {
  311. getAMap,
  312. getMap,
  313. switchMap,
  314. addMarker,
  315. addSearchMarker,
  316. clearMarker,
  317. getMarkers,
  318. getScale,
  319. showInfo,
  320. hideInfo,
  321. handleHover,
  322. creatMask,
  323. trackPlayback
  324. };
  325. }