高德地图(AMap)快速上手

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.7k人参与

该文章已生成可运行项目,

高德地图(AMap)快速上手

1. 依赖与类型支持

package.json:

{
  "dependencies": {
    "@amap/amap-jsapi-loader": "^1.0.1"
  }
}

安装命令:

yarn add @amap/amap-jsapi-loader
  • @amap/amap-jsapi-loader:官方推荐的 SDK 动态加载器,用于在运行时按需加载 AMap 命名空间与插件。

全局类型扩展(src/types/amap.d.ts):

/**
 * 全局类型声明(AMap / AMapUI / 安全配置)
 * 目的:为使用高德地图 JS SDK 的项目提供最小可用的类型提示,避免在引用全局变量时导致 TS 报错。
 */

declare global {
  interface Window {
    AMapUI?: any;

    AMap?: any;

    _AMapSecurityConfig?: { securityJsCode?: string };
  }

  const AMapUI: any;

  const AMap: any;

  const _AMapSecurityConfig: { securityJsCode: string };
}

export {};
  • Window.AMapUI:全局 AMapUI 命名空间,提供 UI 组件与工具;需在加载时配置 AMapUI 才可用。
  • Window.AMap:全局 AMap 命名空间,创建地图实例的入口;依赖环境变量 VITE_AMAP_KEY 成功加载。
  • Window._AMapSecurityConfig.securityJsCode:安全校验代码;如启用安全校验必须配置,建议通过环境变量注入。

2. 环境变量与安全配置

在项目 .env 中配置:

VITE_AMAP_KEY=高德Key
VITE_AMAP_SECURITY_JSCODE=高德安全密钥

注意:

  • 若启用安全校验,必须在加载 SDK 前设置正确的 securityJsCode。
  • 缺失或错误的 KEY 会导致 SDK 加载失败。

3. Hook 设计

核心导出:

  • initMap(containerId, options?) — 在指定容器内创建地图实例;自动完成 SDK 加载与能力检测。
  • destroyMap() — 解绑事件并释放地图实例,支持安全重复调用。

关键实现片段:

  1. 地图实例与初始化
// 地图实例引用(AMap.Map | null)
const mapInstance = shallowRef(null);

// 初始化并创建地图实例
const initMap = async (containerId: string, options?: any) => {
  await getAMap();

  const mapOptions = {
    viewMode: '2D',
    zoom: 7,
    ...options,
  };

  mapInstance.value = new AMap.Map(containerId, mapOptions);

  // 绑定自定义滚轮缩放
  customWheelZoom();

  // 浏览器 Canvas 能力检测(不支持时给出友好提示)
  if (!isCanvasSupported()) {
    MessagePlugin.warning('当前浏览器不支持 Canvas,可能影响地图/热力图显示');
  }
};
  1. SDK 动态加载与安全配置
const getAMap = async () => {
  // 注入安全密钥
  window._AMapSecurityConfig = {
    securityJsCode: import.meta.env.VITE_AMAP_SECURITY_JSCODE || '',
  };

  // 已存在则跳过
  if (window.AMap) return;

  // 校验KEY
  const key = import.meta.env.VITE_AMAP_KEY as string | undefined;
  if (!key) {
    console.error('缺少环境变量 VITE_AMAP_KEY,用于加载高德地图 SDK');
    throw new Error('VITE_AMAP_KEY 未配置');
  }
  await AMapLoader.load({
    key,
    version: '2.0',
    plugins: [],
    AMapUI: {
      version: '1.1',
      plugins: [],
    },
  });
};
  1. 资源销毁
const destroyMap = () => {
  if (!mapInstance.value) return;
  mapInstance.value.off('mousewheel', mouseWheelHandler);
  mapInstance.value.destroy();
  mapInstance.value = null;
};
  1. 可选事件 - 自定义滚轮缩放
const customWheelZoom = () => {
  mapInstance.value?.on('mousewheel', mouseWheelHandler);
};

const mouseWheelHandler = (e: any) => {
  // 单次缩放步长
  const WHEEL_STEP = 1;
  // 原始滚轮增量
  const deltaY = e?.originEvent?.deltaY;

  // 目标级别(取整)
  const deltaStep = +deltaY > 0 ? -WHEEL_STEP : WHEEL_STEP;
  const current = Math.round(mapInstance.value.getZoom() + deltaStep);

  // 应用缩放
  mapInstance.value.setZoom(current);
};
  1. 可选事件 - 浏览器 Canvas 支持检测
const isCanvasSupported = () => {
  const elem = document.createElement('canvas');
  return !!(elem.getContext && elem.getContext('2d'));
};

4. 使用建议与注意事项

  • 在组件 onMounted 中调用 initMap,并在 onBeforeUnmount 中调用 destroyMap,保证资源正确释放。
  • 传入的 containerId 对应的 DOM 必须已存在且可见,否则初始化可能失败。
  • 若需要额外插件,请在 AMapLoader.load 的 plugins 中补充;确保与 SDK 版本兼容。
  • 安全校验启用时,确保 securityJsCode 通过环境变量正确注入。

5. 故障排查

  • SDK 加载失败:检查网络、VITE_AMAP_KEY 与 securityJsCode 是否正确。
  • 热力图或图形不显示:浏览器可能不支持 Canvas,或显卡/驱动限制;请更换浏览器或开启硬件加速。

6. 完整 useAmap 与 vue 代码

import AMapLoader from '@amap/amap-jsapi-loader';
import { MessagePlugin } from 'tdesign-vue-next';
import { shallowRef } from 'vue';

/**
 * useAmap
 * 高德地图 Hook:负责 SDK 动态加载、地图实例的创建/销毁及事件绑定。
 *
 * 导出方法:
 * - initMap(containerId: string, options?: any): Promise<void>
 *   在指定容器内创建地图实例;自动完成 SDK 加载与能力检测。
 * - destroyMap(): void
 *   解绑事件并释放地图实例资源;多次调用安全。
 */
export default function useAmap() {
  // 地图实例
  const mapInstance = shallowRef(null);

  /**
   * 初始化并创建地图实例
   *
   * @param containerId 容器 ID
   * @param options 创建地图配置项
   */
  const initMap = async (containerId: string, options?: any): Promise<void> => {
    await getAMap();

    // 创建地图实例(默认配置可按需覆盖)
    const mapOptions = {
      viewMode: '2D',
      zoom: 7,
      zooms: [7, 20],
      showIndoorMap: false,
      ...options,
    };
    mapInstance.value = new AMap.Map(containerId, mapOptions);

    // 绑定自定义滚轮缩放
    customWheelZoom();

    // 浏览器 Canvas 能力检测(不支持时给出友好提示)
    if (!isCanvasSupported()) {
      MessagePlugin.warning('当前浏览器不支持 Canvas,可能影响地图/热力图显示');
    }
  };

  /**
   * 确保全局 window.AMap 存在;若不存在则按需动态加载 SDK
   */
  const getAMap = async () => {
    // 注入安全密钥
    window._AMapSecurityConfig = {
      securityJsCode: import.meta.env.VITE_AMAP_SECURITY_JSCODE || '',
    };

    // 已存在则跳过加载
    if (window.AMap) return;

    // 校验 KEY
    const key = import.meta.env.VITE_AMAP_KEY as string | undefined;
    if (!key) {
      console.error('缺少环境变量 VITE_AMAP_KEY,用于加载高德地图 SDK');
      throw new Error('VITE_AMAP_KEY 未配置');
    }

    // 动态加载 SDK(含常用插件与 AMapUI)
    try {
      await AMapLoader.load({
        key,
        version: '2.0',
        plugins: [],
        AMapUI: {
          version: '1.1',
          plugins: [],
        },
      });
    } catch (err) {
      console.error('高德地图 SDK 加载失败', err);
      throw err;
    }
  };

  /**
   * 浏览器 Canvas 能力检测
   * 返回:boolean(true 表示支持;false 表示不支持)
   */
  const isCanvasSupported = () => {
    const elem = document.createElement('canvas');
    return !!(elem.getContext && elem.getContext('2d'));
  };

  /**
   * 绑定自定义滚轮缩放事件
   * - 说明:使用原生滚轮事件的增量控制地图缩放级别,替代默认缩放行为。
   */
  const customWheelZoom = () => {
    mapInstance.value?.on('mousewheel', mouseWheelHandler);
  };

  /**
   * 地图滚轮缩放事件处理器
   *
   * - deltaY > 0 视为向下滚动(缩小);否则视为向上滚动(放大)。
   * - 每次滚动调整一个整数级别,避免小数缩放造成的抖动。
   */
  const mouseWheelHandler = (e: any) => {
    // 单次缩放步长
    const WHEEL_STEP = 1;
    // 原始滚轮增量
    const deltaY = e?.originEvent?.deltaY;

    // 目标级别(取整)
    const deltaStep = +deltaY > 0 ? -WHEEL_STEP : WHEEL_STEP;
    const current = Math.round(mapInstance.value.getZoom() + deltaStep);

    // 应用缩放
    mapInstance.value.setZoom(current);
  };

  /**
   * 销毁地图实例
   * 行为:
   * - 解绑自定义滚轮事件;
   * - 释放地图实例资源;
   */
  const destroyMap = () => {
    if (!mapInstance.value) return;

    mapInstance.value.off('mousewheel', mouseWheelHandler);

    mapInstance.value.destroy();
    mapInstance.value = null;
  };

  return {
    // 响应式引用
    mapInstance,
    // 方法
    initMap,
    destroyMap,
  };
}
<script setup lang="ts">
  import { onBeforeUnmount, onMounted, ref } from 'vue';
  import useAmap from './hooks/useAmap';

  const { mapInstance, initMap, destroyMap } = useAmap();

  onMounted(() => {
    initMap('map_container');
  });

  onBeforeUnmount(() => {
    // 组件卸载时销毁地图实例
    destroyMap();
  });
</script>

<template>
  <div class="view-wrapper">
    <!-- 地图 -->
    <div id="map_container" class="map-container"></div>
    <!-- 悬浮在地图上的大屏组件 -->
    <div id="screenId" class="screen-wrapper"></div>
  </div>
</template>

<style scoped lang="less">
  .view-wrapper {
    position: relative;

    .map-container {
      position: absolute;
      width: 100vw;
      height: 100vh;
    }
    .screen-wrapper {
      position: relative;
      pointer-events: none;
    }
  }
</style>
本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值