实现基本的地图打点和轨迹好像也不难

100 阅读3分钟

1. 注册百度账号

  • 略过

2. 账号和获取秘钥

  • 文档地址
  • 登录注册百度账号之后
  • 点击账号和获取密钥
  • 然后点击右上角 控制台 进入注册页
  • 分为个人开发者企业用户学生

3. 创建应用

  • 创建一个应用
  • 复制AK

4. 使用

<!doctype html>
<html lang="en" data-env="dev">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React后台</title>
  </head>
  <body>
    <div id="root"></div>
    <!--  运行轨迹动画库 -->
    <script src="//mapopen.bj.bcebos.com/github/BMapGLLib/TrackAnimation/src/TrackAnimation.min.js"></script>
    <!--  百度地图js基础库 -->
    <script type="text/javascript" src="//api.map.baidu.com/api?type=webgl&v=1.0&ak=自己的密钥"></script>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

5. React 使用

5.1. 功能一、地图打点

import { useImperativeHandle, useState } from 'react'
import { message, Modal } from 'antd'
import { IModalProp, OrderItem } from '@/types'
import { getOrderDetail, updateOrder } from '@/api'

type MarkerTypes = Array<{
  lng: string
  lat: string
  id: number
}>

const OrderMarker = (props: IModalProp) => {
  const [visible, setVisible] = useState(false)
  // 打点数据  最终要上报给接口的
  const [markerData, setMarkerData] = useState<MarkerTypes>([])
  const [markId, setMarkId] = useState('') // 标记点

  // 打开弹窗
  const open = async (data?: OrderItem) => {
    setVisible(true)
    setMarkId(data!.orderId)
    const markerData = await getOrderDetail(data!.orderId)
    renderMap(markerData)
  }

  // 创建marker
  const createMarker = (map: any, lng: string, lat: string) => {
    // 随机生成id 为了在删除时找到对应的marker 并删除
    const id = Math.random()
    markerData.push({ lng, lat, id })
    // 打点
    const marker = new window.BMapGL.Marker(new window.BMapGL.Point(lng, lat))
    marker.id = id

    // 创建右键菜单 取消已经打过的打点
    const markerMenu = new window.BMapGL.ContextMenu()
    markerMenu.addItem(
      new window.BMapGL.MenuItem('删除', function () {
        map.removeOverlay(marker)
        const index = markerData.findIndex(item => item.id === marker.id)
        markerData.splice(index, 1)
        setMarkerData([...markerData]) // 存储markerData
      })
    )
    setMarkerData([...markerData]) // 更新markerData
    marker.addContextMenu(markerMenu) //给标记添加右键菜单
    map.addOverlay(marker) // 添加marker到地图
  }

  // 渲染地图
  const renderMap = (data: OrderItem) => {
    const map = new window.BMapGL.Map('container') // 实例化地图
    map.centerAndZoom(data.cityName, 12) // 设置中心点 城市名称
    const zoomCtrl = new window.BMapGL.ZoomControl() // 添加缩放控件
    map.addControl(zoomCtrl)
    map.enableScrollWheelZoom(true) // 启用缩放

    // 初始化打点 循环打点 差不多是回显的意思
    // 上一次打完点之后你肯定是要回显的 所以需要把上一次的打点数据回显出来
    data.route.forEach(item => {
      createMarker(map, item.lng, item.lat)
    })

    // 地图绑定事件
    map.addEventListener('click', function (e: any) {
      // 点击地图创建marker
      // 经纬度latlng.lng    经度latlng.lat 纬度
      createMarker(map, e.latlng.lng, e.latlng.lat)
    })
  }

  //  暴露方法给父组件
  useImperativeHandle(props.markerRef, () => ({
    open
  }))

  // 确定事件
  const handleOk = async () => {
    await updateOrder({
      orderId: markId,
      route: markerData
    })
    message.success('打点成功')
    handleCancel()
  }

  // 取消事件
  const handleCancel = () => {
    setVisible(false)
    //  取消打点 清空markerData 防止下次打点时数据还在
    setMarkerData([])
  }

  return (
    <Modal
      title='地图打点'
      open={visible}
      okText='确定'
      width={1200}
      cancelText='取消'
      onOk={handleOk}
      onCancel={handleCancel}
    >
      <div id='container' style={{ height: 600 }} />
    </Modal>
  )
}
export default OrderMarker
 

5.2. 功能二、打点之后运行轨迹

  • 百度地图示例文档 代码基本可以直接 copy
  • 一定要引入动画库 切记切记!!!
  • 在上面 index.html 引入
import { useImperativeHandle, useState } from 'react'
import { message, Modal } from 'antd'
import { IModalProp, OrderItem } from '@/types'
import { getOrderDetail } from '@/api'

const MapTrajectory = (props: IModalProp) => {
  const [visible, setVisible] = useState(false)
  const [trackAni, setTrackAni] = useState<{ cancel?: () => void }>()

  const open = async (data?: OrderItem) => {
    const detail = await getOrderDetail(data!.orderId)
    if (detail.route.length > 0) {
      setVisible(true)
      setTimeout(() => {
        renderMap(detail)
      })
    } else {
      message.warning('请先进行打点操作')
    }
  }

  // 渲染轨迹
  const renderMap = (data: OrderItem) => {
    const map = new window.BMapGL.Map('mapTrajectory') // 实例化地图
    map.enableScrollWheelZoom(true) // 启用缩放
    map.centerAndZoom(data.cityName, 17) // 设置中心点城市名称

    const path = data.route || []
    let point = []
    for (var i = 0; i < path.length; i++) {
      point.push(new window.BMapGL.Point(path[i].lng, path[i].lat))
    }

    const polyline = new window.BMapGL.Polyline(point, {
      strokeColor: '#a170f2', // 线条颜色
      strokeWeight: 6, // 线条宽度
      strokeOpacity: 0.5 // 线条透明度
    })

    setTimeout(() => {
      start()
    }, 100)

    function start() {
      const trackAni = new window.BMapGLLib.TrackAnimation(map, polyline, {
        overallView: true,
        tilt: 30,
        duration: 20000,
        delay: 300
      })
      trackAni.start()
      setTrackAni(trackAni)
    }
  }

  useImperativeHandle(props.mapTrajectoryRef, () => ({
    open
  }))

  const handleOk = () => {
    trackAni?.cancel?.()
    setVisible(false)
  }

  const handleCancel = () => {
    // 关闭的时候取消动画
    trackAni?.cancel?.()
    setVisible(false)
  }

  return (
    <Modal
      title='运行轨迹'
      open={visible}
      okText='确定'
      width={1200}
      cancelText='取消'
      onOk={handleOk}
      onCancel={handleCancel}
    >
      <div id='mapTrajectory' style={{ height: 600 }} />
    </Modal>
  )
}

export default MapTrajectory

5.3. 效果展示

5.4. 问题

  1. 不存在类型怎么办
    • 解决 全局声明个类型

// 根目录下创建个global.d.ts
declare interface Window {
  BMapGL: {
    [propName: string]: any
  }
}


// tsconfig.app.json || tsconfig.json
// 在include里引入该声明文件
{
   // ....忽略
  "include": ["src", "typings.d.ts", "global.d.ts"]
}
  1. 调用百度 SDK 出现白名单问题
  2. 自己个学习使用白名单里面写个 http 就行了 最终上线的话就要配置真正的域名了