-
Notifications
You must be signed in to change notification settings - Fork 2
[ Feat ] DetailSection 애니메이션 구현 #19
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
Conversation
… feat/add-full-animation/#2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정말 어려운 작업이군요.. 고생많으셨습니다!
data-order를 설정해서 텍스트가 뷰포트안에 들어올 때만 해당 이미지의 opacity를 1로 설정해서 보이게끔 하셨군요 굿굿!
detail section에서 grid-cols-12 를 설정한 후 자식 요소에게 col-span을 6만큼 설정해주셨는데요
제 생각에는 컬럼을 2개로 나누고 각각 차지하게끔 하면되지 않을까 싶은데, 12개 컬럼으로 나눈 다른 이유가 있을지 궁금해요!
| let isActiveSection = false; | ||
|
|
||
| sections.forEach((section) => { | ||
| const rect = section.getBoundingClientRect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getBoundingClientReact라는 메서드를 처음봤네요... 뷰포트의 상대적인 위치를 계산한다고해요
rect는 TextBlock들이 반환하는 위치의 배열인가요?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getBoundingClientRect()는 특정 요소의 현재 뷰포트를 기준으로 한 위치와 크기 정보를 가져오는 메서드에요
여기서 rect는 TextBlock 요소들이 뷰포트에서 차지하는 위치와 크기를 담고 있는 객체라고 할수있습니다
rect.top → 요소의 상단이 뷰포트의 최상단으로부터 얼마나 떨어져 있는지
rect.height → 요소의 높이
등등
이 커스텀 훅은 사용자가 스크롤을 내릴 때, 현재 화면에서 중앙에 위치한 섹션(TextBlock)을 감지하여 해당 섹션의 data-order 값을 activeSection으로 업데이트하는 역할을 하고있어요. 즉, getBoundingClientRect()를 이용해 각 섹션(TextBlock)이 화면에서 어디에 위치하는지 계산하고, 화면 중앙에 걸친 섹션을 찾을 때 사용하고 있답니다
| const sectionBottom = sectionTop + rect.height; | ||
|
|
||
| if (viewportMiddle >= sectionTop && viewportMiddle <= sectionBottom) { | ||
| const index = parseInt(section.getAttribute('data-order') || '0', 10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parseInt 2번째 인자에 10을 작성하지 않으면 값이 10진수가 아니게 될수도 있나요?! 단순 궁금증
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
명시해주는게 좋다곤 합니다!!
src/components/DetailSection.tsx
Outdated
| </div> | ||
| ))} | ||
| </div> | ||
| <div className="right-0 top-[7.7rem] col-span-6 col-start-7 w-full md:sticky md:h-[calc(100dvh-7.7rem)]"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
md일 때 sticky와 height를 따로 설정해주셨는데 어떤 역할을 하는지 궁금해요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
md는 제가 임의로 자연스러워보이는 변경 포인트로 설정한 기준점이고, 화면 크기가 md 이상일 때 우측 이미지를 sticky로 고정하고 height을 calc(100dvh-7.7rem)로 설정해서 뷰포트에서 헤더 높이(7.7rem)를 제외한 나머지 부분을 꽉 채우도록 설정한 것입니다.
sticky가 적용된 상태에서 스크롤을 내려도, 화면 높이만큼 영역을 유지하며 이미지가 교체될 수 있도록 한 것이에여
화면 크기가 md 이하일 때는 텍스트와 gif가 세로로 교차되어 나열되도록 해서 sticky나 height을 지정하지 않도록 한것입니다.
package.json
Outdated
| "framer-motion": "^12.4.10", | ||
| "motion": "^12.4.7", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앗.. import { motion } from 'framer-motion'하려니까 에러떠서 안깔려있는줄 알고 그냥 framer-motion으로 설치했는데 다른 패키지였군요!!
저거 찾아보니까 프레이머에서 framer-motion의 새롭게 출시한 후속 패키지처럼 보여요. 삭제 후 motion으로 통일해놓겠습니다..!!
Ivoryeee
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
프레이머 사용에 익숙치 않으셨을 텐데도 뚝딱뚝딱 잘 구현해 주셨네요 작업하느라 수고 많으셨습니다!
src/components/DetailSection.tsx
Outdated
| <MediaBlock imgSrc={contents.imgSrc} imgDescription={contents.imgDescription} /> | ||
| </section> | ||
| <div className="flex w-full items-center justify-center"> | ||
| <div className="w-full md:grid md:grid-cols-12"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저번에 반응형 고려하실 때 대부분의 단위를 sm으로 잡으셨던 걸로 기억하는데, md로 변경하신 이유가 궁금합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sm으로 하면 변경 지점의 간격이 너무 커서, 좀 더 자연스러운 변경 포인트로 제가 임의로 설정했습니다!
src/components/DetailSection.tsx
Outdated
| alt={content.imgDescription} | ||
| width={1073} | ||
| height={789} | ||
| className="rounded-[2rem]" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
엇 모바일용 이미지도 width가 1073으로 들어가나요? 피그마에서 확인해 보니 모바일용 미디어는 327x241 인 것 같던데, 데스크탑과 동일한 크기로 넣으신 이유가 있는지 궁금해요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
데스크탑용 크기를 넣어도 창 크기가 줄어들면 비율에 맞게 자연스럽게 축소되는데, 모바일용 크기를 지정해버리니까 창을 조금만 줄여도 바로 모바일 사이즈로 확 바뀌어서요,,,!
KIMGEONHWI
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쉽지 않은 작업이었을 것 같은데 고생많으셨습니다!!
src/hooks/useActiveSection.ts
Outdated
| const sectionTop = window.scrollY + rect.top; | ||
| const sectionBottom = sectionTop + rect.height; | ||
|
|
||
| if (viewportMiddle >= sectionTop && viewportMiddle <= sectionBottom) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 sectionTop = window.scrollY + rect.top;와 const sectionBottom = sectionTop + rect.height;으로 sectionTop과 sectionBottom을 정의해서 viewportMiddle과 비교연산을 거치도록 로직이 되어있습니다. 그러나, const viewportMiddle = window.scrollY + window.innerHeight / 2;를 const viewportMiddle = window.innerHeight / 2;와 같이 getBoundingClientRect()는 뷰포트 기준 좌표를 반환하기 때문에 window.innerHeight / 2를 그대로 사용하고, if (rect.top <= viewportMiddle && rect.bottom >= viewportMiddle)으로 if문을 수정해서 사용하면 코드가 더 간소화 될 것 같은데 window.scrollY를 더해준 이유가 있을까요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 코드에서 window.scrollY를 더한 이유는 스크롤 위치를 반영해서, 요소가 페이지 전체에서 어느 정도 위치해 있는지를 계산하려고 한 것입니다. 하지만 viewportMiddle도 뷰포트 기준으로 계산된 값이라, 같은 기준에서 비교하려면 window.scrollY를 더할 필요가 없을 수도 있겠네요 한번 시도해보겠습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
참고로, 제가 테스트할 때는 문제 없었습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
덕분에 코드가 훨씬 간소화됐네요! 반영했습니다~!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KIMGEONHWI 건휘님 최고네요. 코드 이해가 훨신 더 쉬워졌어요 🚀🚀🚀
음 그래두될것 같긴한데 지금 방식을 유지하는게 더 정밀하게 레이아웃을 컨트롤할 수 있을 것 같습니다. 사실 flex로 정렬했을때 도저히 옆에 고정이 안되길래 typed 사이트 구현 방식 따라해보느라 12로 설정해본거긴 해요,,ㅋㅋ |
- TextBlock 컴포넌트 삭제, DetailContents 컴포넌트 생성하여 텍스트와 모바일용 이미지를 같이 관리
KIMGEONHWI
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM~
Ivoryeee
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 수고하셨어요!
10tacion
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 수고스러운 작업 감사드립니다 🙇♂️🙇♂️
* style: detailSection 정렬 수정 * feat: framer-motion을 통한 DetailSection animation 구현 * feat: home의 'use client' 구문 제거 * style: 모바일 반응형 구현 * refactor: useActiveSection 훅 분리 * style: 짜잘한 스타일 수정 * style: 스타일 수정 및 필요없는 코드 삭제 * chore: framer-motion 패키지 삭제 후 motion/react로 통일 * refactor: 랜딩 1차 큐에이 반영 * style: 미세한 레이아웃 조정, 여백값 변경 등 피그마에 맞춰서 수정 * style: 반응형 중단점 lg로 수정 * feat: DetailSection 컴포넌트 구조 변경 - TextBlock 컴포넌트 삭제, DetailContents 컴포넌트 생성하여 텍스트와 모바일용 이미지를 같이 관리 * style: 모바일일때 스타일 수정 * refactor: 코드리뷰 반영
|
한서님 대박이시네요 ! |

🔥 Related Issues
✅ 작업 리스트
🔧 작업 내용
사용자가 스크롤하면서 콘텐츠를 읽어 내려가면 그에 맞는 이미지가 우측에 표시되도록 하는 애니메이션을 framer-motion을 통해 구현했습니다.
typed 사이트의 애니메이션 참고함
모바일에서는 TextBlock 아래에 이미지를 직접 배치하고 MediaBlock은 숨김 처리하도록 하여 반응형 구현- TextBlock과 MediaBlock을 각각 mapping하도록 수정-> 이유: 텍스트와 미디어가 완전히 다른 그리드 영역에 배치되어 있어서 분리된 두 개의 컨테이너에서 각각 매핑해야 함, 모바일에서는 텍스트 아래에 이미지가 바로 나타나고, 데스크톱에서는 오른쪽에 고정된 영역에 이미지가 표시, activeSection 상태에 따라 데스크톱의 이미지만 활성/비활성이 전환되어야 하므로... 좀 지저분해 보이는데 레이아웃 구조상 어쩔 수 없었습니당,,배열로 관리하던 텍스트데이터로 매핑해주니까 알아보기 쉽지 않은 코드가 된 것 같아 DetailContent 컴포넌트 구조를 아래와 같이 변경했습니다!
- TextBlock을 없애고 DetailContent 컴포넌트를 생성하여 텍스트와 모바일용 이미지를 같이 두었습니다.
- 텍스트 데이터로 한꺼번에 매핑하는 방식에서, 각 섹션을 key로 두고 참조하도록 변경했습니다. 코드 중복이 좀 있으나 가독성 면에선 더 낫다고 판단했어요!
제안사항 있으시면 리뷰 부탁드려요.
🧐 새로 알게된 점
🤔 궁금한 점
📸 스크린샷 / GIF / Link
2025-03-11.8.02.07.mov
2025-03-11.8.03.21.mov