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);// 存儲到 refmapRef.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><Modaltitle="地圖選擇"open={isModalOpen}onOk={handleOk}onCancel={handleCancel}width={1000}okText={"確認"}cancelText={"關閉"}><Spin spinning={loading}><Searchplaceholder="請輸入地址"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;}