Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript로 제어하는 스크롤 스냅 구현 #459

Merged
merged 2 commits into from
Sep 20, 2023

Conversation

solo5star
Copy link
Member

#️⃣ 연관된 이슈

close #51
close #294
close #302

📝 작업 내용

  • 기존에 CSS로 구현되어 있던 스크롤 스냅을 JavaScript로 구현하였습니다.
    • touchmove 이벤트와 transition: translateY(*px) 의 조합으로 스크롤을 제어합니다.
  • JavaScript로 제어함으로서, DOM scroll box 모델을 사용함으로서 발생하던 문제들(홈 화면에서 스와이프 시 흰 화면으로 플리커링되는 현상 발생 #51, 빠르게 스크롤 시 가끔 넘어가지 않는 오류 #294)을 해결할 수 있습니다.
  • 제어할 수 있는 범위가 넓어짐으로서 기능 확장을 기대할 수 있습니다.
    • 스크롤 스피드 조절
    • 스크롤 timing function 변경
    • 무한 회전 스크롤
    • 스크롤 애니메이션 변경
  • 터치, 마우스 휠로 스크롤이 가능합니다.
    • 터치패드 스크롤 및 마우스 클릭 드래그 방식은 아직 불완전합니다.

🐼 구현 내용

image

  • FSM(Finite-State Machine) 모델을 도입하여 구현하였습니다.
  • 기능은 ScrollSnapContainer 컴포넌트에 구현되어 있습니다.
  • ScrollSnapContainer는 위의 상태 중 하나만 가질 수 있습니다.
  • 현재 상태에서 어떤 상태로 전이(transition)할 지 정할 수 있습니다.
type MachineState =
  // 아무런 동작도 하지 않는 상태
  | {
      label: 'idle';
    }
  // 터치 방향(좌우 혹은 상하)에 따라 스와이프를 해야할 지 결정하는 상태
  // 이 상태에선 실질적으로 스와이프가 동작하지 않는다
  | {
      label: 'touchdecision';
      originTouchPoint: TouchPoint;
    }
  // 터치의 움직임에 따라 스크롤이 동작하는 상태
  | {
      label: 'touchmove';
      prevPosition: number;
      recentPositionHistory: { timestamp: number; position: number }[];
    }
  // 터치가 끝났고 스크롤이 가장 가까운 스냅 포인트로 이동하는(애니메이션) 상태
  | {
      label: 'snap';
      startedAt: number;
      startedPosition: number;
    };

FSM 상태는 tagged union으로 나타내었으며 상태의 전이는 setState를 통해 할 수 있습니다.

    setMachineState({
      label: 'touchmove',
      prevPosition: position,
      recentPositionHistory: [{ timestamp: Date.now(), position }],
    });

'touchmove' 상태로 전이하는 예시는 위와 같습니다.

image

전체 아이템 갯수와 상관없이 위, 아래 아이템까지만 렌더링하기 때문에 성능에도 긍정적인 영향을 줍니다.

  • 이미지 로딩을 최소한으로 할 수 있게끔 해줍니다.
  • DOM Node 생성을 최소화함으로서 CPU 및 메모리 사용량을 최소화할 수 있습니다.

🥹 향후 구현 필요한 내용

  • 좌, 우 스크롤 구현 예정입니다. (카페 좌, 우 스와이프에 적용 예정)
  • 터치패드 스크롤 및 마우스 클릭 드래그 방식 고치기

💬 리뷰 요구사항

Copy link
Collaborator

@jeongwusi jeongwusi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인 부탁드립니다 ❤️

};

const CafeCard = (props: CardProps) => {
const { cafe, onIntersect } = props;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 Intersection도 필요가 없나보네

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그럼요~~ DOM의 제어로 인해 발생하는 이벤트를 구독할 필요가 없습니다

overflow-y: hidden;
`;

export default ScrollSnapContainer;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호... ScrollSnapContainer 여기가 핵심이군여

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다!!


return (
<Container
{...divProps}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기는 어떤 것이 들어올 것을 예상하고 추가를 하셨나요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<div> 에서 원래 사용하는 props를 그대로 포워딩해준 것입니다!

onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseLeave}
onWheel={handleWheel}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleWheel은 무슨 기능인가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

마우스 휠을 조작하면 발생하는 이벤트입니다

Comment on lines +1 to +3
export const easeOutExpo = (x: number): number => {
return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 친구는 어떤 역할을 하나요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각각의 값들이 무엇을 의미하는지 알 수가 없어서, 더 해석이 힘든 것 같아요!
값들에 적절한 네이밍을 붙여주는 건 어떨까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스니펫이라 그런데 양해 부탁드립니다 😭 이름을 붙이기 보다 easeOutExpo 그 자체로 이해하고 사용하는 것이 좋을 것 같습니다

Copy link
Collaborator

@nuyh99 nuyh99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다🚀
Approve 하겠습니다

Comment on lines +23 to +25
// 터치 방향(좌우 혹은 상하)에 따라 스와이프를 해야할 지 결정하는 상태
// 이 상태에선 실질적으로 스와이프가 동작하지 않는다
| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여쭤볼 게 있습니다!
프론트엔드에서는 코드에 주석을 많이 쓰는 편인가요??

Copy link
Member Author

@solo5star solo5star Sep 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

원래 잘 쓰진 않으나 이 파일에선 핵심적이고 복잡한 로직을 설명하는 것이 중요하기 때문에 의도적으로 주석을 많이 채웠습니다

Copy link
Collaborator

@green-kong green-kong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어렵네요 리액트 ㅠㅠ

Comment on lines +1 to +3
export const easeOutExpo = (x: number): number => {
return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각각의 값들이 무엇을 의미하는지 알 수가 없어서, 더 해석이 힘든 것 같아요!
값들에 적절한 네이밍을 붙여주는 건 어떨까요?

@solo5star
Copy link
Member Author

백엔드 아저씨들도 리뷰해주시는군요 ㅋㅋㅋ 감사합니당

@solo5star solo5star changed the base branch from dev to main September 20, 2023 13:29
Copy link
Collaborator

@jeongwusi jeongwusi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굿굿 확인하였습니다!

@solo5star solo5star merged commit acb36e2 into main Sep 20, 2023
1 check failed
@solo5star solo5star deleted the feat/302-javascript-controlled-swipe branch September 20, 2023 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FE 될 때까지 한다잉 FE:홈화면 홈화면과 관련된 이슈입니다 우선순위:🟠높음 비교적 먼저 작업해야 합니다. 카테고리:기능🛠️ 만들어줘잉 카테고리:리팩토링🪚 리팩토링입니다. 카테고리:최적화🚀 성능 최적화와 연관된 이슈입니다.
Projects
Status: Done
4 participants