728x90
๋ฐ์ํ
๐๊ฐ๋ก ์คํฌ๋กค๋ฐ ๊ตฌํ
page.js
"use client";
import { useState, useEffect, useRef } from "react";
import gsap from "gsap";
export default function page() {
const scrollSectionRef = useRef(null);
const scrollContentRef = useRef(null);
useEffect(() => {
const scrollSection = scrollSectionRef.current;
const scrollContent = scrollContentRef.current;
const handleScroll = () => {
const scrolled = window.pageYOffset;
const sectionOffset = Math.abs(scrollSection.offsetTop - scrolled);
const notReachedBottom = parseInt(
Math.max(
0,
scrollSection.getBoundingClientRect().bottom - window.innerHeight
)
);
if (scrollSection.offsetTop <= scrolled && notReachedBottom) {
gsap.to(scrollContent, {
x: -sectionOffset,
});
}
};
document.addEventListener("scroll", handleScroll);
// Clean up the event listener on component unmount
return () => {
document.removeEventListener("scroll", handleScroll);
};
}, []); // Empty dependency array to run the effect only once on mount
return (
<>
<section id="projects" ref={scrollSectionRef}>
<div className="horizontal_scroll_wrap">
<div className="horizontal_scroll_content" ref={scrollContentRef}>
<div className="about_project"></div>
<div className="about_project"></div>
<div className="about_project"></div>
<div className="about_project"></div>
</div>
</div>
</section>
</>
);
}
css
/* height๊ฐ์ด scroll์ ์ฒด width ๊ฐ์ด ๋๋ค.about-project๊ฐ์๋ฐ๋ผ height๊ฐ ๋๋ฆฌ๊ธฐ */
#projects {
background-color: #B39C7A;
position: relative;
height: 1180vh;
}
.horizontal_scroll_wrap {
position: sticky;
top: 0;
left: 0;
height: 100vh;
overflow: hidden;
}
/* scroll_width๊ฐ ์ค์ - about-project๊ฐ์๋ฐ๋ผ width๊ฐ ๋๋ฆฌ๊ธฐ */
.horizontal_scroll_content {
display: flex;
height: 100vh;
width: 600vw;
will-change: transform;
margin: 2vh 0 0 3vw;
}
scrollSectionRef์ scrollContentRef
- ๊ฐ๊ฐ ์คํฌ๋กค ์น์ ๊ณผ ์คํฌ๋กค ์ปจํ ์ธ ์ ๋ํ React Ref์ด๋ค.
- ์ด๋ฅผ ํตํด ํด๋น DOM ์์์ ์ ๊ทผํ ์ ์๋ค.
const scrolled = window.pageYOffset
- window.pageYOffset ๋ scrollY์ ๋ค๋ฅธ ์ด๋ฆ์ด๋ค. ์คํฌ๋กค์ด ์์ง์ผ๋ก ์ผ๋ง๋ ์คํฌ๋กค( ์ธ๋ก ์คํฌ๋กค) ๋๋์ง ํฝ์ ๋จ์๋ก ๋ฐํํ๋ค.
- ์ด ๊ฐ์ scrolled ๋ณ์์ ํ ๋นํ์ฌ ํ์ฌ ์คํฌ๋กค๋ ์์น๋ฅผ ์ ์ฅํ๋ค.
const sectionOffset = Math.abs(scrollSection.offsetTop - scrolled)
- scrollSection์ scrollSectionRef๋ฅผ ํตํด ์ฐธ์กฐํ ์คํฌ๋กค ์น์ ์ DOM ์์์ด๋ค.
- scrollSection.offsetTop์ ์น์ ์ ์๋จ์ด ์ ์ฒด ๋ทฐํฌํธ์ ์๋จ์์ ์ผ๋ง๋ ๋จ์ด์ ธ ์๋์ง๋ฅผ ๋ํ๋ธ๋ค.
- scrolled๋ ์ด๋ฏธ ๊ณ์ฐ๋ ํ์ฌ ์คํฌ๋กค ์์น์ ๋๋ค.
- Math.abs ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๊ฐ ๊ฐ์ ์ฐจ์ด๋ฅผ ์ ๋๊ฐ์ผ๋ก ๊ณ์ฐํฉ๋๋ค.
- ์ด๋ ๊ฒ ํจ์ผ๋ก์จ ์ธ๋ก ์คํฌ๋กค ์์น์ ์น์ ์ ์๋จ ์์น ์ฌ์ด์ ์๋์ ์ธ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ๊ณ , sectionOffset ๋ณ์์ ์ ์ฅํฉ๋๋ค.
scrollSection.offsetTop๋ ์คํฌ๋กค ์น์ ์ ์๋จ ์์น๋ฅผ ๋ํ๋ด๋ ๊ณ ์ ๋ ๊ฐ์ด๋ฉฐ,
scrolled๋ ํ์ฌ ์ฌ์ฉ์๊ฐ ์คํฌ๋กคํ ์์น๋ฅผ ๋์ ์ผ๋ก ๋ํ๋ด๋ ๊ฐ์ด๋ค.
notReachedBottom
- scrollSection.getBoundingClientRect().bottom์ ์คํฌ๋กค ์น์ ์ ์๋์ชฝ ๊ฒฝ๊ณ(bottom)์ด ํ์ฌ ๋ทฐํฌํธ์์ ์ผ๋ง๋ ๋จ์ด์ ธ ์๋์ง๋ฅผ ๋ํ๋ธ๋ค.
- window.innerHeight๋ ํ์ฌ ๋ทฐํฌํธ์ ๋์ด๋ฅผ ๋ํ๋ธ๋ค.
- Math.max(0, ...)๋ฅผ ์ฌ์ฉํ์ฌ 0๋ณด๋ค ์์ ๊ฐ์ ๋ฐฉ์งํ๊ณ , parseInt๋ฅผ ํตํด ์ ์๋ก ๋ณํ.
- ์ด๋ ๊ฒ ๊ณ์ฐ๋ notReachedBottom์ ์คํฌ๋กค ์น์ ์ ์๋์ชฝ์ด ๋ทฐํฌํธ ์์ ์์ง ์์ ๊ฒฝ์ฐ, ์ฆ ๋ทฐํฌํธ ์๋๋ก ์คํฌ๋กค๋์์ ๋ 0 ์ด์์ ๊ฐ์ ๊ฐ์ง๊ฒ ๋๋ค.
if (scrollSection.offsetTop <= scrolled && notReachedBottom)
{ gsap.to(scrollContent, { x: -sectionOffset, }); }
- ์กฐ๊ฑด๋ฌธ์ ํ์ฌ ์ธ๋ก ์คํฌ๋กค ์์น(scrolled)๊ฐ ์คํฌ๋กค ์น์ ์ ์๋จ ์์น(scrollSection.offsetTop)๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ๊ณ , notReachedBottom์ด 0๋ณด๋ค ํฐ ๊ฒฝ์ฐ์ ์ฐธ์ด ๋๋ค.
- ์ด ์กฐ๊ฑด์ ์ธ๋ก ์คํฌ๋กค ์์น๊ฐ ์คํฌ๋กค ์น์ ์ ๋๋ฌํ๊ณ ๋์์ ์น์ ์ด ๋ทฐํฌํธ ์๋๋ก ๋์ด๊ฐ์ง ์์ ๊ฒฝ์ฐ๋ฅผ ์๋ฏธํ๋ค.
- ๋ง์ฝ ์กฐ๊ฑด์ด ์ฐธ์ด๋ผ๋ฉด (notReachedBottom์ด 0๋ณด๋ค ํฌ๋ฉด์ ์ธ๋ก ์คํฌ๋กค ์์น๊ฐ ์คํฌ๋กค ์น์ ์ ๋๋ฌํ ๊ฒฝ์ฐ), gsap.to๋ฅผ ์ฌ์ฉํ์ฌ scrollContent์ X ์ขํ๋ฅผ -sectionOffset ๋งํผ ์ด๋์ํจ๋ค.
- { x: -sectionOffset }๋ ์ ๋๋ฉ์ด์ ์ ์์ฑ ์ค์ ์ด๋ค. ์ฌ๊ธฐ์ x๋ ๊ฐ๋ก ๋ฐฉํฅ์ ์ด๋์ ๋ํ๋ด๊ณ , -sectionOffset์ ๊ทธ ์ด๋ ๊ฑฐ๋ฆฌ์ด๋ค.
- ์ด๋ก์จ ๊ฐ๋ก ์คํฌ๋กค ํจ๊ณผ๋ฅผ ๋ถ์ฌํ๊ฒ ๋๋ค.
โปํฌ๊ธฐ ๋ฐ ์์น์ ๊ด๋ จ๋ ๋ฉ์๋
- clientWidth : ์์์ ๊ฐ๋ก ๊ฐ(๋ง์ง/๋ณด๋ ๋ถํฌํจ)
- clientHeight : ์์์ ์ธ๋ก ๊ฐ(๋ง์ง/๋ณด๋ ๋ถํฌํจ)
- clientTop : ์์์ Y์ถ ์์น ๊ฐ(๋ถ๋ชจ ๊ธฐ์ค)
- clientLeft : ์์์ X์ถ ์์น ๊ฐ(๋ถ๋ชจ ๊ธฐ์ค)
- offsetWidth : ์์์ ๊ฐ๋ก ๊ฐ(๋ณด๋/ํจ๋ฉ ํฌํจ)
- offsetHeight : ์์์ ์ธ๋ก ๊ฐ(๋ณด๋/ํจ๋ฉ ํฌํจ)
- offsetTop : ์์์ Y์ถ ์์น ๊ฐ(๋ฌธ์ ๊ธฐ์ค)
- offsetLeft : ์์์ X์ถ ์์น ๊ฐ(๋ฌธ์ ๊ธฐ์ค)
- offsetParent: ๋ถ๋ชจ ์์๋ฅผ ๊ธฐ์ค์ผ๋ก ์์น ์ ๋ณด
- getBoundingClientRect() ๋ฉ์๋ : ์์์ ํฌ๊ธฐ ๋ฐ ์์น ์ ๋ณด
- getClientRect() ๋ฉ์๋ : ์์์ ํฌ๊ธฐ ๋ฐ ์์น ์ ๋ณด
๐์ต์ข ๊ตฌํ ๋์์
์ฐธ๊ณ ์๋ฃ
728x90
๋ฐ์ํ
'Frontend > Next.js' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Next.js 14๋ฒ์ - Layouts,Metadata,Dynamic Routes (0) | 2024.02.08 |
---|---|
Next.js - ์คํฌ๋กค ์ ๋ ๊ฐ์ด๋ ๊ตฌํ (0) | 2023.12.19 |
Next.js - modal ์ฐฝ ๋ง๋ค๊ธฐ (0) | 2023.12.19 |
Next.js 14๋ฒ์ - favicon ๋ฐ๊พธ๊ธฐ (1) | 2023.11.10 |
Nextjs - next.js ๊ตฌ์กฐ์ img ๋ฃ๋ ๋ฒ (0) | 2023.11.06 |