앱을 만들다보면 가로로 컨텐츠를 스크롤하는 캐러셀이 필요할 때가 있다. 그럴 때 외부 라이브러리를 사용하는 것도 방법이지만, 복잡한 애니메이션을 필요로 하는게 아닌 이상 ScrollView 만으로 충분히 구현할 수도 있다.
//ScrollViewCarousel.tsx
import styled from '@emotion/native';
import React, {useState} from 'react';
import {ScrollView} from 'react-native';
const CarouselContainer = styled.View`
flex: 1;
`;
const Row = styled.View`
flex-direction: row;
`;
const CarouselItemContainer = styled.View`
width: ${(props: {width: number}) => props.width}px;
height: 100%;
padding: 20px;
`;
const CarouselItem = styled.View`
flex: 1;
background-color: ${(props: {color: string}) => props.color};
`;
const ScrollViewCarousel = () => {
const data = ['tomato', 'skyblue', 'green', 'beige', 'yellow'];
const [itemWidth, setItemWidth] = useState(0);
return (
<CarouselContainer style={{flex: 1}}>
<ScrollView
style={{flex: 1}}
horizontal
pagingEnabled
contentContainerStyle={{width: `${100 * data.length}%`}}
scrollEventThrottle={200}
decelerationRate="fast"
onContentSizeChange={w => setItemWidth(w / data.length)}
showsHorizontalScrollIndicator={false}>
<Row>
{data.map((item: string) => {
return (
<CarouselItemContainer key={item} width={itemWidth}>
<CarouselItem color={item} />
</CarouselItemContainer>
);
})}
</Row>
</ScrollView>
</CarouselContainer>
);
};
export default ScrollViewCarousel;
---------------------------------------
//App.tsx
import React from 'react';
import ScrollViewCarousel from './src/components/ScrollViewCarousel';
import styled from '@emotion/native';
const AppBackground = styled.View`
justify-content: center;
align-items: center;
flex: 1;
background-color: black;
`;
const CarouselBox = styled.View`
width: 80%;
height: 60%;
border-radius: 10px;
background-color: white;
overflow: hidden;
`;
const App = () => {
return (
<AppBackground>
<CarouselBox>
<ScrollViewCarousel />
</CarouselBox>
</AppBackground>
);
};
export default App;
<ScrollView
style={{flex: 1}}
horizontal
pagingEnabled
contentContainerStyle={{width: `${100 * data.length}%`}}
scrollEventThrottle={200}
decelerationRate="fast"
onContentSizeChange={w => setItemWidth(w / data.length)}
showsHorizontalScrollIndicator={false}>
</ScrollView>
이 코드의 가장 핵심은 당연하게도 ScrollView에 있다. 각각 하나씩 설명하자면, 먼저 ScrollView 또한 하나의 View 이므로, flex:1을 주어 CarouselContainer를 가득 채워주었고, contentContainerStyle 속성을 통해 contentContainer의 width를 CarouselContainer의 width* data의 길이만큼 주었다. 그리고 onContentSizeChange를 사용해 퍼센트로 계산된 contentContainer의 width를 정확한 px값으로 가져왔다. 이제 이렇게 받아온 width 값을 다시 캐러셀에 보여주고 싶은 data의 갯수만큼 나눠서 각 아이템의 width가 CarouselContainer에 가득 차도록 width 값을 얻어낸 것이다. (이부분은 조금 복잡할 수 있으므로 여러번 코드를 읽어볼 필요가 있다.)
그 외에 다른 속성들로는 가로 스크롤을 위한 horizontal, 스크롤 할때마다 자석처럼 각 아이템이 중앙에 올 수 있도록 해주는 pagingEnabled, 최적화에 사용되는 scrollEventThrottle, 터치를 뗀 후 스크롤이 멈추는 속도를 결정하는 decelerationRate, 스크롤바를 안보이게 설정할 수 있는 showHorizontalScrollIndicator가 있다.
이처럼 ScrollView의 기본 속성만으로 빠르게 캐러셀을 만들어볼 수 있었다. (스타일 코드가 캐러셀 구현 코드보다 더 긴 것 같다.) 캐러셀 각 아이템의 너비를 받아와서 계산하는 로직이 조금 복잡할 수 있지만, 이부분은 직접 한두번 구현해보면 어떤 느낌인지 알 수 있을 거라고 생각한다.
참고 링크: https://rossbulat.medium.com/react-native-carousels-with-horizontal-scroll-views-60b0587a670c