지난 시간
섹션 4개가 나온다. 각 색션마다 배경색이 있다.
index는 섹션의 인덱스이다. (현재 보고 있는 섹션의 순서)
window.scrollY가 2 * index * window.innerHeight 에서 (2 * index + 1) * window.innerHeight 까지 스크롤을 내리는 동안 섹션은 스크롤을 따라 고정된다.
window.scrollY가 (2 * index + 1) * window.innerHeight 에서 (2 * index + 2) * window.innerHeight = 2 * (index + 1) * window.innerHeight 까지 스크롤을 내리는 동안 고정은 해제되고, 현재 섹션은 올라가고 새로운 섹션이 나타난다.
요구 사항
섹션이 고정돼있는 동안 인터랙션을 스크롤을 따라 나타나게 구현해보자.
이 글의 경우에는 섹션이 고정된 이후부터 스크롤을 내림에 따라 글자가 커지기 시작해 고정이 해제될때 커지는 것이 중지되는 것을 해본다.
다루는 개념
eventListener
useState
useEffect
eventListener
사용자의 클릭,마우스움직이기등의 행동(event)를 감지(listen)한다.
스크롤을 움직이는 것 역시 감지가능하다.
target.addEventListener(type, listener[, options]);
target: 이벤트를 등록할 요소
type: 이벤트 종류
listener: 이벤트 발생시 실행할 콜백함수
option: 여기선 사용하지 않는다.
우린 화면상의 scroll의 세로위치를 이용해 이벤트를 발생시킬 것이므로
target = window, type = "scroll" listener = const handleScroll = () => {스크롤 세로 위치를 받아와 그만큼 글자크기를 변경시킴} 을 넣어준다.
소스코드
TestCompo.tsx
interface proptype {
color: string;
start: number;
end: number;
}
const mysize = { start: 10, end: 50 };
const TestCompo = ({ color, start, end }: proptype) => {
const [fontSize, setFontSize] = useState(mysize.start);
const handleScroll = () => {
const newFontSize =
mysize.start +
((Math.min(window.scrollY, end) - start) * (mysize.end - mysize.start)) /
(end - start);
setFontSize(newFontSize);
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<div className="parent">
<div className="scroll-component" style={{ backgroundColor: color }}>
<p style={{ fontSize: fontSize }}>Component {color}</p>
</div>
</div>
);
};
export default TestCompo;
mysize
글자의 최소크기(start) 및 최대크기(end)를 담고있다.
props
color:이전에 사용한 컴포넌트의 배경색을 담는다.
start:글자크기가 변경되기 시작하는 스크롤위치
end:글자크기의 변경이 끝나는 스크롤위치
useState
상태를 관리하게 해준다. 간략히 설명하자면, const[value,setValue] = useState(initValue);로 선언해준다.
initValue는 초기에 넣어줄 상태값이다.
value로 현재 상태값을 가져올 수 있다.
상태값을 newValue로 변경하려면 setValue(newValue)로 변경해준다.
상태값이 변한다면 컴포넌트는 재랜더링이 된다. 따라서 변경된 상태값이 반영된다.
이 코드의 경우엔 글자의 크기를 useState로 관리해준다.
handleScroll
이벤트 발생시 실행하는 함수이다. 스크롤을 변경한다면 실행된다.
여기서 window.scrollY에 비례해 글자크기의 상태를 변경(setFontSize)해줘야한다.
수학적인 식을 유도해주면 된다.
useEffect
useEffect(setup, dependencies?)
setup에는 콜백함수를 넣어준다.
dependencies에는 배열을 넣어준다.
배열의 원소에 있는 값이 바뀔때마다 콜백함수가 실행된다.
만약에 빈 배열을 넣어준다면 컴포넌트가 처음 마운트 될때 실행된다.
리턴에는 cleanup code를 넣어준다. 컴포넌트가 언마운트될때 실행된다. 메모리 누수를 방지해준다.
왜 useEffect를 썻는가?
만약 eventListener를 useEffect없이 선언한다면, 컴포넌트가 리랜더링될때마다 eventListner를 add한다. 랜더링이 빈번하게 일어날 것이므로 메모리누수가 발생하고 성능저하가 발생한다. 따라서 불필요한 eventListner를 add하지않기위해서 useEffect hook을 사용한다.
TestPage.tsx
const componentsData = [
{ id: 1, color: "red" },
{ id: 2, color: "green" },
{ id: 3, color: "blue" },
{ id: 4, color: "purple" },
];
const TestPage = () => {
return (
<div style={{ height: window.innerHeight * 8 }}>
{componentsData.map((component, index) => (
<TestCompo
color={component.color}
key={component.id}
start={2 * index * window.innerHeight}
end = {(2 * index + 1) * window.innerHeight}
/>
))}
</div>
);
}
export default TestPage;
각 섹션의 start와 end에 섹션의 top이 0이되는 시점(2*window.innerHeight)~섹션의 고정이 풀려야할 시점((2*window.innerHeight+1)*window.innerHeight)를 넣어준다.
추가
이 경우엔 각 섹션이 차지하는 높이가 같고, 이벤트를 걸어줄 scrollY범위의 비율도 같아 TestCompo의 props로 index를 넣어줘 start와 end를 계산할수도 있다.
글자크기를 결정할때, 위 코드대로하면 최소글자크기가 계산이 안될수도있다. 위 코드는 최대글자크기만 제한시켰다.
왜그렇고 어떻게 해결해야하는가? Max를 쓴 것과 비슷한 방법을 쓰거나 조건문을 쓰면 될 것이다.
eventListner를 부모컴포넌트(TestPage)에 추가시키고 스크롤의 세로위치를 props로 주고 TestCompo에선 받은 세로위치를 토대로 글자크기만 계산시킬수도있다.
굳이 eventListner를 각 섹션마다 추가할 필요는 없다. 하지만 현재는 섹션이 4개라 성능적으로 큰 문제는 일어나지않아 코딩하기 쉬운스타일로 코드를 작성했다.
'개발' 카테고리의 다른 글
아니, 왜 컴포넌트 사이즈 얻어오는 hook이 이상해진거지? width와 height가 둘다 0이야 (0) | 2023.11.20 |
---|---|
React로 스크롤 기반 인터랙티브 웹 만들기 2 인터랙션2 (1) | 2023.10.22 |
React로 스크롤 기반 인터랙티브 웹 만들기 1 목적, 레이아웃 (0) | 2023.10.15 |
Redux와 Redux ToolKit사용하기 (1) | 2023.08.17 |
discord js 로 간단한 봇 개발하기 3. client.on (1) | 2023.06.10 |