Skip to content

Commit

Permalink
FE: [feat] 러닝 실시간 위치 폴리라인 생성 #23
Browse files Browse the repository at this point in the history
  • Loading branch information
hyeona01 committed Nov 25, 2024
1 parent 2b55989 commit 335ad70
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 10 deletions.
31 changes: 30 additions & 1 deletion src/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4",
"xmldom": "^0.6.0"
"xmldom": "^0.6.0",
"zustand": "^5.0.1"
},
"scripts": {
"start": "craco start",
Expand Down
10 changes: 9 additions & 1 deletion src/frontend/src/components/common/AlertModal.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";

const AlertModal = () => {
type AlertModalProps = {
courseId: number;
};

const AlertModal = ({ courseId }: AlertModalProps) => {
const [count, setCount] = useState(5);
const [visible, setVisible] = useState(true);
const navigate = useNavigate();

useEffect(() => {
if (count > 0) {
const timer = setTimeout(() => setCount(count - 1), 1000);
return () => clearTimeout(timer);
} else {
// 코스 미선택시 courseId 0으로 설정
navigate(`/record/${courseId}/running`);
setVisible(false);
}
}, [count]);
Expand Down
16 changes: 10 additions & 6 deletions src/frontend/src/components/common/KakaoMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import axios from "axios";
import { api } from "@/apis";
import { RouteResponse } from "@/types/routes";
import { Route } from "@/types/kakaoMap";
import useCourseStore from "@/store/useCourseStore";

type KakaoMapProps = {
selectedCourse: RouteResponse;
onClickCourse: (id: number) => void;
};

const KakaoMap = ({ selectedCourse, onClickCourse }: KakaoMapProps) => {
const { setCourse } = useCourseStore();

// 현재 위치(마커) 상태
const [current, setCurrent] = useState({
lat: 37.51265,
Expand Down Expand Up @@ -131,19 +134,20 @@ const KakaoMap = ({ selectedCourse, onClickCourse }: KakaoMapProps) => {

const handlePolylineClick = (route: Route) => {
// 폴리라인 중심좌표 이동
const centerLat =
route.path.reduce((sum, point) => sum + point.lat, 0) / route.path.length;
const centerLng =
route.path.reduce((sum, point) => sum + point.lng, 0) / route.path.length;
// const centerLat =
// route.path.reduce((sum, point) => sum + point.lat, 0) / route.path.length;
// const centerLng =
// route.path.reduce((sum, point) => sum + point.lng, 0) / route.path.length;

setState((prev) => ({
...prev,
center: { lat: centerLat, lng: centerLng },
// 폴리라인 시작좌표 이동
center: { lat: route.path[0].lat, lng: route.path[0].lng },
}));

// 선택한 폴리라인 값 업데이트
onClickCourse(route.id);
// setSelectedCourseId(route.id);
setCourse(route.path);
};

return (
Expand Down
7 changes: 6 additions & 1 deletion src/frontend/src/pages/Record/RecordPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import AlertModal from "@/components/common/AlertModal";
import { dummyCourseData } from "@/constants/dummy";
import { RouteResponse } from "@/types/routes";
import { ReactComponent as CourseCTA } from "@/assets/icons/CourseCTA.svg";
import useCourseStore from "@/store/useCourseStore";

const RecordPage = () => {
const { setCourse } = useCourseStore();

const [openSheet, setOpenSheet] = useState(false);
const [confirmModal, setConfirmModal] = useState(false);
const [selectedCategory, setSelectedCategory] = useState<courseMenu>(
Expand Down Expand Up @@ -48,6 +51,7 @@ const RecordPage = () => {
if (selectedCourse) {
setSelectedCourse(selectedCourse); // 선택된 ID 업데이트
setOpenSheet(!openSheet); // Sheet 여부
// setCourse({courseId: selectedCourse.courseId, path: selectedCourse.})
}
}
};
Expand Down Expand Up @@ -154,7 +158,8 @@ const RecordPage = () => {
onClick={() => setConfirmModal(true)}
className="fixed bottom-[20vh] z-50"
/>
{confirmModal && <AlertModal />}
{/* 러닝 시작 모달 */}
{confirmModal && <AlertModal courseId={selectedCourse.courseId} />}
</div>
</>
) : (
Expand Down
78 changes: 78 additions & 0 deletions src/frontend/src/pages/Running/RunningPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { useEffect, useState } from "react";
import { Map, MapMarker, Polyline } from "react-kakao-maps-sdk";
import { LatLng } from "@/types/kakaoMap";
import useCourseStore from "@/store/useCourseStore";

const RunningPage = () => {
const { course } = useCourseStore(); // 선택된 코스 URL
const [current, setCurrent] = useState<LatLng>({
lat: 37.51265,
lng: 127.0919,
}); // 현재 위치
const [state, setState] = useState<LatLng[]>([]); // 실시간 경로 리스트
const [selectedCourse, setSelectedCourse] = useState<LatLng[]>([]); // 선택된 코스 경로

// 실시간 현재 위치 추가
useEffect(() => {
const addLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
const newLocation = {
lat: position.coords.latitude,
lng: position.coords.longitude,
};
setCurrent(newLocation); // 현재 위치 업데이트
setState((prevState) => [...prevState, newLocation]); // 실시간 경로 추가
},
(error) =>
console.error("실시간 위치를 받아오지 못하였습니다.", error)
);
} else {
console.error("Geolocation가 지원되지 않습니다.");
}
};

const intervalId = setInterval(addLocation, 5000); // 5초마다 위치 업데이트

return () => clearInterval(intervalId); // 컴포넌트 언마운트 시 interval 정리
}, []);

return (
<div>
<Map
center={state[state.length - 1] || { lat: 37.51265, lng: 127.0919 }}
style={{
position: "fixed",
top: "0",
left: "0",
width: "100%",
height: "100vh",
}}
level={3}
>
{/* 실시간 경로 */}
<Polyline
path={state}
strokeWeight={6}
strokeColor="#15258E"
strokeOpacity={0.7}
strokeStyle="solid"
/>
{/* 선택한 경로 */}
{course && (
<Polyline
path={course}
strokeWeight={6}
strokeColor={"#E21919"}
strokeOpacity={0.7}
strokeStyle="solid"
/>
)}
<MapMarker position={current} />
</Map>
</div>
);
};

export default RunningPage;
2 changes: 2 additions & 0 deletions src/frontend/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import CrewMemberDetailPage from "@/pages/CrewMemberDetail/CrewMemberDetailPage"
import OtherCrewPage from "@/pages/OtherCrew/OtherCrewPage";
import SigninPage from "@/pages/Signin/SigninPage";
import SignupPage from "@/pages/Signup/SIgnupPage";
import RunningPage from "./pages/Running/RunningPage";

const router = createBrowserRouter([
{
Expand All @@ -23,6 +24,7 @@ const router = createBrowserRouter([
{ path: "/", element: <HomePage /> },

{ path: "/record", element: <RecordPage /> },
{ path: "/record/:id/running", element: <RunningPage /> },

{ path: "/community", element: <CommunityPage /> },
{ path: "/community/:id", element: <CommunityDetailPage /> },
Expand Down
17 changes: 17 additions & 0 deletions src/frontend/src/store/useCourseStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { LatLng } from "@/types/kakaoMap";
import { create } from "zustand";

type CourseStore = {
course: LatLng[] | null;
setCourse: (newCourse: LatLng[]) => void; // 코스 업데이트
};

const useCourseStore = create<CourseStore>()((set) => ({
course: null,
setCourse: (newCourse) =>
set(() => ({
course: newCourse,
})), // 코스 업데이트
}));

export default useCourseStore;
7 changes: 7 additions & 0 deletions src/frontend/src/types/routes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LatLng } from "./kakaoMap";

export type RouteResponse = {
courseId: number;
courseUrl: string;
Expand All @@ -6,3 +8,8 @@ export type RouteResponse = {
name: string;
tags: string[];
};

export type RouteStore = {
courseId: number;
path: LatLng[];
};

0 comments on commit 335ad70

Please sign in to comment.