import Taro from '@tarojs/taro';
|
import { DEFAULT_LATITUDE, DEFAULT_LONGITUDE } from '../../../../robotaxi/constants/map';
|
import {
|
HOME_START_SITE_SOURCE_MANUAL_SELECTED,
|
writeHomeStartSiteDisplayState
|
} from '../../../../robotaxi/hooks/use-home-start-site-display';
|
|
type Deps = {
|
mapState: any;
|
siteState: any;
|
toOrigin: () => void;
|
getMarkerCommitDelayMs?: () => number;
|
onRegionBegin?: () => void;
|
onRegionSettled?: (payload: { lat: number; lon: number; isInside: boolean }) => void;
|
onScaleChange?: (scale: number) => void;
|
};
|
|
/**
|
* 地图 region-change 编排:处理地图拖拽/缩放后的吸附、回填和自动归位。
|
*/
|
export default function useRegionChange({ mapState, siteState, toOrigin, getMarkerCommitDelayMs, onRegionBegin, onRegionSettled, onScaleChange }: Deps) {
|
/**
|
* 查询完成后提交站点 marker:命中吸附时立即提交,未吸附时避让车辆动画窗口再提交。
|
*/
|
const commitQueriedMarkers = (newMarkers: any[], isInside: boolean, adsorbed: boolean) => {
|
if (!isInside || adsorbed) {
|
mapState.commitMarkers(newMarkers);
|
return;
|
}
|
const delayMs = typeof getMarkerCommitDelayMs === 'function' ? getMarkerCommitDelayMs() : 0;
|
mapState.scheduleMarkerCommit(newMarkers, delayMs);
|
};
|
|
/**
|
* 将用户显式换点后的吸附结果同步为首页确认上车点。
|
*/
|
const syncManualSelectedStartSite = (site: any) => {
|
if (!site?.latitude || !site?.longitude) {
|
return;
|
}
|
writeHomeStartSiteDisplayState(site, HOME_START_SITE_SOURCE_MANUAL_SELECTED);
|
};
|
|
/**
|
* 按最终落点回传地图结算结果,保证场站围栏与吸附后的中心点一致。
|
*/
|
const settleRegionResult = (payload: { lat: number; lon: number; isInside: boolean }) => {
|
if (typeof onRegionSettled === 'function') {
|
onRegionSettled(payload);
|
}
|
};
|
|
/**
|
* 通过中心点主动触发吸附判断。
|
* @param options.allowAdsorb 是否允许吸附到最近点位,默认允许;查看运营区域等仅浏览场景需传 false 以保持视野不被吸附打断。
|
*/
|
const queryPointerByCenter = async (lat: number, lng: number, options?: { allowAdsorb?: boolean }) => {
|
const allowAdsorb = options?.allowAdsorb !== false;
|
siteState.setBounce(true);
|
siteState.setShowToolTip(false);
|
try {
|
const { isInside, newMarkers, sites } = await mapState.updateMapData(lat, lng, undefined, { commitMarkers: false });
|
if (!isInside) {
|
siteState.clearCachedStartSiteSelection();
|
commitQueriedMarkers(newMarkers, false, false);
|
settleRegionResult({ lat, lon: lng, isInside: false });
|
siteState.setIsAdsorbed(false);
|
if (allowAdsorb) {
|
siteState.setShowToolTip(true); // 运营区外恢复气泡提示
|
}
|
siteState.setBounce(false);
|
return;
|
}
|
const nearestCandidates = sites.length > 0 ? sites : newMarkers;
|
const { adsorbed, marker } = allowAdsorb
|
? siteState.tryAdsorb(lat, lng, nearestCandidates)
|
: { adsorbed: false, marker: null }; // 仅浏览运营区域时不吸附最近点位
|
commitQueriedMarkers(newMarkers, true, adsorbed);
|
const finalLat = adsorbed && marker ? marker.latitude : lat; // 最终纬度
|
const finalLng = adsorbed && marker ? marker.longitude : lng; // 最终经度
|
if (allowAdsorb) {
|
// 仅浏览运营区域时不回写地图中心,避免 Map 组件按旧 scale 重绘导致刚展开的运营区视野被收回
|
mapState.setLatitude(finalLat);
|
mapState.setLongitude(finalLng);
|
}
|
settleRegionResult({ lat: finalLat, lon: finalLng, isInside: true });
|
setTimeout(async () => {
|
siteState.setBounce(false);
|
if (!adsorbed) {
|
await siteState.resolveAddress(lat, lng);
|
}
|
if (!allowAdsorb) {
|
siteState.setShowToolTip(false); // 查看运营区域期间隐藏气泡,避免遮挡围栏,拖动地图后自动恢复
|
}
|
}, 600);
|
} catch (error) {
|
siteState.setBounce(false);
|
}
|
};
|
|
const handleRegionEnd = (causedBy: string, wasReturningToOrigin: boolean) => {
|
siteState.setBounce(true);
|
const mapCtx = Taro.createMapContext('map');
|
mapCtx.getScale({
|
success: (res) => {
|
if (res.scale <= 3 || res.scale >= 20) {
|
if (!mapState.resetTimer.current) {
|
mapState.setMapScale(res.scale);
|
mapState.resetTimer.current = setTimeout(() => {
|
toOrigin();
|
}, 6000);
|
}
|
}
|
if (typeof onScaleChange === 'function') {
|
onScaleChange(res.scale);
|
}
|
}
|
});
|
mapCtx.getCenterLocation({
|
success: async (res) => {
|
const { latitude: lat, longitude: lng } = res;
|
if (
|
!lat ||
|
!lng ||
|
(Math.abs(lat - DEFAULT_LATITUDE) < 0.0001 && Math.abs(lng - DEFAULT_LONGITUDE) < 0.0001)
|
) {
|
siteState.setBounce(false);
|
return;
|
}
|
|
if (causedBy === 'gesture' || causedBy === 'drag' || causedBy === 'scale' || wasReturningToOrigin) {
|
// 只有发生显著位移时才同步经纬度,避免微小抖动引发的二次重绘
|
if (Math.abs(mapState.latitude - lat) > 0.0001 || Math.abs(mapState.longitude - lng) > 0.0001) {
|
mapState.setLatitude(lat);
|
mapState.setLongitude(lng);
|
}
|
}
|
|
try {
|
const { isInside, newMarkers, sites } = await mapState.updateMapData(lat, lng, undefined, { commitMarkers: false });
|
if (!isInside) {
|
siteState.clearCachedStartSiteSelection();
|
commitQueriedMarkers(newMarkers, false, false);
|
settleRegionResult({ lat, lon: lng, isInside: false });
|
siteState.setIsAdsorbed(false);
|
siteState.setShowToolTip(true); // 运营区外恢复气泡提示
|
siteState.setBounce(false);
|
return;
|
}
|
const nearestCandidates = sites.length > 0 ? sites : newMarkers;
|
const { adsorbed, marker } = siteState.tryAdsorb(lat, lng, nearestCandidates);
|
commitQueriedMarkers(newMarkers, true, adsorbed);
|
const finalLat = adsorbed && marker ? marker.latitude : lat; // 最终纬度
|
const finalLng = adsorbed && marker ? marker.longitude : lng; // 最终经度
|
if (adsorbed && marker && (causedBy === 'gesture' || causedBy === 'drag' || wasReturningToOrigin)) {
|
syncManualSelectedStartSite(marker);
|
}
|
settleRegionResult({ lat: finalLat, lon: finalLng, isInside: true });
|
if ((causedBy === 'gesture' || causedBy === 'drag' || wasReturningToOrigin) && marker) {
|
mapCtx.moveToLocation({
|
latitude: marker.latitude,
|
longitude: marker.longitude
|
});
|
mapState.setLatitude(marker.latitude);
|
mapState.setLongitude(marker.longitude);
|
}
|
setTimeout(async () => {
|
siteState.setBounce(false);
|
if (!adsorbed) {
|
await siteState.resolveAddress(lat, lng);
|
}
|
}, 600);
|
} catch (error) {
|
siteState.setBounce(false);
|
}
|
}
|
});
|
};
|
|
/**
|
* region-change 事件入口。
|
*/
|
const handleRegionChange = (e: any) => {
|
const type = e?.type || e?.detail?.type;
|
const causedBy = e?.causedBy || e?.detail?.causedBy;
|
if (mapState.isLocationAuthDenied) return;
|
if (causedBy === 'gesture' || causedBy === 'drag' || causedBy === 'scale') {
|
mapState.shouldIgnoreRegionChange.current = false;
|
if (mapState.resetTimer.current) {
|
clearTimeout(mapState.resetTimer.current);
|
mapState.resetTimer.current = null;
|
}
|
} else if (mapState.isReturningToOrigin.current) {
|
mapState.shouldIgnoreRegionChange.current = false;
|
} else {
|
mapState.shouldIgnoreRegionChange.current = true;
|
return;
|
}
|
if (type === 'begin') {
|
siteState.setShowToolTip(false);
|
if (typeof onRegionBegin === 'function') {
|
onRegionBegin();
|
}
|
return;
|
}
|
if (type !== 'end') return;
|
const wasReturningToOrigin = mapState.isReturningToOrigin.current;
|
if (mapState.isReturningToOrigin.current) {
|
mapState.isReturningToOrigin.current = false;
|
}
|
if (mapState.shouldIgnoreRegionChange.current) {
|
mapState.shouldIgnoreRegionChange.current = false;
|
return;
|
}
|
handleRegionEnd(causedBy, wasReturningToOrigin);
|
};
|
|
return { handleRegionChange, queryPointerByCenter };
|
}
|