씬프로드를 제작하면서 여러가지 사진을 하나의 프레임에서 볼 수 있도록 구현할 필요가 있었다. 블로그 같이 사진 하나 하나 나열하고 글을 쓰는 형식이 아니라, 인스타그램과 같이 사진과 글을 따로 보여주는 형식이었기 때문에 하나의 프레임에서 사진을 옆으로 넘기는 형식이 필요했다.
옆으로 넘길 수 있기 때문에 slide라고 생각했는데,
Carousel이라는 명칭이 있다. 회전목마가 넘어가는 것과 같아서 그런건가?
일단, 기본적인 아이디어는 엘리스 SW 교육을 받을 때 들었던 강의에서 가져왔다.
3가지를 조합해 Carousel을 만들 수 있다.
export default function Carousel() {
return <div className="carousel"></div>
}
보여지는 화면은 내부에 컨테이너를 가지는데, 넘치는 부분은 가려질 수 있도록 CSS에서 overflow에 대한 속성을 지정한다.
.carousel {
overflow: hidden;
}
가로로 넘치는 부분을 hidden
속성으로 가린다.
컨테이너의 넓이를 보여지는 화면 너비 * 아이템 갯수
로 지정해 좌우로 넓게 이어지도록 했다.
현재, 보여지는 화면의 너비는 좌우로 꽉찬 100%이기 때문에 100% * 아이템 갯수
로 했다.
export default function Carousel() {
const containerWidth = 100 * images.length;
return <div className="carousel">
<div
className="carousel__container"
style={{ width: `${containerWidth}%`}}
>
{ /* items */ }
</div>
</div>
}
내부의 컨테이너는 아이템들을 가로로 정렬하기 편하도록 flex
속성을 부여했다.
.carousel__container {
display: flex;
}
이제 내부의 컨테이너에 아이템을 넣어주면 된다.
씬프로드는 내부에 이미지들을 넣어준다.
이미지의 너비는 컨테이너 너비 100%를 기준으로 1/N 씩 나눠가질 수 있도록 한다. (const itemWidth = 100 / images.length
)
{
images.map((image) => {
<figure
className="carousel__item"
style={{ width: `${itemWidth}%` }}
>
<img className="carousel__img" />
</figure>
})
}
이미지를 다루다보면, 이미지를 감싸는 figure 태그 밖으로 이미지가 커지는 경우가 있다. 이미지 크기를 잡기 위해서 이미지 태그에 대해서
box-sizing: border-box;
CSS 속성을 주면 이미지가 부모 태그 밖으로 커지지 않는다.
화면을 움직이는 방식은 여러 방법이 있을 수 있다.
엘리스 SW 교육에서 사용한 방식을 margin-left
를 활용한 방식이었다. 이 외에도 transform
을 이용해서 컨테이너의 위치를 변경하는 것도 가능할 것 같다.
일단, margin-left
를 이용해 옮기는 방식을 선택했다.
쉽게 구현할 수 있는 아이디어였기 때문에 선택했다.
이제 컨테이너의 margin-left
를 tsx에서 변경할 수 있도록 컨테이너의 style 속성에 marginLeft 속성을 준다.
export default function Carousel() {
const containerWidth = 100 * images.length;
const [leftMargin, setLeftMargin] = useState(0);
return <div className="carousel">
<div
className="carousel__container"
style={{
width: `${containerWidth}%`,
marginLeft: `${leftMargin}%`
}}
>
{ /* items */ }
</div>
</div>
}
margin-left
는 변화에 따라 상태를 추적해야 하기 때문에 useState
로 값을 관리한다.
이제 사용자의 입력에 따라 leftMargin의 값이 변화하도록 코드를 구현한다.
Carousel의 좌측과 우측에 버튼을 구현하고, 우측에 있는 버튼을 클릭할 경우 margin-left
의 값을 음수로 만들어 보여지는 화면에서 컨테이너가 다음 그림과 같이 되도록 한다.
반대로 좌측에 있는 버튼을 클릭한다면 margin-left에 값을 더해 돌아오도록 한다.
지금과 같이 구현한다면, 화면은 짠, 짠, 짠 하고 변하게 된다.
하지만 회전목마와 같은 Carousel은 옆으로 이동하는 모습을 보여준다.
이를 위해 컨테이너의 CSS 속성에 transition
속성을 부여한다.
.carousel__container {
transition: margin ease 0.5s;
}
transition
을 사용하면 부드럽게 넘어가게 된다.
Carousel.tsx와 Carousel.css에서 전체 코드를 확인할 수 있다.
처음 Container에 이미지를 넣을 때, 이미지를 크기에 맞추면 아래로 정렬되고, 정렬을 중간으로 맞추려고 하면 부모 태그 사이즈보다 커지는 문제가 발생했다.
생각보다 단순하게 해결할 수 있었다.
이미지를 감싸는 figure
태그에 flex
속성을 주고,
img
에는 box-sizing: border-box; object-fit: contain;
속성을 통해 해결할 수 있었다.
flex 박스로 지정하면 잘 정렬된 형태로 나오고,
box-sizing 속성을 통해 이미지가 부모 태그 밖으로 커지지 않도록 하고, contain으로 화면을 꽉채워줬더니 해결되었다.
처음 Carousel을 완성했을 때, 이미지가 넘어갈 때 드르륵 드르륵 화면이 떨리는 현상이 있었다.
해당 문제는 scroll-bar가 생겼다가 사라졌다가 하면서 생기는 문제였다.
scroll-bar가 보이지 않도록 설정하고 해결했다.
html::-webkit-scrollbar {
display: none;
}