百度地图使用react组件渲染自定义覆盖物

383 阅读1分钟

1. 问题

我想使用百度地图自定义dom覆盖物,但是我想渲染react组件,这里记录一下遇到的问题。 (百度地图示例)[lbs.baidu.com/jsdemo.htm#…]

1.1. 解决

首先想到的就是使用renderToString,这个方法可以将组件渲染成一个字符串,通常用在ssr上,但是百度地图是直接插入的dom元素,所以必须返回一个dom对象,字符串是不行的。

// CustomOverlay定义
// 官方的缺少定义
declare namespace BMapGL {
  class CustomOverlay<P = any> extends Overlay {
    constructor(dom: (properties: P) => HTMLElement, opts: CustomOverlayOptions);
  }

  interface CustomOverlay<P = any> extends Overlay {
    setProperties: (properties: P) => void;
    getProperties: () => P;
    setOptions: (option: Partial<CustomOverlayOptions<P>>) => void;
    setPoint: (point: BMapGL.Point) => void;
  }
  interface CustomOverlayOptions<P = any> {
    point: Point;
    offsetX?: number;
    offsetY?: number;
    MinZoom?: number;
    MaxZoom?: number;
    properties?: P;
    enableMassClear?: boolean;
    enableDraggingMap?: boolean;
  }
}

// 大概的使用方法
const createDOM = (props: p) => {
    return document.createElement('div')
};
const layer = new BMapGL.CustomOverlay<p>(createDOM, {point: new BMapGL.Point(0, 0)});
map.addOverlay(layer);

解决问题方法就是重新调用render函数渲染组件,不过会丢失context,如果你需要就只能手动穿进去了。

    const createDOM = (props: SelectedAsset) => {
      // 这里创建的是react的根节点
      const container = document.createElement('div');
      container.setAttribute('style', 'width: 100%;height: 100%;');
      // 这里阻止了一些事件的冒泡
      // 在弹窗操作的时候地图也操作了
      container.onwheel = (e) => e.stopPropagation();
      container.ondblclick = (e) => e.stopPropagation();
      container.oncontextmenu = (e) => e.stopPropagation();
      // 使用react的createRoot创建一个新的渲染节点
      infoWindowRoot.current = createRoot(container);
      // 这里返回的是新的react的根节点
      return container;
    };
    // 然后就在渲染节点上创建覆盖物
    infoWidnow.current = new BMapGL.CustomOverlay<SelectedAsset>(createDOM, {
      point: new BMapGL.Point(0, 0),
      offsetY: -40,
    });
    infoWidnow.current.hide?.();
    map.current?.addOverlay(infoWidnow.current);
// 使用的时候就只需要调用render
useEffect(() => {
infoWidnow.current.setPoint(selectedAsset.point);
// 外部数据变化的时候就render
// 只要是同一个root重复调用render只会更新节点,而不是完全重新渲染
infoWindowRoot.current.render(
  <InfoWidnow
    selectedAsset={selectedAsset}
    dict={initialState?.dict}
    onClose={() => infoWidnow.current?.hide?.()}
  />,
);
infoWidnow.current.show?.();
}, [selectedAsset]);