From d2aced396f3a692e96468ad8f092373b7d5e5b96 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Thu, 25 Jun 2026 16:48:26 +0700 Subject: [PATCH] =?UTF-8?q?fix:=20b=E1=BA=A3n=20=C4=91=E1=BB=93=20xoay=20m?= =?UTF-8?q?=C6=B0=E1=BB=A3t=20tr=C3=AAn=20Iphone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/TourDetailPage.tsx | 156 +++++++++++++++++++------- 1 file changed, 113 insertions(+), 43 deletions(-) diff --git a/frontend/src/pages/TourDetailPage.tsx b/frontend/src/pages/TourDetailPage.tsx index 9ba355e..fd27cf8 100644 --- a/frontend/src/pages/TourDetailPage.tsx +++ b/frontend/src/pages/TourDetailPage.tsx @@ -222,7 +222,7 @@ const MapRotationHandler = ({ rotation }: { rotation: number }) => { // 1. Dùng translateZ(0) để kích hoạt GPU trên iOS. // 2. Giảm scale xuống ~1.6 (vừa đủ che góc) để giảm tải cho bộ nhớ đệm đồ họa. container.style.transform = `rotate(${cumulativeRotationRef.current}deg) scale(2.2) translateZ(0)`; - container.style.transition = 'transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1)'; + container.style.transition = 'transform 0.15s ease-out'; }, [rotation, map]); return null; }; @@ -867,6 +867,8 @@ export const TourDetailPage = ({ // Theo dõi hướng thiết bị (la bàn) useEffect(() => { + let lastHeading: number | null = null; + const handleOrientation = (event: any) => { let heading: number | null = null; @@ -876,17 +878,17 @@ export const TourDetailPage = ({ } // 2. Đối với Android (Chrome): Cần kiểm tra tính tuyệt đối của dữ liệu else if (event.alpha !== null && event.alpha !== undefined) { - // Chrome trên Android chỉ cung cấp hướng la bàn chuẩn khi event.absolute là true - // hoặc khi nhận từ sự kiện 'deviceorientationabsolute' if (event.absolute === true || event.type === 'deviceorientationabsolute') { - // Alpha trên Android tăng theo chiều ngược kim đồng hồ (0=North, 90=West) - // Cần chuyển đổi sang chiều kim đồng hồ để khớp với logic quay bản đồ heading = (360 - event.alpha) % 360; } } if (heading !== null) { - setDeviceOrientationHeading(heading); + // Chỉ cập nhật nếu hướng thay đổi lớn hơn 2 độ để giảm tải vẽ lại + if (lastHeading === null || Math.abs(heading - lastHeading) > 2) { + lastHeading = heading; + setDeviceOrientationHeading(heading); + } } }; @@ -1242,6 +1244,31 @@ export const TourDetailPage = ({ const rejectJoinRequest = useTourStore(state => state.rejectJoinRequest); const deleteTour = useTourStore(state => state.deleteTour); + const toggleCompassMode = async () => { + if (!isHeadingMode) { + if ( + typeof DeviceOrientationEvent !== 'undefined' && + typeof (DeviceOrientationEvent as any).requestPermission === 'function' + ) { + try { + const permissionState = await (DeviceOrientationEvent as any).requestPermission(); + if (permissionState === 'granted') { + setIsHeadingMode(true); + } else { + alert("Để xoay bản đồ theo hướng di chuyển, vui lòng cấp quyền truy cập cảm biến hướng (La bàn)."); + } + } catch (error) { + console.error("Error requesting compass permission:", error); + setIsHeadingMode(true); + } + } else { + setIsHeadingMode(true); + } + } else { + setIsHeadingMode(false); + setMapRotation(0); + } + }; // Khôi phục vị trí và mức zoom từ localStorage const [initialViewState] = useState(() => { @@ -1905,7 +1932,9 @@ export const TourDetailPage = ({ )} - {/* Tour Header Info */} + {!(activeTab === 'plan' && viewMode === 'map') && ( + <> + {/* Tour Header Info */}
)}
+ + )} {/* Main Content Area - Điều chỉnh max-width khi ở chế độ bản đồ */} -
+
{/* Tab Switcher */} -
+ {!(activeTab === 'plan' && viewMode === 'map') && ( +
{tabs.map((tab) => (
+ )} {/* Tab Panels */}
{activeTab === 'plan' && (
{/* View Mode Toggle */} -
-
- - + {viewMode !== 'map' && ( +
+
+ + +
-
+ )} {/* Export Buttons */} -
- - -
+ {viewMode !== 'map' && ( +
+ + +
+ )} {viewMode === 'timeline' ? ( { @@ -2259,7 +2296,7 @@ export const TourDetailPage = ({ onOpenNavigationPage={onOpenNavigationPage} /> ) : ( -
+
{/* Search Bar Overlay - Thanh tìm kiếm địa điểm trên bản đồ lộ trình */} {!isPublicView && (
@@ -2485,6 +2522,19 @@ export const TourDetailPage = ({ + {/* Floating Compass Button */} + + {/* Floating Locate User Button */} + + {/* Floating Back to Timeline Button */} + {/* Menu ngữ cảnh khi click chuột phải vào con đường */} {routeMenu && ( @@ -3194,7 +3253,7 @@ export const TourDetailPage = ({
{/* Floating Action Button (Mobile) */} - {((activeTab === 'plan' && canEdit) || (activeTab === 'photo' && canUploadPhoto)) && !isPublicView && ( + {((activeTab === 'plan' && viewMode === 'timeline' && canEdit) || (activeTab === 'photo' && canUploadPhoto)) && !isPublicView && (
+ {/* Floating Locate User Button (Fullscreen) */}