React - 캐러셀(carousel) 만들기

bkboy·2022년 7월 13일
5

웹 개발

목록 보기
18/26
post-thumbnail

캐러셀(carousel)

회전목마라는 뜻으로 이미지 슬라이드이다. 페이지를 제작하면서 직접 만들어보고 관련 내용을 정리하려고 한다.

이런 비슷한 형태를 구현했다. 밑에 label의 색이 바뀌는 부분은 아직 구현을 못했다. 수동이 아닌 자동으로 구현했다.

코드

import React, { useEffect, useRef, useState } from 'react';
import Image from '../Atoms/Image';
import styled from 'styled-components';
import Button from '../Atoms/Button';
import Input from '../Atoms/Input';

const Container = styled.div`
    width: 1000px;
    height: 500px;
    margin: 0 auto;
    overflow: hidden;
    position: relative;
`;

const ImageBox = styled.ul`
    margin: 10px 0 0 0;
    padding: 0;
    width: 100%;
    display: flex;
    transition: ${(props) => (!props.count ? '' : 'all 0.5s ease-in-out')};
    transform: ${(props) => 'translateX(-' + props.count * 1000 + 'px)'};
`;
const ImageList = styled.li`
    list-style: none;
`;
const Bullets = styled.div`
    position: absolute;
    display: flex;
    flex-direction: column-reverse;
    right: 10px;
    bottom: 10px;
    z-index: 2;
`;
const Label = styled.label`
    display: inline-block;
    border-radius: 50%;
    background-color: rgba(88, 84, 84, 0.55);
    width: 10px;
    height: 10px;
    margin-top: 5px;
    cursor: pointer;
`;

const ImgSlider = () => {
    const TOTAL_SLIDES = 3;
    const imgLength = 1000;
    const IMG = [
        'images/1.png',
        'images/2.png',
        'images/3.png',
        'images/1.png',
    ];
    const [curruntIdx, setCurrentIdx] = useState(0);
    const [count, setCount] = useState(0);
    const slideRef = useRef(null);

    const nextSlide = () => {
        if (curruntIdx >= TOTAL_SLIDES) {
            setCurrentIdx(0);
        } else {
            setCurrentIdx((prev) => prev + 1);
        }
    };

    const prevSlide = () => {
        if (curruntIdx === 0) {
            setCurrentIdx(TOTAL_SLIDES);
        } else {
            setCurrentIdx((prev) => prev - 1);
        }
    };

    // useEffect(() => {
    //     console.log(curruntIdx);
    //     slideRef.current.style.transition = `all 0.5s ease-in-out`;
    //     slideRef.current.style.transform = `translateX(-${curruntIdx}000px)`;
    // }, [curruntIdx]);

    useEffect(() => {
        const timer = setInterval(() => {
            setCount((prev) => (prev === TOTAL_SLIDES ? 0 : prev + 1));
        }, 3000);

        return () => {
            clearInterval(timer);
        };
    }, [count]);
    return (
        <>
            <Container>
                <Input type="radio" name="slider" id="slider1" />
                <Input type="radio" name="slider" id="slider2" />
                <Input type="radio" name="slider" id="slider3" />
                <ImageBox ref={slideRef} count={count}>
                    {IMG.map((ele, idx) => (
                        <ImageList key={idx}>
                            <Image src={ele} />
                        </ImageList>
                    ))}
                </ImageBox>
                <Bullets>
                    <Label for="slider1">&nbsp;</Label>
                    <Label for="slider2">&nbsp;</Label>
                    <Label for="slider3">&nbsp;</Label>
                </Bullets>
            </Container>
            {/* <Button onClick={prevSlide}>prev</Button>
            <Button onClick={nextSlide}>next</Button> */}
        </>
    );
};

export default ImgSlider;

주석 처리된 부분은 수동 슬라이드 코드이다.

ImgSlider 컴포넌트부터 살펴보자.

현재 이미지는 총 4개를 사용하고 마지막 인덱스인 3의 값을 변수로 저장해줬다. img의 width 값도 변수로 저장해주었다.

currentIdx와 slideRef는 아직은 활용하지 않는다. 라벨 기능 추가에 사용하려고 한다. nextSlide, prevSlide도 수동 관련 기능이다.

useEffect 부분을 보자.

useEffect(() => {
        const timer = setInterval(() => {
            setCount((prev) => (prev === TOTAL_SLIDES ? 0 : prev + 1));
        }, 3000);

        return () => {
            clearInterval(timer);
        };
    }, [count]);

setInterval을 사용해 3초마다 count상태를 증가 시킨다. count가 증가되면서 렌더링이 계속 일어날 것이다.

그리고 이 count를 이용하여

const ImageBox = styled.ul`
    margin: 10px 0 0 0;
    padding: 0;
    width: 100%;
    display: flex;
    transition: ${(props) => (!props.count ? '' : 'all 0.5s ease-in-out')};
    transform: ${(props) => 'translateX(-' + props.count * 1000 + 'px)'};
`;

스타일드 컴포넌트에 props로 넘겨 translateX을 조절하고있다.

앞서 이미지는 4개를 사용 중이라고 했는데 자세히 보면 첫번째 이미지가 두개인 것을 알 수 있다. 이는 무한 반복되는 구조를 만들기 위한 속임수이다. 마지막 이미지에서 가짜 첫번째 이미지로 가고 그때 transition을 없애고 첫번째 이미지로 이동이 된다. 그 후 똑같은 반복이 이어지는 것이다.

아직 수정해야 할 부분이 꽤 있다. 일단 한바퀴를 돈 뒤 첫번째 이미지에서 다른 이미지보다 2배의 시간을 비추는 점을 수정해야하고 라벨을 이용한 즉각 이동과 색상 변화로 몇번째인지 알려주는 기능을 추가해야한다.

수정1

우선 1번째 이미지에 더 긴 시간 머무는 것을 수정하겠다.
그러기 위해서는 1번 이미지에 왔을 때 timeInterval이 0이 되어야한다.

const bool = useRef(false);
    useEffect(() => {
        const timer = setInterval(
            () => {
                if (count < TOTAL_SLIDES) {
                    bool.current = false;
                    setCount((prev) => prev + 1);
                } else {
                    bool.current = true;
                    setCount(0);
                }
            },
            bool.current ? 0 : 2500
        );

        return () => {
            clearInterval(timer);
        };
    }, [count]);

새로운 변수를 추가하고, useEffect 내부를 수정했다.
bool은 false로 초기화 하고 마지막 이미지에 도착했을 때 true로 만들어준다. bool true가 되면 interval이 0 이되고 다음 이미지로 바로 넘어가게되고 다시 bool false가 된다 이 과정을 반복한다.

profile
음악하는 개발자

0개의 댓글