修正后的地理位置

This commit is contained in:
cwchen 2026-01-14 16:49:02 +08:00
parent 93233e49c4
commit 5f28cfe935
1 changed files with 45 additions and 143 deletions

View File

@ -421,7 +421,6 @@
lastLocationTime: 0, // 上次位置更新时间
lastLocationPoint: null, // 上次位置点,用于计算移动速度
isMoving: false, // 是否在移动状态
screenOrientation: 'landscape', // 屏幕方向:'landscape'(横屏)或 'portrait'(竖屏)
elements: {
panel: null,
tree: null,
@ -712,8 +711,8 @@
point.lat
);
// 位置变化超过1米就更新或者精度提升超过5米降低阈值减少延迟
if (distance > 1 ||
// 位置变化超过2米就更新或者精度提升超过5米
if (distance > 2 ||
(accuracy < this.state.currentLocation.accuracy - 5) ||
(accuracy < 50 && this.state.currentLocation.accuracy >= 50)) {
shouldUpdate = true;
@ -789,8 +788,8 @@
point.lat
);
// 位置变化超过1米就更新或者精度提升超过5米降低阈值减少延迟
if (distance > 1 ||
// 位置变化超过2米就更新或者精度提升超过5米
if (distance > 2 ||
(accuracy < this.state.currentLocation.accuracy - 5) ||
(accuracy < 50 && this.state.currentLocation.accuracy >= 50)) {
shouldUpdate = true;
@ -837,7 +836,7 @@
enableHighAccuracy: true, // 使用GPS精确定位
timeout: 6000 // 6秒超时
});
}, 300); // 每0.3秒更新一次,提高实时性和灵敏度,减少延迟
}, 500); // 每0.5秒更新一次,提高实时性和灵敏度
// 保存geolocation实例以便后续使用
this.baiduGeolocation = geolocation;
@ -1350,20 +1349,17 @@
return;
}
// 移动时立即更新位置减少延迟静止时使用requestAnimationFrame平滑更新
if (this.isMoving) {
// 移动时直接更新不使用requestAnimationFrame延迟
this._doUpdateLocationMarker();
} else {
// 静止时使用requestAnimationFrame平滑更新
if (this.updateMarkerTimer) {
cancelAnimationFrame(this.updateMarkerTimer);
}
this.updateMarkerTimer = requestAnimationFrame(() => {
this.updateMarkerTimer = null;
this._doUpdateLocationMarker();
});
// 如果已经有待处理的更新,取消它,重新调度(确保使用最新数据)
if (this.updateMarkerTimer) {
cancelAnimationFrame(this.updateMarkerTimer);
}
// 使用requestAnimationFrame实现平滑更新保持实时性
// 对于位置更新,立即执行;对于方向更新,异步执行
this.updateMarkerTimer = requestAnimationFrame(() => {
this.updateMarkerTimer = null;
this._doUpdateLocationMarker();
});
},
/**
@ -1378,8 +1374,7 @@
try {
// 优先更新位置(立即执行,不等待方向更新)
if (typeof this.overlays.locationMarker.setPosition === 'function') {
// 直接更新位置,实时跟随移动
// 移动时立即更新静止时也可以立即更新已通过updateLocationMarker控制
// 直接更新位置,实时跟随移动(不检查距离,每次都更新以确保平滑)
this.overlays.locationMarker.setPosition(point);
} else {
// 如果不支持setPosition检查位置是否变化降低阈值提高灵敏度
@ -1397,16 +1392,16 @@
}
// 实时更新方向使用角度差值计算考虑0-360度边界
// 统一使用1度阈值确保方向更新及时准确
const headingThreshold = 1;
// 移动时提高阈值2度减少抖动静止时降低阈值1度保持响应性
const headingThreshold = this.isMoving ? 2 : 1;
const headingChanged = !this.lastHeading || Math.abs(this.angleDifference(this.state.currentHeading, this.lastHeading)) >= headingThreshold;
if (headingChanged) {
const oldHeading = this.lastHeading;
this.lastHeading = this.state.currentHeading;
// 移动时立即更新图标减少延迟静止时使用requestAnimationFrame平滑更新
const updateIcon = () => {
// 异步更新图标使用requestAnimationFrame确保平滑不阻塞位置更新
requestAnimationFrame(() => {
this.createRotatedLocationIcon(this.state.currentHeading).then((icon) => {
if (this.overlays.locationMarker) {
// 优先使用setIcon更新不删除重建
@ -1425,15 +1420,7 @@
// 恢复旧的方向值
this.lastHeading = oldHeading;
});
};
if (this.isMoving) {
// 移动时立即更新不使用requestAnimationFrame延迟
updateIcon();
} else {
// 静止时使用requestAnimationFrame平滑更新
requestAnimationFrame(updateIcon);
}
});
}
} catch (e) {
// 如果更新失败,重新创建标记
@ -1562,24 +1549,18 @@
// 转换公式:画布角度 = 90度 - 地图角度因为画布的0度对应地图的90度
// 应用方向偏移量(用于校正方向偏差)
let adjustedHeading = this.normalizeHeading(heading + (this.headingOffset || 0));
// 根据屏幕方向进一步调整(如果需要)
// 注意方向校正已经在handleOrientation中处理这里通常不需要再次调整
// 但如果图标旋转仍有问题,可以在这里添加额外的校正
const adjustedHeading = this.normalizeHeading(heading + (this.headingOffset || 0));
// 转换为画布旋转角度
// 标准转换画布的0度向右对应地图的90度正东
// 所以:画布角度 = 90度 - 地图角度
// 根据屏幕方向调整旋转角度
let rotation = (90 - adjustedHeading) * Math.PI / 180;
// 如果横屏和竖屏方向表现不同,可以在这里进一步调整
// 例如竖屏时可能需要额外的180度反转
// if (this.screenOrientation === 'portrait') {
// rotation = (270 - adjustedHeading) * Math.PI / 180;
// }
// 如果方向仍然不对,可以尝试以下方案(取消注释):
// 方案1反转180度
// rotation = (270 - adjustedHeading) * Math.PI / 180;
// 方案2直接使用heading如果图标本身已经指向正确方向
// rotation = -adjustedHeading * Math.PI / 180;
ctx.rotate(rotation);
@ -1606,18 +1587,6 @@
return heading;
},
/**
* 检测屏幕方向
*/
detectScreenOrientation() {
const width = window.innerWidth || window.screen.width;
const height = window.innerHeight || window.screen.height;
// 如果宽度大于高度,认为是横屏;否则是竖屏
this.screenOrientation = width > height ? 'landscape' : 'portrait';
console.log('屏幕方向:', this.screenOrientation, `(${width}x${height})`);
return this.screenOrientation;
},
/**
* 计算两个角度之间的最短差值考虑0-360度边界
*/
@ -1628,20 +1597,6 @@
return diff;
},
/**
* 判断方向是否为南北方向(需要反转)
* 南北方向0度正北、180度正南以及0-45度、135-225度、315-360度范围
* 东西方向90度正东、270度正西以及45-135度、225-315度范围
*/
isNorthSouthDirection(heading) {
// 归一化到0-360度
const normalized = this.normalizeHeading(heading);
// 南北方向0-45度、135-225度、315-360度
return (normalized >= 0 && normalized <= 45) ||
(normalized >= 135 && normalized <= 225) ||
(normalized >= 315 && normalized <= 360);
},
/**
* 移动平均滤波(减少抖动)
* 移动时使用更强的平滑,静止时使用较弱的平滑
@ -1653,8 +1608,8 @@
}
// 根据移动状态动态调整历史记录大小
// 移动时使用较少样本3个减少延迟静止时使用较多样本5个保持平滑
const targetHistorySize = this.isMoving ? 3 : 5;
// 移动时使用更多样本10个进行更强的平滑静止时使用较少样本5个保持响应性
const targetHistorySize = this.isMoving ? 10 : 5;
if (!this.headingHistorySize) {
this.headingHistorySize = targetHistorySize;
} else {
@ -1669,7 +1624,7 @@
}
// 如果历史记录不足,直接返回原始值
const minHistorySize = this.isMoving ? 1 : 2; // 移动时减少样本要求,降低延迟,提高响应性
const minHistorySize = this.isMoving ? 5 : 3; // 移动时需要更多样本
if (this.headingHistory.length < minHistorySize) {
return rawHeading;
}
@ -1725,22 +1680,6 @@
this.headingHistory = [];
this.lastOrientationUpdateTime = 0;
// 检测屏幕方向
this.detectScreenOrientation();
// 监听屏幕方向变化
if (window.addEventListener) {
window.addEventListener('resize', () => {
this.detectScreenOrientation();
});
// 监听orientationchange事件移动设备
window.addEventListener('orientationchange', () => {
setTimeout(() => {
this.detectScreenOrientation();
}, 100); // 延迟100ms确保方向已改变
});
}
console.log('开始监听设备方向(优先使用旋转矢量传感器)...');
// 1. 优先尝试使用旋转矢量传感器通过DeviceOrientationEvent的quaternion
@ -1849,22 +1788,17 @@
const oldHeading = this.state.currentHeading;
const diff = Math.abs(this.angleDifference(newHeading, oldHeading));
// 统一使用1度阈值确保方向更新及时准确
const threshold = 1;
// 移动时提高阈值2度减少抖动静止时降低阈值1度保持响应性
const threshold = this.isMoving ? 2 : 1;
if (diff >= threshold || oldHeading === 0) {
this.state.currentHeading = newHeading;
console.log('方向更新:', newHeading.toFixed(1), '度', '变化:', diff.toFixed(1), '度', this.isMoving ? '(移动中)' : '(静止)');
if (this.state.currentLocation) {
// 移动时立即更新静止时使用requestAnimationFrame
if (this.isMoving) {
this.updateLocationMarker(); // 移动时直接调用已在updateLocationMarker中处理
} else {
requestAnimationFrame(() => {
this.updateLocationMarker();
});
}
requestAnimationFrame(() => {
this.updateLocationMarker();
});
}
}
},
@ -1933,10 +1867,15 @@
else if (event.alpha !== null && event.alpha !== undefined && !isNaN(event.alpha)) {
// event.alpha 是绕Z轴旋转的角度0-360度
// 在某些设备上alpha是逆时针的需要转换为顺时针
// 根据实际测试Android设备通常需要反转
// 尝试反转360 - alpha
rawHeading = (360 - event.alpha) % 360;
console.log('使用传统传感器(alpha):', event.alpha.toFixed(1), '度 ->', rawHeading.toFixed(1), '度');
// 根据设备类型调整Android通常需要反转iOS可能不需要
// 先尝试不反转
rawHeading = event.alpha;
// 如果方向反了,可以尝试反转(取消下面的注释):
// rawHeading = (360 - event.alpha) % 360;
// 或者使用headingOffset来校正更灵活
// this.headingOffset = 180; // 在初始化时设置
}
// 4. iOS Safari 使用 webkitCompassHeading
else if (event.webkitCompassHeading !== null && event.webkitCompassHeading !== undefined) {
@ -1948,45 +1887,8 @@
}
if (rawHeading !== null && !isNaN(rawHeading)) {
// 根据屏幕方向和移动状态分别校正方向角度
let correctedHeading = rawHeading;
const isPortrait = this.screenOrientation === 'portrait';
const isLandscape = this.screenOrientation === 'landscape';
// 横屏 + 移动需要反转180度
if (this.isMoving && isLandscape) {
correctedHeading = (rawHeading + 180) % 360;
console.log('横屏+移动时方向反转:', rawHeading.toFixed(1), '度 ->', correctedHeading.toFixed(1), '度');
}
// 横屏 + 静止:保持原始方向或需要特定校正
else if (!this.isMoving && isLandscape) {
// 横屏静止时可能需要加180度或保持原样根据实际测试调整
// correctedHeading = (rawHeading + 180) % 360; // 如果需要反转,取消注释
correctedHeading = rawHeading; // 暂时保持原样
console.log('横屏+静止时方向:', rawHeading.toFixed(1), '度 ->', correctedHeading.toFixed(1), '度');
}
// 竖屏 + 移动:根据移动方向判断是否需要反转
// 东西方向90度、270度时方向是准的不需要反转
// 南北方向0度、180度时方向是反的需要反转180度
else if (this.isMoving && isPortrait) {
if (this.isNorthSouthDirection(rawHeading)) {
// 南北方向需要反转180度
correctedHeading = (rawHeading + 180) % 360;
console.log('竖屏+移动(南北方向)时方向反转:', rawHeading.toFixed(1), '度 ->', correctedHeading.toFixed(1), '度');
} else {
// 东西方向,保持原样
correctedHeading = rawHeading;
console.log('竖屏+移动(东西方向)时方向保持:', rawHeading.toFixed(1), '度');
}
}
// 竖屏 + 静止需要反转180度
else if (!this.isMoving && isPortrait) {
correctedHeading = (rawHeading + 180) % 360;
console.log('竖屏+静止时方向反转:', rawHeading.toFixed(1), '度 ->', correctedHeading.toFixed(1), '度');
}
// 使用移动平均滤波平滑方向数据(减少抖动)
const smoothedHeading = this.smoothHeading(correctedHeading);
const smoothedHeading = this.smoothHeading(rawHeading);
// 更新方向(如果变化足够大)
this.updateHeadingIfChanged(smoothedHeading);