当前位置: 首页 > news >正文

react如何引用(按需加载)百度地图,并结合and组件化封装

1.技术选项:

vite+react+antdesign

load-script

2.实现思路:

1.按需加载如何实现?

  要实现按需加载就不能直接在项目的入口文件这种地方去通过script标签引入,这里使用load-script封装了一个加载百度地图的Bmap.js方法,实现动态的插入script脚本。

根目录下创建Bmap.js文件

import _loadScript from 'load-script';
 
export function Map(url) {
    return new Promise((resolve, reject) => {
        _loadScript(url, (error, script) => {
            if (error) {
                return reject(error);
            }
            console.log('====================================');
            console.log(script);
            console.log('====================================');
            resolve(script);
        });
    });
}
 
 
export default function loadBMap() {
    return new Promise(function(resolve, reject) {
        if (typeof BMap !== 'undefined') {
            resolve(BMap);
            return true;
        }
        window.onBMapCallback = function() {
            resolve(BMap);
        };
        let script = document.createElement('script');
        script.type = 'text/javascript';
        script.src =
            'https://api.map.baidu.com/api?v=3.0&ak=自己的';
        script.onerror = reject;
        document.head.appendChild(script);
    });
}
2.初始化加载地图(注意事项)

要初始化加载地图,我们需要确保的时候,在页面绘制完,或者弹框加载完成之后再去调用初始化的方法,否则就会报错。

报错示例:

Cannot read properties of undefined (reading  kc )

封装示例图

3.map.js脚本实现如下:

// 加载百度地图
export function LoadBaiduMapScript() {
  console.log("百度地图脚本初始化ing----------------");
  const BMap_URL =
    "https://api.map.baidu.com/api?v=3.0&ak=换成自己的&callback=onBMapCallback";
  return new Promise((resolve, _reject) => {
    // 如果已加载直接返回
    if (typeof BMap !== "undefined") {
      resolve(BMap);
      return true;
    }
    // 百度地图异步加载回调处理
    window.onBMapCallback = function () {
      console.log("百度地图脚本初始化成功...");
      resolve(BMap);
    };
    // 插入script脚本
    const scriptNode = document.createElement("script");
    scriptNode.setAttribute("type", "text/javascript");
    scriptNode.setAttribute("src", BMap_URL);
    document.body.appendChild(scriptNode);
  });
}

React实现代码如下:

注意:new BMap 可能会爆红 不用管,不影响使用

地图实例,标记示例,手掌选择实例使用useRef存储吗,使用useState存储会导致内容重新渲染,造成初始化失败

import { Modal, Button, message, Input, Spin } from "antd";
import {
  useState,
  forwardRef,
  useImperativeHandle,
  useEffect,
  useRef,
} from "react";
import positionIcon from "@/assets/images/position.png";
import { LoadBaiduMapScript } from "./map";
import "./index.less";

interface MapPropsType {
  onConfirm: (val) => void;
}

const MapChoice = forwardRef((props: MapPropsType, ref) => {
  const { Search } = Input;
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [searchValue, setSearchValue] = useState<string>("");
  const [loading, setLoading] = useState<boolean>(false);
  const [address, setAddress] =
    useState<string>("河南省郑州市二七区建设东路48号");
  const [lon, setLon] = useState<number>(113.65);
  const [lat, setLat] = useState<number>(34.76);

  // 使用 ref 存储地图相关对象(避免状态异步问题)
  const mapRef = useRef<any>(null);
  const pointRef = useRef<any>(null);
  const markerRef = useRef<any>(null);

  const showModal = () => {
    setIsModalOpen(true);
  };

  const handleOk = () => {
    setIsModalOpen(false);
    const data = { address, lon, lat };
    props.onConfirm(data); // 回调方式
  };

  const handleCancel = () => {
    setIsModalOpen(false);
  };

  useImperativeHandle(ref, () => ({
    showModal,
    handleOk,
    handleCancel,
  }));

  useEffect(() => {
    if (!isModalOpen) return;

    const init = async () => {
      await LoadBaiduMapScript();
      initMap({});
      await browserPosition();
    };

    init();
  }, [isModalOpen]);

  // 初始化地图
  const initMap = (record: any) => {
    const currentAddress = record.address || address || "郑州市";
    const currentLon = record?.coordinates?.lon ?? lon;
    const currentLat = record?.coordinates?.lat ?? lat;

    // 创建地图实例(使用 ref 存储)
    const mapInstance = new BMap.Map("container");
    const pointInstance = new BMap.Point(currentLon, currentLat);
    const myIcon = new BMap.Icon(positionIcon, new BMap.Size(23, 25));
    const markerInstance = new BMap.Marker(pointInstance, myIcon);

    // 存储到 ref
    mapRef.current = mapInstance;
    pointRef.current = pointInstance;
    markerRef.current = markerInstance;

    // 初始化地图设置
    mapInstance.centerAndZoom(pointInstance, 15);
    mapInstance.enableScrollWheelZoom();
    mapInstance.addOverlay(markerInstance);

    // 更新状态
    setAddress(currentAddress);
    setLon(currentLon);
    setLat(currentLat);

    // 绑定点击事件
    mapInstance.addEventListener("click", (e: any) => {
      const clickedPoint = new BMap.Point(e.point.lng, e.point.lat);
      mapInstance.centerAndZoom(clickedPoint, 15);
      pointRef.current = clickedPoint;

      // 逆地理编码获取地址
      const gc = new BMap.Geocoder();
      gc.getLocation(clickedPoint, (rs: any) => {
        if (rs?.address) {
          setAddress(rs.address);
          setLon(e.point.lng);
          setLat(e.point.lat);
          upInfoWindow(rs.address, e.point.lng, e.point.lat);
        }
      });
    });

    // 初始信息窗口
    upInfoWindow(currentAddress, currentLon, currentLat);
  };

  // 更新信息窗口
  const upInfoWindow = (address: string, lon: number, lat: number) => {
    if (!mapRef.current || !pointRef.current) return;

    const opts = {
      width: 250,
      height: 120,
      title: "经纬度",
    };
    const word = `<div>地址:${address}</div>
                  <div>经度:${lon}</div>
                  <div>纬度:${lat}</div>`;
    const infoWindow = new BMap.InfoWindow(word, opts);

    mapRef.current.openInfoWindow(infoWindow, pointRef.current);
    markerRef.current.addEventListener("click", () => {
      mapRef.current.openInfoWindow(infoWindow, pointRef.current);
    });
  };

  // 地址搜索
  const handleSearch = () => {
    if (!searchValue?.trim()) {
      message.warning("搜索框不能为空");
      return;
    }

    const myGeo = new BMap.Geocoder();
    myGeo.getPoint(searchValue.trim(), (point: any) => {
      if (point) {
        mapRef.current.centerAndZoom(point, 15);
        pointRef.current = point;
        setLon(point.lng);
        setLat(point.lat);

        const gc = new BMap.Geocoder();
        gc.getLocation(point, (rs: any) => {
          if (rs?.address) {
            setAddress(rs.address);
            upInfoWindow(rs.address, point.lng, point.lat);
          }
        });
      } else {
        message.warning("您选择的地址没有解析到结果!");
      }
    });
  };

  // 浏览器定位
  const browserPosition = async () => {
    setLoading(true);
    const geolocation = new BMap.Geolocation();

    geolocation.getCurrentPosition(async (r: any) => {
      if (r?.point) {
        const point = new BMap.Point(r.point.lng, r.point.lat);
        mapRef.current.centerAndZoom(point, 15);
        pointRef.current = point;

        const gc = new BMap.Geocoder();
        gc.getLocation(point, (rs: any) => {
          if (rs?.address) {
            setAddress(rs.address);
            setLon(r.point.lng);
            setLat(r.point.lat);
            upInfoWindow(rs.address, r.point.lng, r.point.lat);
          }
        });
      } else {
        message.error("定位失败,请手动输入经纬度");
      }
      setLoading(false);
    });
  };

  return (
    <>
      <div className="flex items-center">
        <div className="mr-2">位置</div>
        <Button type="primary" onClick={showModal}>
          唤醒地图
        </Button>
      </div>
      <Modal
        title="地图选择"
        open={isModalOpen}
        onOk={handleOk}
        onCancel={handleCancel}
        width={1000}
        okText={"确认"}
        cancelText={"关闭"}
      >
        <Spin spinning={loading}>
          <Search
            placeholder="请输入地址"
            value={searchValue}
            onChange={(e) => setSearchValue(e.target.value)}
            onSearch={handleSearch}
            style={{ width: "40%" }}
          />
          <div id="container" className="positionbox"></div>
        </Spin>
      </Modal>
    </>
  );
});

export default MapChoice;

less样式代码

.positionbox {
    width: 100%;
    height: 64vh;
    margin-top: 20px;
  }

实现效果如下

相关文章:

  • 【leetcode】拆解与整合:分治并归的算法逻辑
  • ​​​​​​Spring Boot热部署插件
  • 【第33节】windows原理:初探PE文件
  • 用 pytorch 从零开始创建大语言模型(三):编码注意力机制
  • 详解CountDownLatch底层源码
  • SQL:CASE WHEN使用详解
  • 二层网络综合实验
  • 从 JDK 11 到 JDK 17:OpenRewrite 实战 Spring Boot 升级指南
  • 项目-苍穹外卖(十七) Apache POI+导出数据
  • 路由器DHCP地址池冲突
  • 【网络协议详解】—— STP 、RSTP、MSTP技术(学习笔记)
  • 15 网络编程:三要素(IP地址、端口、协议)、UDP通信实现和TCP通信实现 (黑马Java视频笔记)
  • C语言复习笔记--操作符详解(下)
  • 蓝桥杯 之 LCA算法
  • 搜广推校招面经六十一
  • 多线程案例-单例模式
  • Tcp套接字编程
  • go - grpc入门
  • 5G_WiFi_CE_杂散测试
  • C语言入门教程100讲(0)从了解C语言的发展史开始
  • 锦江酒店:第一季度营业收入约29.42亿元,境内酒店出租率同比增长
  • 今年一季度全国社会物流总额达91万亿元,工业品比重超八成
  • 河北:开展领导干部任性用权等形式主义官僚主义问题专项整治
  • 路边“僵尸车”被人以1450元卖了,嫌疑人被刑拘
  • 西湖大学本科招生新增三省两市,首次面向上海招生
  • 法治日报调查直播间“杀熟”乱象:熟客越买越贵,举证难维权不易