379 lines
12 KiB
HTML
379 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
|
<title>路径规划</title>
|
|
|
|
<style>
|
|
/* --- 全局样式 --- */
|
|
html,
|
|
body,
|
|
#container {
|
|
width: 100%;
|
|
height: 100%;
|
|
margin: 0;
|
|
font-family: sans-serif;
|
|
}
|
|
|
|
/* --- 暴力隐藏高德自带的导流链接和Logo (关键) --- */
|
|
.amap-callamap,
|
|
.amap-lib-driving-callBtn,
|
|
.amap-copyright,
|
|
.amap-logo {
|
|
display: none !important;
|
|
}
|
|
|
|
#panel .amap-call {
|
|
display: none;
|
|
}
|
|
|
|
/* --- 搜索栏样式 --- */
|
|
#search-box {
|
|
position: absolute;
|
|
top: 10px;
|
|
left: 10px;
|
|
right: 10px;
|
|
z-index: 100;
|
|
background: #fff;
|
|
padding: 10px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.input-row {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: center;
|
|
}
|
|
|
|
input {
|
|
flex: 1;
|
|
padding: 10px;
|
|
border: 1px solid #eee;
|
|
border-radius: 4px;
|
|
background-color: #f9f9f9;
|
|
font-size: 14px;
|
|
}
|
|
|
|
button {
|
|
padding: 0 15px;
|
|
height: 38px;
|
|
background: #3366FF;
|
|
color: #fff;
|
|
border: none;
|
|
border-radius: 4px;
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* --- 导航结果面板 (底部弹出) --- */
|
|
#panel {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 35%;
|
|
/* 面板高度 */
|
|
background-color: white;
|
|
overflow-y: auto;
|
|
border-top: 1px solid #eee;
|
|
border-top-left-radius: 12px;
|
|
border-top-right-radius: 12px;
|
|
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
|
z-index: 99;
|
|
display: none;
|
|
/* 默认隐藏 */
|
|
}
|
|
|
|
/* --- 自定义定位按钮样式 --- */
|
|
#location-btn {
|
|
position: fixed;
|
|
right: 10px;
|
|
bottom: 50px;
|
|
/* 默认位置 */
|
|
width: 44px;
|
|
height: 44px;
|
|
background-color: #fff;
|
|
border-radius: 50%;
|
|
/* 圆形更符合现代审美 */
|
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
|
z-index: 150;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: bottom 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
/* 平滑动画 */
|
|
}
|
|
|
|
#location-btn:active {
|
|
background-color: #f2f2f2;
|
|
}
|
|
|
|
#location-btn svg {
|
|
width: 24px;
|
|
height: 24px;
|
|
fill: #555;
|
|
}
|
|
|
|
/* --- 关键:当 body 有 panel-active 类时,按钮上移 --- */
|
|
body.panel-active #location-btn {
|
|
bottom: 38%;
|
|
/* 对应 #panel 的 height + 一点间距 */
|
|
}
|
|
</style>
|
|
|
|
<!-- 1. 配置安全密钥 -->
|
|
<script type="text/javascript">
|
|
window._AMapSecurityConfig = {
|
|
securityJsCode: '0529b72df6bf0c577ff2182cb8b1d970',
|
|
}
|
|
</script>
|
|
|
|
<!-- 2. 加载地图和插件 (去掉了 Geolocation 插件,避免弹窗) -->
|
|
<script
|
|
src="https://webapi.amap.com/maps?v=2.0&key=2cc1d822e313307fe311c3127a1deeb5&plugin=AMap.MoveAnimation,AMap.Driving,AMap.TruckDriving,AMap.AutoComplete,AMap.ToolBar,AMap.Scale">
|
|
</script>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id="search-box">
|
|
<div class="input-row">
|
|
<input id="startInput" placeholder="起点: 请输入当前地点" />
|
|
</div>
|
|
<div class="input-row">
|
|
<input id="endInput" placeholder="终点: 请输入目的地" />
|
|
<button onclick="startRouteSearch()">路径规划</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="container"></div>
|
|
|
|
<div id="panel"></div>
|
|
|
|
<!-- 自定义定位按钮 -->
|
|
<div id="location-btn" onclick="backToLocation()">
|
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
|
<path
|
|
d="M512 85.333333a426.666667 426.666667 0 1 0 0 853.333334 426.666667 426.666667 0 0 0 0-853.333334z m0 149.333334a277.333333 277.333333 0 1 1 0 554.666666 277.333333 277.333333 0 0 1 0-554.666666z m0 149.333333a128 128 0 1 0 0 256 128 128 0 0 0 0-256z"
|
|
fill="#666666"></path>
|
|
</svg>
|
|
</div>
|
|
|
|
<script>
|
|
var map, marker, driving, truckDriving;
|
|
var currentLat, currentLng;
|
|
var isTruckMode = false;
|
|
|
|
function initMap() {
|
|
map = new AMap.Map('container', {
|
|
resizeEnable: true,
|
|
zoom: 15,
|
|
viewMode: '3D'
|
|
});
|
|
|
|
// 通知 Flutter 地图加载完毕
|
|
map.on('complete', function () {
|
|
console.log("JS->: Map is ready.");
|
|
if (window.flutter_inappwebview) {
|
|
window.flutter_inappwebview.callHandler('mapReady');
|
|
}
|
|
});
|
|
|
|
// 添加基础控件
|
|
map.addControl(new AMap.Scale());
|
|
map.addControl(new AMap.ToolBar({
|
|
visible: true,
|
|
position: {
|
|
top: '200px',
|
|
right: '10px'
|
|
} // 稍微避开定位按钮
|
|
}));
|
|
|
|
// --- 初始化轿车规划 ---
|
|
driving = new AMap.Driving({
|
|
map: map,
|
|
panel: "panel",
|
|
policy: AMap.DrivingPolicy.LEAST_TIME
|
|
});
|
|
|
|
// --- 初始化货车规划 ---
|
|
if (AMap.TruckDriving) {
|
|
truckDriving = new AMap.TruckDriving({
|
|
map: map,
|
|
panel: "panel",
|
|
size: 2,
|
|
policy: 0,
|
|
width: 2.5,
|
|
height: 2,
|
|
load: 1,
|
|
weight: 10,
|
|
axlesNum: 2,
|
|
});
|
|
}
|
|
|
|
// --- 输入提示 ---
|
|
new AMap.AutoComplete({
|
|
input: "startInput"
|
|
});
|
|
new AMap.AutoComplete({
|
|
input: "endInput"
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 核心功能 1: 接收 Flutter 传来的定位数据
|
|
* Flutter 端调用: webViewController.evaluateJavascript("updateMyLocation(...)")
|
|
*/
|
|
function updateMyLocation(lat, lng, angle) {
|
|
var rawLat = parseFloat(lat);
|
|
var rawLng = parseFloat(lng);
|
|
var rawAngle = parseFloat(angle);
|
|
var gps = [rawLng, rawLat];
|
|
|
|
AMap.convertFrom(gps, 'gps', function (status, result) {
|
|
if (result.info === 'ok') {
|
|
var mPoint = result.locations[0];
|
|
currentLng = mPoint.lng;
|
|
currentLat = mPoint.lat;
|
|
var position = [currentLng, currentLat];
|
|
|
|
if (!marker) {
|
|
marker = new AMap.Marker({
|
|
map: map,
|
|
position: position,
|
|
icon: "https://webapi.amap.com/images/car.png",
|
|
offset: new AMap.Pixel(-26, -13),
|
|
autoRotation: true,
|
|
angle: isNaN(rawAngle) ? 0 : rawAngle,
|
|
});
|
|
map.setCenter(position);
|
|
|
|
|
|
} else {
|
|
marker.moveTo(position, {
|
|
duration: 1000,
|
|
autoRotation: true
|
|
});
|
|
if (!isNaN(rawAngle)) marker.setAngle(rawAngle);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 核心功能 2: 点击按钮回到当前位置
|
|
*/
|
|
function backToLocation() {
|
|
if (currentLng && currentLat) {
|
|
map.panTo([currentLng, currentLat]);
|
|
map.setZoom(17);
|
|
console.log("JS: 已回到当前位置");
|
|
} else {
|
|
console.log("JS: 暂无定位数据,向 Flutter 请求...");
|
|
if (window.flutter_inappwebview) {
|
|
window.flutter_inappwebview.callHandler('requestLocation');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 核心功能 3: 路径规划
|
|
*/
|
|
function startRouteSearch() {
|
|
// 获取输入框的文字
|
|
var startKw = document.getElementById('startInput').value;
|
|
var endKw = document.getElementById('endInput').value;
|
|
|
|
if (!startKw) {
|
|
alert("请输入起点");
|
|
return;
|
|
}
|
|
|
|
if (!endKw) {
|
|
alert("请输入终点");
|
|
return;
|
|
}
|
|
|
|
// 清除旧路线
|
|
if (driving) driving.clear();
|
|
|
|
// 收起键盘
|
|
document.getElementById('startInput').blur();
|
|
document.getElementById('endInput').blur();
|
|
|
|
// --- 构造路径规划的点 (使用数组方式,更灵活) ---
|
|
var points = [];
|
|
|
|
// 1. 处理起点逻辑
|
|
// 如果输入框是空的,或者写着 "我的位置",则使用 GPS 坐标
|
|
if (!startKw || startKw === '我的位置') {
|
|
if (!currentLng || !currentLat) {
|
|
// 如果还没获取到定位
|
|
if (window.flutter_inappwebview) {
|
|
window.flutter_inappwebview.callHandler('requestLocation');
|
|
}
|
|
alert("正在获取定位,请稍后...");
|
|
return;
|
|
}
|
|
// 使用精准坐标对象 (避免高德去猜 '我的位置' 关键词)
|
|
points.push({
|
|
keyword: '我的位置', // 用于显示的名字
|
|
location: new AMap.LngLat(currentLng, currentLat) // 实际导航用的坐标
|
|
});
|
|
} else {
|
|
// 如果用户手动输入了地点 (例如 "北京南站")
|
|
// 直接存入关键词,让高德自己去搜
|
|
points.push({
|
|
keyword: startKw
|
|
});
|
|
}
|
|
|
|
// 2. 处理终点逻辑 (通常是关键词)
|
|
points.push({
|
|
keyword: endKw
|
|
});
|
|
|
|
// 3. 发起搜索
|
|
// points 数组里现在是一个起点对象和一个终点对象
|
|
driving.search(points, function (status, result) {
|
|
if (status === 'complete') {
|
|
console.log('JS: 规划成功');
|
|
var panel = document.getElementById('panel');
|
|
panel.style.display = 'block';
|
|
document.body.classList.add('panel-active');
|
|
} else {
|
|
console.log('JS: 规划失败', result);
|
|
alert("规划失败,请检查起终点名称");
|
|
}
|
|
});
|
|
}
|
|
|
|
// 辅助功能:手动绘制路线 (Flutter 可能用到)
|
|
function drawCustomRoute(pointsJsonString) {
|
|
var path = typeof pointsJsonString === 'string' ? JSON.parse(pointsJsonString) : pointsJsonString;
|
|
if (driving) driving.clear();
|
|
var polyline = new AMap.Polyline({
|
|
path: path,
|
|
isOutline: true,
|
|
outlineColor: '#ffeeee',
|
|
borderWeight: 3,
|
|
strokeColor: "#3366FF",
|
|
strokeWeight: 6,
|
|
lineJoin: 'round'
|
|
});
|
|
map.add(polyline);
|
|
map.setFitView([polyline]);
|
|
}
|
|
|
|
window.onload = initMap;
|
|
</script>
|
|
</body>
|
|
|
|
</html> |