地图的基本展示
首先实现地图的基本展示
组件定义与状态初始化
const mapContainerRef = useRef<HTMLDivElement>(null);
const [isDarkMode, setIsDarkMode] = useState<boolean>(false);
mapContainerRef
: 使用useRef
创建一个引用,指向地图容器的DOM元素。isDarkMode
: 使用useState
创建一个状态变量,用于跟踪当前是否处于深色模式。
初始化地图函数
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元素已经挂载。 - 创建一个新的地图实例,设置缩放级别、中心点以及根据
isDarkMode
状态选择地图样式。 - 调用
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]);
在组件挂载时执行以下操作:
- 配置高德地图的安全密钥。
- 动态创建并插入一个
<script>
标签来加载高德地图的JS API。 - 设置脚本加载完成后调用
initMap
函数来初始化地图。 - 返回一个清理函数,在组件卸载时移除之前插入的脚本标签,防止内存泄漏。
[isDarkMode]
作为依赖数组,意味着每当isDarkMode
状态改变时,都会重新执行useEffect
中的逻辑,从而更新地图样式。
渲染组件
return (
<div className="box">
<h2>重庆到成都的路线</h2>
<button onClick={() => setIsDarkMode(!isDarkMode)}>
{isDarkMode ? "切换到浅色模式" : "切换到深色模式"}
</button>
<div ref={mapContainerRef} style={{ width: "100%", height: "500px" }}></div>
</div>
);
- 显示标题“重庆到成都的路线”。
- 提供一个按钮,点击可以切换浅色模式和深色模式。
注意:切换浅色模式和深色模式功能,要配置安全密钥、替换为你的 API Key后才会生效
- 使用
ref
属性将mapContainerRef
绑定到一个div
元素上,这个div
作为地图的容器,并设置了宽度和高度。
注意:div 一定要设置宽度和高度才能看到
完整代码:
import React, { useEffect, useRef, useState } from "react";
const DataPage = () => {
// 使用 useRef 来获取容器元素
const mapContainerRef = useRef<HTMLDivElement>(null);
const [isDarkMode, setIsDarkMode] = useState<boolean>(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();
}
};
useEffect(() => {
// 设置代理服务器地址
window._AMapSecurityConfig = {
securityJsCode: "api", // 配置安全密钥
};
// 创建并插入地图 JSAPI 脚本
const script = document.createElement("script");
script.src = "https://webapi.amap.com/maps?v=1.4.15&key=api"; // 替换为你的 API Key
script.async = true;
// 将脚本插入到 body 中
document.body.appendChild(script);
// 初始化地图
script.onload = initMap;
// 清理函数,在组件卸载时移除脚本标签
return () => {
document.body.removeChild(script);
};
}, [isDarkMode]);
return (
<div className="box">
<h2>重庆到成都的路线</h2>
<button onClick={() => setIsDarkMode(!isDarkMode)}>
{isDarkMode ? "切换到浅色模式" : "切换到深色模式"}
</button>
<div
ref={mapContainerRef}
style={{ width: "100%", height: "500px" }}
></div>
</div>
);
};
export default DataPage;
添加控件
按钮控制显示/隐藏缩放控件
<button onClick={() => setToolbarVisible(!toolbarVisible)}>
{toolbarVisible ? "隐藏缩放控件" : "显示缩放控件"}
</button>
onClick
事件处理函数:- 当用户点击按钮时,触发一个匿名函数,该函数调用
setToolbarVisible
来切换toolbarVisible
状态变量的值。 !toolbarVisible
表示取反当前的状态值。如果toolbarVisible
为true
,则设置为false
;反之亦然。
- 当用户点击按钮时,触发一个匿名函数,该函数调用
按钮文本:
使用三元运算符根据
toolbarVisible
的状态决定按钮上显示的文本:- 如果
toolbarVisible
为true
,则显示“隐藏缩放控件”。 - 如果
toolbarVisible
为false
,则显示“显示缩放控件”。
- 如果
添加控件函数
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
(布尔值,指示是否显示控件)。 - 首先检查
toolbarVisible
是否为false
。如果是,则直接返回,不执行后续逻辑。
- 接受两个参数:
加载插件:
- 使用
window.AMap.plugin
方法加载高德地图的插件AMap.ToolBar
和AMap.Scale
。 - 这些插件提供了地图上的缩放控件和比例尺功能。
- 回调函数会在插件加载完成后执行。
- 使用
创建并添加控件:
- 创建一个包含缩放工具条和比例尺实例的数组
controls
。 - 使用
forEach
遍历controls
数组,并调用map.addControl(control)
将每个控件添加到地图上。
- 创建一个包含缩放工具条和比例尺实例的数组
完整代码:
import React, { useEffect, useRef, useState } from "react";
const DataPage= () => {
// 使用 useRef 来获取容器元素
const mapContainerRef = useRef<HTMLDivElement>(null);
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" : "", // 根据是否是深色模式选择地图样式
});
// 缩放到包含整个折线的视图
map.setFitView();
addControls(map, toolbarVisible);
}
};
// 添加控件
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); // 添加控件
});
});
};
useEffect(() => {
// 设置代理服务器地址
window._AMapSecurityConfig = {
securityJsCode: "api", // 配置安全密钥
};
// 创建并插入地图 JSAPI 脚本
const script = document.createElement("script");
script.src = "https://webapi.amap.com/maps?v=1.4.15&key=api"; // 替换为你的 API Key
script.async = true;
// 将脚本插入到 body 中
document.body.appendChild(script);
// 初始化地图
script.onload = initMap;
// 清理函数,在组件卸载时移除脚本标签
return () => {
document.body.removeChild(script);
};
}, [isDarkMode, toolbarVisible]);
return (
<div className="box">
<h2>重庆到成都的路线</h2>
<button onClick={() => setIsDarkMode(!isDarkMode)}>
{isDarkMode ? "切换到浅色模式" : "切换到深色模式"}
</button>
<button onClick={() => setToolbarVisible(!toolbarVisible)}>
{toolbarVisible ? "隐藏缩放控件" : "显示缩放控件"}
</button>
<div
ref={mapContainerRef}
style={{ width: "100%", height: "500px" }}
></div>
</div>
);
};
export default DataPage;
定义坐标并创建折线
定义坐标并创建折线
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);
}
定义坐标:
beijing
和chengdu
分别存储了重庆和成都的地理坐标(经度和纬度)。
创建折线路径:
- 如果
showPolyline
为true
,则创建一个由两个坐标点组成的路径数组path
。
- 如果
创建折线对象:
使用
window.AMap.Polyline
创建一个新的折线对象,并设置其属性:path
: 折线路径,即从重庆到成都的坐标点。strokeColor
: 折线的颜色,这里是红色 (#FF0000
)。strokeWeight
: 折线的宽度,单位是像素。strokeOpacity
: 折线的透明度,范围是[0, 1]
。map
: 将折线添加到的地图实例。
缩放地图以适应折线:
- 调用
map.setFitView([polyline])
方法,调整地图的缩放级别和中心点,使得折线完全显示在地图视图中。
- 调用
添加飞行路径动画:
- 调用
addFlightAnimation(map, polyline)
函数,为折线路径添加飞行动画效果。
- 调用
完整代码:
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> = () => {
// 使用 useRef 来获取容器元素
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", // 使用有效的图片URL
size: new window.AMap.Size(32, 32), // 设置图标的大小,单位为像素,默认值是图片的实际大小
offset: new window.AMap.Pixel(-16, -16), // 图标偏移量,通常是-size.width/2, -size.height
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;
}
// 使用 requestAnimationFrame 实现平滑动画
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;
};
useEffect(() => {
// 设置代理服务器地址
window._AMapSecurityConfig = {
securityJsCode: "api", // 配置安全密钥
};
// 创建并插入地图 JSAPI 脚本
const script = document.createElement("script");
script.src =
"https://webapi.amap.com/maps?v=1.4.15&key=api"; // 替换为你的 API Key
script.async = true;
// 将脚本插入到 body 中
document.body.appendChild(script);
// 初始化地图
script.onload = initMap;
// 清理函数,在组件卸载时移除脚本标签
return () => {
document.body.removeChild(script);
};
}, [showPolyline, isDarkMode, toolbarVisible]);
return (
<div className="box">
<h2>重庆到成都的路线</h2>
<button onClick={() => setShowPolyline(!showPolyline)}>
{showPolyline ? "隐藏路线" : "显示路线"}
</button>
<button onClick={() => setIsDarkMode(!isDarkMode)}>
{isDarkMode ? "切换到浅色模式" : "切换到深色模式"}
</button>
<button onClick={() => setToolbarVisible(!toolbarVisible)}>
{toolbarVisible ? "隐藏缩放控件" : "显示缩放控件"}
</button>
<div
ref={mapContainerRef}
style={{ width: "100%", height: "500px" }}
></div>
</div>
);
};
export default MapPage;