From 472420a6fd73455dc28d41544c6b3eda5a16a4b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Jan 2026 22:04:18 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=9E=E3=83=83=E3=83=97=E6=89=8B?= =?UTF-8?q?=E5=8B=95=E6=93=8D=E4=BD=9C=E6=99=82=E3=81=AE=E8=87=AA=E5=8B=95?= =?UTF-8?q?=E8=BF=BD=E5=BE=93=E3=82=92=E7=84=A1=E5=8A=B9=E5=8C=96=E3=81=97?= =?UTF-8?q?=E3=80=8C=E7=8F=BE=E5=9C=A8=E5=9C=B0=E3=81=AB=E6=88=BB=E3=82=8B?= =?UTF-8?q?=E3=80=8D=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ユーザーが地図をパン操作した際に位置更新による自動カメラ移動を 停止し、右下に「現在地に戻る」ボタンを表示するようにした。 https://claude.ai/code/session_014Dfww6cbtt9ZEBh2Ea2REQ --- app/(tabs)/map.tsx | 122 +++++++++++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 32 deletions(-) diff --git a/app/(tabs)/map.tsx b/app/(tabs)/map.tsx index cde46a7..69b94bb 100644 --- a/app/(tabs)/map.tsx +++ b/app/(tabs)/map.tsx @@ -48,6 +48,7 @@ export default function MapScreen() { const [selectedDevices, setSelectedDevices] = useState>(new Set()); const [isFilterExpanded, setIsFilterExpanded] = useState(true); const mapRef = useRef(null); + const [isFollowing, setIsFollowing] = useState(true); // アニメーション用の共有値(開いた状態で初期化) const rotateValue = useSharedValue(180); @@ -73,9 +74,31 @@ export default function MapScreen() { }; }); - // デバイス選択が変わったらカメラ調整 + // 追従モード時のみカメラ調整 useEffect(() => { - if (Platform.OS === "web" || !mapRef.current) return; + if (Platform.OS === "web" || !mapRef.current || !isFollowing) return; + + const allCoords = getAllCoordinates(trajectories); + if (allCoords.length === 0) return; + + mapRef.current.fitToCoordinates(allCoords, { + edgePadding: { top: 50, right: 50, bottom: 50, left: 50 }, + animated: true, + }); + }, [trajectories, isFollowing]); + + // ユーザーが手動で地図を操作したら追従を解除 + const handleMapPanDrag = useCallback(() => { + setIsFollowing(false); + }, []); + + // 現在地に戻るボタン + const handleReturnToCurrentLocation = useCallback(() => { + if (!mapRef.current) return; + if (Platform.OS !== "web") { + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + } + setIsFollowing(true); const allCoords = getAllCoordinates(trajectories); if (allCoords.length === 0) return; @@ -268,36 +291,48 @@ export default function MapScreen() { ) : MapView ? ( - - {trajectories.map((trajectory) => ( - - {Polyline && trajectory.coordinates.length > 1 && ( - - )} - {Marker && trajectory.latestPosition && ( - - )} - - ))} - + + + {trajectories.map((trajectory) => ( + + {Polyline && trajectory.coordinates.length > 1 && ( + + )} + {Marker && trajectory.latestPosition && ( + + )} + + ))} + + {!isFollowing && ( + + 現在地に戻る + + )} + ) : null} @@ -321,7 +356,30 @@ const styles = StyleSheet.create({ flexShrink: 0, marginBottom: 4, }, + mapContainer: { + flex: 1, + position: "relative", + }, map: { flex: 1, }, + returnButton: { + position: "absolute", + bottom: 16, + right: 16, + backgroundColor: "#007AFF", + paddingHorizontal: 16, + paddingVertical: 10, + borderRadius: 24, + shadowColor: "#000", + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5, + }, + returnButtonText: { + color: "#FFFFFF", + fontSize: 14, + fontWeight: "600", + }, });