-
첫번째 게임 prototype구성 - useReducer, useRef, transition/transform 사용Web Dev/5. Projects 2021. 5. 22. 23:26728x90
요새 이런저런거 공부한다고 막상 개발을 못해서 좀 답답했는데, 토요일 10시에 일어났기 때문에 어쩐지 지금 딱 개발하기 딱 좋다.
1. 현재 메인 화면에서 client-side navigation되는 부분이 없어서 이부분을 해줄거다. 첫번째 게임에 대해서만.
2. Tap Tap 동작이 있고, react에서 css animation을 적용할 것.
밤에 만든것
Component 기본 구성
지금 현재 큰 흐름은 이런식이다. index페이지에서 puzzle 컴포넌트로 연결하고, puzzle 페이지는 useReducer를 통해서 상태를 관리한다.
useReducer를 통한 상태 관리
// useReducer를 이용한 state 관리 const STATUS = { LOADING: 0, GAME_INTRO: 1, GAME_STARTED: 2, GAME_CANCELED: 3, GAME_FINISHED: 4, }; function reducer(state, action) { switch (action.type) { case "LOADING": return { status: STATUS["LOADING"] }; case "GAME_INTRO": return { status: STATUS["GAME_INTRO"] }; case "GAME_STARTED": return { status: STATUS["GAME_STARTED"] }; case "GAME_CANCELED": return { status: STATUS["GAME_CANCELED"] }; case "GAME_FINISHED": return { status: STATUS["GAME_FINISHED"] }; default: return state; } } const Puzzle = () => { const router = useRouter(); const [gameStatus, dispatch] = useReducer(reducer, { status: STATUS["GAME_INTRO"], }); const yesHandler = (e: React.TouchEvent | React.MouseEvent) => { dispatch({ type: "GAME_STARTED" }); }; const noHandler = (e: React.TouchEvent | React.MouseEvent) => { e.preventDefault(); dispatch({ type: "GAME_CANCELED" }); router.push("/"); }; ////// more
이렇게 상태를 구성해서 gameStatus를 관리한다.
현재는 데이터를 받아오거나 하는것도 없어서 puzzle페이지에 들어가는 순간 상태는 STATUS["GAME_INTRO"] 이고 이때는 Modal컴포넌트를 렌더링한다. 이때 Modal 컴포넌트는 다른 게임에서도 사용할 수 있도록 Wrapping을 해주는 컴포넌트이고, yesHandler, NoHandler는 아래화면에서 yes, no를 선택했을때 상태를 업데이트 해주는 동작을 처리한다.
useRef를 통해서 Dom Element의 위치를 찾고, transition/transform 적용
Game 컴포넌트는 간단한 애니메이션이 적용된 게임이다.
이렇게 되어있는데, 애기들이 B를 탭하고 느낌표있는 곳을 탭을하면 애니메이션이 동작한다.
이때 해야하는 것은 B와 느낌표 박스의 위치를 먼저 찾는 것이다.
const answerRef = useRef<HTMLButtonElement>(null); // 정답으로 선택 될것 const targetRef = useRef<HTMLButtonElement>(null); // 이동해야하는 곳 useEffect(() => { if (answerRef.current && targetRef.current) { const answer = answerRef.current.getBoundingClientRect(); const target = targetRef.current.getBoundingClientRect(); setPosition({ x: target.left - answer.left, y: target.top - answer.top }); } }, [answerRef, targetRef]);
이때 각 element의 위치는 useRef를 통해서 찾았다.
이렇게 하고나서 정답이 찾아진 순간 B 박스에 transition style을 아래와 같이 추가한다.
if (foundAnswer && selected === index) { return ( <button key={index} className="cursor-pointer w-48 bg-red-50 border-solid border-8 border-red-500 flex justify-around items-center text-9xl font-extrabold text-red-500" onClick={() => setSelected(index)} style={{ ...styles, transform: `translate(${position.x}px, ${position.y}px)`, }} > {op} </button> ); } const styles = { transition: `transform 2s ease-out`, };
이 컴포넌트는 정답을 찾았을때 선택되어있는 박스에 스타일을 추가하는데 기본적인 style은 tailwindCSS를 통해서 적용했는데, transition 동작을 할건데 transform에 대해서만 할 수 있도록 transition: `transform 2s ease-out`, 을 적용했다. 그리고 실제로 어떻게 transform이 동작해야하는지는 inline 스타일에 x, y 좌표값을 계산해서 넘겨주었다.
이렇게 하면 스무스하게 박스에 animation을 처리하는 것을 볼 수 있다.
참고한 자료
https://www.youtube.com/watch?v=ztvNwFV0Ai0
https://webclub.tistory.com/481
https://codingbroker.tistory.com/54
후기
한 다섯시간 정도 삽질했을까.. 간단한 transform, transition관련한 부분을 React 컴포넌트에 어떻게 적용할지 감이 잘안와서 헤맸지만 animation을 잘적용하면 확실히 화면에서 할 수 있는것들이 많아질 것 같다.
'Web Dev > 5. Projects' 카테고리의 다른 글
Jump to definition 기능 확장을 위한 vscode extension 개발 (0) 2021.08.01 [개인 블로그 renewal] Next.js와 client-side navigation, 상태관리(작성중) (0) 2021.05.15 [개인 블로그 renewal] Aws Amplify, App Sync 관련 설정하기 (0) 2021.05.14 [개인 블로그 renewal] 프로젝트 노선 변경 + custom notion (0) 2021.05.14 Next.js - Amplify - Tailwind CSS 세팅하기 (1) 2021.05.13