Skip to content

Latest commit

 

History

History
270 lines (202 loc) · 8.76 KB

README.md

File metadata and controls

270 lines (202 loc) · 8.76 KB

React-BMap

npm version License: MIT

用 React 组件写百度地图。地图、标注、路线、控件都是普通的 React 组件和 Hook,跟着 state 走,无需手动操作 DOM 或记 SDK 的命令式 API。一套代码同时支持百度地图 JSAPI 3.0(2D) 和 4.0(WebGL)。

<BMapProvider ak="您的密钥">
  <Map center={{ lng: 116.404, lat: 39.915 }} zoom={12}>
    <Marker position={{ lng: 116.404, lat: 39.915 }} />
  </Map>
</BMapProvider>

安装

npm install @baidumap/react-bmap

需要 React 18+(react / react-dom 为 peer 依赖)。

在 百度地图开放平台 申请一个浏览器端 ak 密钥即可,不用在 index.html 里手动加 <script>,组件库会自动加载地图脚本。

上手

1. 顶层放一个 Provider

BMapProvider 负责加载地图并把密钥、版本传下去,通常放在应用最外层,只写一次。

import { BMapProvider } from '@baidumap/react-bmap';

function Root() {
  return (
    <BMapProvider ak="您的密钥" version="4.0">
      <App />
    </BMapProvider>
  );
}
  • version="4.0"(默认):WebGL 三维地图,支持旋转、俯仰、3D。
  • version="3.0":传统 2D 地图,更轻量。

2. 放一张地图

Map 必须有确定的宽高,容器没高度会看不到地图。

import { Map } from '@baidumap/react-bmap';

<div style={{ width: '100%', height: 500 }}>
  <Map center={{ lng: 116.404, lat: 39.915 }} zoom={12} />
</div>

3. 往地图里塞东西

覆盖物、控件都作为 Map 的子元素:

import { Map, Marker, InfoWindow, NavigationControl, ScaleControl } from '@baidumap/react-bmap';

<Map center={{ lng: 116.404, lat: 39.915 }} zoom={12}>
  <Marker position={{ lng: 116.404, lat: 39.915 }} />
  <InfoWindow position={{ lng: 116.404, lat: 39.915 }} content="天安门" />
  <NavigationControl />
  <ScaleControl />
</Map>

常见用法

点击地图 / 标注

覆盖物的点击回调会拿到经纬度:

<Map
  center={{ lng: 116.404, lat: 39.915 }}
  zoom={12}
  onClick={(e) => console.log('点了地图', e.point)}
>
  <Marker
    position={{ lng: 116.404, lat: 39.915 }}
    onClick={(point) => console.log('点了标注', point)}
  />
</Map>

用 state 驱动标注

标注就是数据的映射,增删改直接改 state:

function Markers() {
  const [points, setPoints] = useState([{ lng: 116.404, lat: 39.915 }]);

  return (
    <Map
      center={{ lng: 116.404, lat: 39.915 }}
      zoom={12}
      onClick={(e) => setPoints((prev) => [...prev, e.point])}
    >
      {points.map((p, i) => (
        <Marker key={i} position={p} />
      ))}
    </Map>
  );
}

画线、画圆

<Map center={{ lng: 116.404, lat: 39.915 }} zoom={12}>
  <Polyline
    path={[
      { lng: 116.399, lat: 39.910 },
      { lng: 116.405, lat: 39.920 },
    ]}
    strokeColor="#3388ff"
    strokeWeight={4}
  />
  <Circle
    center={{ lng: 116.404, lat: 39.915 }}
    radius={800}
    fillColor="#3388ff"
    fillOpacity={0.3}
  />
</Map>

自定义标注图标

<Marker
  position={{ lng: 116.404, lat: 39.915 }}
  icon={{ url: '/pin.png', size: { width: 32, height: 32 } }}
/>

规划路线

检索类功能都以 Hook 提供,返回强类型结果,也可让它直接画到地图上:

import { Map, useDrivingRoute } from '@baidumap/react-bmap';

function RouteDemo() {
  const { search, data } = useDrivingRoute({ location: '北京' });

  return (
    <>
      <button
        onClick={() =>
          search({ lng: 116.404, lat: 39.915 }, { lng: 116.373, lat: 39.907 })
        }
      >
        驾车路线
      </button>
      <Map center={{ lng: 116.404, lat: 39.915 }} zoom={12} />
      {data && <p>共 {data.getNumPlans()} 条方案</p>}
    </>
  );
}

地址转坐标(地理编码)

import { useGeocoder } from '@baidumap/react-bmap';

const { getPoint, getLocation, data } = useGeocoder();
getPoint('北京市海淀区上地十街10号'); // 地址转坐标,data.point 即坐标
// getLocation({ lng: 116.404, lat: 39.915 }); // 反向:坐标转地址

命令式操作地图

需要主动控制地图(飞到某点、缩放、坐标转换等)时,给 Map 一个 ref:

import { useRef } from 'react';
import { Map, type MapRef } from '@baidumap/react-bmap';

function Demo() {
  const map = useRef<MapRef>(null);

  return (
    <>
      <button onClick={() => map.current?.panTo({ lng: 116.404, lat: 39.915 })}>回到中心</button>
      <button onClick={() => map.current?.setZoom(15)}>放大</button>
      <Map ref={map} center={{ lng: 116.404, lat: 39.915 }} zoom={12} />
    </>
  );
}

ref 上提供了地图的全量方法:panTo / flyTo / setZoom / setViewport / getBounds / pointToPixel / getScreenshot / 街景 / 视角动画等。

能用哪些组件

从 @baidumap/react-bmap 顶层直接 import,每个组件都带 TypeScript 类型。

  • 覆盖物:Marker、Label、Polyline、Polygon、Circle、Rectangle、BezierCurve、Prism、GroundOverlay、GroundPoint、PointCollection、InfoWindow、SimpleInfoWindow、Symbol、Icon、IconSequence、Hotspot、CustomOverlay、Marker3D、MapMask、PlaceDetail、PlaceDetailPanel
  • 控件:NavigationControl、NavigationControl3D、ScaleControl、OverviewMapControl、MapTypeControl、CopyrightControl、GeolocationControl、PanoramaControl、ZoomControl、CityListControl、LocationControl、LogoControl
  • 图层:TileLayer、NormalLayer、GeoJSONLayer、DistrictLayer、TrafficLayer、CustomLayer、CanvasLayer、RasterTileLayer、FeatureLayer、FillLayer、DOMLayer、PointIconLayer、PointShapeLayer、PanoramaCoverageLayer、LineLayer、PixelLayer、BaiduLayer、ThreeLayer
  • 右键菜单:ContextMenu、MenuItem
  • 全景:Panorama、PanoramaLabel
  • 检索 Hook:useDrivingRoute、useWalkingRoute、useRidingRoute、useTransitRoute、useTruckRoute、useLocalSearch、useAutocomplete、useBusLineSearch、usePlaceDetail、useGeocoder、useBoundary、useLocalCity、useConvertor、useGeolocation、usePanoramaService

部分组件只在 4.0(WebGL)下可用(如 Prism、MapMask、Marker3D)。在不支持的版本使用时,默认会打印一条警告并跳过,不会让页面崩溃。

ThreeLayer 需要宿主自备 three.js

ThreeLayer 不打包 three.js,也不把它写进依赖 —— 3D 场景由你自己搭,版本该由宿主工程决定。SDK 的构造函数第一件事就是检查 window.THREE,缺了会直接抛异常,所以必须把 three.js 挂到全局:

import * as THREE from 'three';
window.THREE = THREE;  // 或用 <script> 引 UMD 包

注意 r150 起 three.js 移除了 build/three.min.js,用 CDN 的话要么取 three@0.137.5 这类还带 UMD 全局的版本,要么自己 import 后赋值。异步加载时,等 window.THREE 就绪再渲染 <ThreeLayer>;检测不到时组件会打印一次警告并跳过创建。

场景操作走 ref 拿到的命令式句柄:

const layerRef = useRef<ThreeLayerRef>(null);

<ThreeLayer
  ref={layerRef}
  antialias
  onInit={(renderer, scene, camera, layer) => {
    const mesh = new THREE.Mesh(
      new THREE.BoxGeometry(500, 500, 500),
      new THREE.MeshBasicMaterial({ color: 0x1890ff }),
    );
    const [x, y] = layer.toWorld(center)!;  // 经纬度 → 图层世界坐标(单位约等于米)
    mesh.position.set(x, y, 250);
    layer.add(mesh);
    layer.triggerRepaint();  // raf 循环在 needsUpdate 为假时会自己停,改完场景要补这一下
  }}
/>

只有 alpha、antialias 和「是否接了 onRender」是构造期参数,改它们会重建图层;visible、minZoom、maxZoom、zIndex 走 setter,改了不会销毁你的场景。图层级的 opacity 对 ThreeLayer 无效(SDK 从不读它),透明度请设在材质上。

常见问题

地图不显示? 检查 Map 的容器是否有明确的宽高,ak 是否有效、是否配置了域名白名单。

报错「只能加载一个版本」? 同一个页面只能加载一个 JSAPI 版本,确保只有一个 BMapProvider,且 version 保持一致。

要用代理、隐藏 ak? 给 BMapProvider 传 serviceHost(代理地址,末尾带 /),此时不需要 ak。

本地跑示例

npm install
npm run examples

启动后在浏览器里可以看到每个组件的交互示例和 API 表格。

许可证

MIT