在前端开发中,地图功能的实现十分常见,而React与高德地图的结合能够打造出丰富且交互性强的地图应用。本文将详细介绍如何利用React实现高德地图的基础展示、深色模式切换、缩放控件的显示与隐藏,以及动态路线的绘制和飞行路径动画效果,帮助大家快速掌握相关技术要点。

一、搭建基础地图

在开始构建复杂的地图功能前,需要先实现地图的基本展示。这是后续所有功能的基础。

(一)组件定义与状态初始化

const mapContainerRef = useRef<HTMLDivElement>(null); const [isDarkMode, setIsDarkMode] = useState<boolean>(false); 

上述代码使用useRef创建了一个mapContainerRef引用,这个引用用来获取地图容器对应的DOM元素。同时,通过useState创建了isDarkMode状态变量,它用于记录当前地图是否处于深色模式,初始值为false,也就是默认是浅色模式。

(二)初始化地图函数

const initMap = () => { if (mapContainerRef.current) { const map = new window.AMap.Map(mapContainerRef.current, { zoom: 5, center: [110, 35], mapStyle: isDarkMode ? "amap://styles/dark" : "", }); map.setFitView(); } }; 

initMap函数的作用是初始化地图实例。首先,它会检查mapContainerRef.current是否存在,只有当这个引用对应的DOM元素已经挂载到页面上时,才会继续执行后续操作。接着,创建一个新的高德地图实例,设置地图的缩放级别为5,中心点坐标为[110, 35],并且根据isDarkMode的值来选择地图样式,如果是深色模式,就使用"amap://styles/dark"样式,否则不设置特殊样式。最后,调用map.setFitView()方法,这个方法会自动调整地图视图,让地图上的所有覆盖物都能完整显示在当前视图中,虽然此时还没有添加覆盖物,但这是一个通用的设置。

(三)使用useEffect处理副作用

useEffect(() => { window._AMapSecurityConfig = { securityJsCode: "api", }; const script = document.createElement("script"); script.src = "https://webapi.amap.com/maps?v=1.4.15&key=api"; script.async = true; document.body.appendChild(script); script.onload = initMap; return () => { document.body.removeChild(script); }; }, [isDarkMode]); 

在React组件中,useEffect用于处理副作用操作。这里,当组件挂载到页面上时,会执行一系列操作:

  1. 配置高德地图的安全密钥,设置window._AMapSecurityConfig对象中的securityJsCode属性,这一步很关键,它确保了地图API的安全调用。
  2. 创建一个<script>标签,通过设置src属性来引入高德地图的JS API,版本号为1.4.15,同时别忘了将key替换为你自己申请的API Key。async属性设置为true,表示这个脚本会在后台加载,不会阻塞页面的渲染。
  3. 将创建好的脚本标签添加到document.body中,这样就开始加载地图API了。
  4. 当脚本加载完成后,会触发onload事件,此时调用initMap函数来初始化地图。
  5. 最后返回一个清理函数,当组件从页面上卸载时,这个函数会被执行,它的作用是从document.body中移除之前添加的脚本标签,防止内存泄漏。

这里[isDarkMode]作为useEffect的依赖数组,意味着只要isDarkMode状态发生变化,useEffect中的代码就会重新执行,这样就能实现当地图模式切换时,重新初始化地图并应用新的样式。

(四)渲染组件

return ( <div className="box"> <h2>重庆到成都的路线</h2> <button onClick={() => setIsDarkMode(!isDarkMode)}> {isDarkMode ? "切换到浅色模式" : "切换到深色模式"} </button> <div ref={mapContainerRef} style={{ width: "100%", height: "500px" }}></div> </div> ); 

在组件的返回部分,首先展示了一个标题重庆到成都的路线,用于说明地图的主要内容。接着是一个按钮,点击这个按钮会调用setIsDarkMode(!isDarkMode)函数,实现深色模式和浅色模式的切换,并且按钮上的文字会根据当前的模式状态进行相应的变化。最后,通过ref属性将mapContainerRef绑定到一个<div>元素上,这个<div>就是地图的显示容器,设置它的宽度为100%,高度为500px,这样地图才能正常显示在页面上。需要注意的是,一定要给这个<div>设置宽度和高度,否则地图将无法显示。

二、添加缩放控件

在地图上添加缩放控件,可以让用户更方便地操作地图,放大或缩小地图视图。下面来看看如何实现这一功能。

(一)按钮控制显示/隐藏缩放控件

<button onClick={() => setToolbarVisible(!toolbarVisible)}> {toolbarVisible ? "隐藏缩放控件" : "显示缩放控件"} </button> 

这段代码创建了一个按钮,当用户点击按钮时,会触发onClick事件。在事件处理函数中,调用setToolbarVisible(!toolbarVisible),这行代码的作用是取反toolbarVisible的当前值,从而实现缩放控件显示和隐藏状态的切换。同时,按钮上显示的文本也会根据toolbarVisible的值进行变化,如果toolbarVisibletrue,表示缩放控件当前是显示状态,按钮文本就显示为“隐藏缩放控件”;反之,如果toolbarVisiblefalse,按钮文本则显示为“显示缩放控件”。

(二)添加控件函数

const addControls = (map, toolbarVisible) => { if (!toolbarVisible) return; window.AMap.plugin(["AMap.ToolBar", "AMap.Scale"], () => { const controls = [ new window.AMap.ToolBar(), new window.AMap.Scale(), ]; controls.forEach((control) => { map.addControl(control); }); }); }; 

addControls函数用于向地图添加缩放控件和比例尺。它接受两个参数,map是地图实例,toolbarVisible是一个布尔值,用于判断是否要显示这些控件。如果toolbarVisiblefalse,函数会直接返回,不执行后续添加控件的操作。

toolbarVisibletrue时,通过window.AMap.plugin方法加载高德地图的AMap.ToolBarAMap.Scale插件。这两个插件分别提供了缩放工具条和比例尺的功能。插件加载完成后,会执行回调函数。在回调函数中,创建一个包含缩放工具条和比例尺实例的数组controls,然后使用forEach方法遍历这个数组,将每个控件通过map.addControl(control)添加到地图上。

三、定义坐标并创建折线

为了在地图上展示重庆到成都的路线,需要定义两个城市的坐标,并创建一条折线来连接它们。

(一)定义坐标

const beijing: [number, number] = [106.5516, 29.563]; const chengdu: [number, number] = [104.06579, 30.570462]; 

这里定义了两个常量beijingchengdu,分别存储了重庆和成都的地理坐标。坐标由经度和纬度组成,这是在地图上定位一个地点的关键信息。

(二)创建折线

if (showPolyline) { const path = [beijing, chengdu]; const polyline = new window.AMap.Polyline({ path: path, strokeColor: "#FF0000", strokeWeight: 5, strokeOpacity: 0.8, map: map, }); map.setFitView([polyline]); addFlightAnimation(map, polyline); } 

showPolylinetrue时,会执行创建折线的操作。首先,创建一个包含重庆和成都坐标的路径数组path。然后,使用window.AMap.Polyline创建一个折线对象polyline,并设置它的多个属性:

  • path:指定折线的路径,也就是前面创建的path数组。
  • strokeColor:设置折线的颜色为红色(#FF0000)。
  • strokeWeight:设置折线的宽度为5像素。
  • strokeOpacity:设置折线的透明度为0.8,取值范围是[0, 1],数值越接近1越不透明。
  • map:将创建好的折线添加到地图实例中。

创建好折线后,调用map.setFitView([polyline])方法,让地图自动调整视图,使整个折线都能完整显示在地图上。最后,调用addFlightAnimation(map, polyline)函数,为这条折线添加飞行路径动画效果。

(三)添加飞行路径动画

const addFlightAnimation = (map: any, polyline: any) => { const marker = new window.AMap.Marker({ icon: "https://webapi.amap.com/images/car.png", size: new window.AMap.Size(32, 32), offset: new window.AMap.Pixel(-16, -16), autoRotation: true, angle: -90, }); marker.setMap(map); const path = polyline.getPath(); if (path.length < 2) { console.error("路径点不足,无法进行动画"); return; } let count = 0; const length = path.length; const animateMarker = () => { count = (count + 1) % length; const lnglat = path[count]; marker.setPosition(lnglat); if (count < length - 1) { const nextLnglat = path[count + 1]; const angle = getAngle(lnglat, nextLnglat); marker.setRotation(angle); } else { console.log("动画完成,可以在这里处理结束逻辑"); count = 0; } window.requestAnimationFrame(animateMarker); }; window.requestAnimationFrame(animateMarker); }; const getAngle = (start: [number, number], end: [number, number]) => { const lat1 = start[1], lng1 = start[0]; const lat2 = end[1], lng2 = end[0]; const y = Math.sin(((lng2 - lng1) * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180); const x = Math.cos((lat1 * Math.PI) / 180) * Math.sin((lat2 * Math.PI) / 180) - Math.sin((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.cos(((lng2 - lng1) * Math.PI) / 180); return ((Math.atan2(y, x) * 180) / Math.PI + 360) % 360; }; 

addFlightAnimation函数用于为折线添加飞行路径动画效果。首先,创建一个标记marker,这个标记代表飞行的物体,这里使用了一个汽车图标(https://webapi.amap.com/images/car.png),并设置了标记的大小、偏移量、自动旋转属性以及初始角度。然后将这个标记添加到地图上。

接着获取折线的路径点数组path,如果路径点不足2个,就无法进行动画,会在控制台输出错误信息并返回。之后定义一个计数器count和路径点的长度length

animateMarker函数是动画的核心逻辑,它会在每次调用时更新标记的位置。通过count的递增来获取路径数组中的下一个坐标点,并将标记移动到该位置。同时,计算下一个点的方向并设置标记的旋转角度,让标记的方向与飞行方向一致。当标记到达路径的最后一个点时,可以在这里处理动画结束的逻辑,比如重新开始动画。最后,使用window.requestAnimationFrame方法来实现平滑的动画效果,它会在浏览器下次重绘之前调用传入的函数。

getAngle函数用于计算两个坐标点之间的方向角,通过一系列三角函数的计算,返回一个表示方向的角度值,这个值会用于设置标记的旋转角度。

四、完整代码示例

import React, { useEffect, useRef, useState } from "react"; import "./index.scss"; // 定义 AMap 相关类型 declare global { interface Window { AMap: any; _AMapSecurityConfig: { serviceHost: string; }; } } interface MapPageProps {} const MapPage: React.FC<MapPageProps> = () => { const mapContainerRef = useRef<HTMLDivElement>(null); const [showPolyline, setShowPolyline] = useState<boolean>(true); const [isDarkMode, setIsDarkMode] = useState<boolean>(false); const [toolbarVisible, setToolbarVisible] = useState<boolean>(true); const initMap = () => { if (mapContainerRef.current) { const map = new window.AMap.Map(mapContainerRef.current, { zoom: 5, center: [110, 35], mapStyle: isDarkMode ? "amap://styles/dark" : "", }); const beijing: [number, number] = [106.5516, 29.563]; const chengdu: [number, number] = [104.06579, 30.570462]; if (showPolyline) { const path = [beijing, chengdu]; const polyline = new window.AMap.Polyline({ path: path, strokeColor: "#FF0000", strokeWeight: 5, strokeOpacity: 0.8, map: map, }); map.setFitView([polyline]); addFlightAnimation(map, polyline); } addControls(map, toolbarVisible); } }; const addControls = (map: any, toolbarVisible: boolean) => { if (!toolbarVisible) return; window.AMap.plugin(["AMap.ToolBar", "AMap.Scale"], () => { const controls = [ new window.AMap.ToolBar(), new window.AMap.Scale(), ]; controls.forEach((control) => { map.addControl(control); }); }); }; const addFlightAnimation = (map: any, polyline: any) => { const marker = new window.AMap.Marker({ icon: "https://webapi.amap.com/images/car.png", size: new window.AMap.Size(32, 32), offset: new window.AMap.Pixel(-16, -16), autoRotation: true, angle: -90, }); marker.setMap(map); const path = polyline.getPath(); if (path.length < 2) { console.error("路径点不足,无法进行动画"); return; } let count = 0; const length = path.length; const animateMarker = () => { count = (count + 1) % length; const lnglat = path[count]; marker.setPosition(lnglat); if (count < length - 1) { const nextLnglat = path[count + 1]; const angle = getAngle(lnglat, nextLnglat); marker.setRotation(angle); } else { console.log("动画完成,可以在这里处理结束逻辑"); count = 0; } window.requestAnimationFrame(animateMarker); }; window.requestAnimationFrame(animateMarker); }; const getAngle = (start: [number, number], end: [number, number]) => { const lat1 = start[1], lng1 = start[0]; const lat2 = end[1], lng2 = end[0]; const y = Math.sin(((lng2 - lng1) * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180); const x = Math.cos((lat1 * Math.PI) / 180) * Math.sin((lat2 * Math.PI) / 18 

以上就是React如何集成高德地图实现动态路线绘制、控件交互与深色模式切换,希望对你有帮助!