웹 사이트를 탐험해보다가 특정 위치에서 가로스크롤이 되는 페이지를 봤던 기억이 있습니다. 어떻게 구현했을지 생각해보며 리액트로 구현해봤습니다.
빠른 구현을 위해 인라인 스타일을 사용하여 구현하였습니다.
import { useEffect, useRef } from 'react';
export const HorizontalScrolling = () => {
const containerElRef = useRef<HTMLDivElement | null>(null);
const imageItemListStickyContainerElRef = useRef<HTMLDivElement | null>(null);
const imageItemListElRef = useRef<HTMLUListElement | null>(null);
const setContainerElHeight = () => {
if (
!containerElRef.current ||
!imageItemListElRef.current ||
!imageItemListStickyContainerElRef.current
)
return;
containerElRef.current.style.height =
imageItemListElRef.current.offsetWidth -
imageItemListStickyContainerElRef.current.offsetWidth +
imageItemListStickyContainerElRef.current.offsetHeight +
'px';
};
const setImageItemListElTranslateX = () => {
if (
!containerElRef.current ||
!imageItemListElRef.current ||
!imageItemListStickyContainerElRef.current
)
return;
const scrollOffset = window.scrollY - containerElRef.current.offsetTop;
const maxTranslateX = imageItemListElRef.current.offsetWidth - imageItemListStickyContainerElRef.current.offsetWidth;
const translateX = Math.min(maxTranslateX, Math.max(0, scrollOffset));
imageItemListElRef.current.style.transform = `translateX(-${translateX}px)`;
};
useEffect(() => {
const handleScroll = () => {
setContainerElHeight();
setImageItemListElTranslateX();
};
const handleResize = () => {
setContainerElHeight();
};
setContainerElHeight();
window.addEventListener('scroll', handleScroll);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div>
<div>
<div>text...</div>
<div>text...</div>
// 이하 생략
</div>
<div ref={containerElRef} style={{ position: 'relative' }}>
<div
ref={imageItemListStickyContainerElRef}
style={{
position: 'sticky',
left: 0,
top: 0,
overflowX: 'hidden',
}}
>
<ul
ref={imageItemListElRef}
style={{
display: 'inline-grid',
gridTemplateColumns: 'repeat(10, calc(50vh - 32px))',
gridTemplateRows: 'repeat(2, 1fr)',
gap: 16,
padding: 16,
height: '100vh',
backgroundColor: '#4740f9',
alignItems: 'center',
}}
>
<li
style={{
width: '100%',
height: '100%',
borderRadius: 12,
}}
>
<img
width={'100%'}
height={'100%'}
src="/오둥이프사모음/수면바지.png"
alt="수면바지"
style={{ objectFit: 'cover', borderRadius: 12 }}
/>
</li>
<li
style={{
width: '100%',
height: '100%',
borderRadius: 12,
}}
>
<img
width={'100%'}
height={'100%'}
src="/오둥이프사모음/집 가고싶둥.jpg"
alt="집 가고싶둥"
style={{ objectFit: 'cover', borderRadius: 12 }}
/>
</li>
// 이하 생략
</ul>
</div>
</div>
<div>
<div>text...</div>
<div>text...</div>
// 이하 생략
</div>
</div>
);
};
