react-typescript로 캐러셀 슬라이더 만들어보기

이경준·2021년 4월 14일
4


라이브러리에 의존하게 되는것 같아서 연습삼아 이미지 슬라이더를 만들어 보았다.

1. 컴포넌트 분리

import GlobalStyle from "./GlobalStyle";
import * as Styled from "./App.style";
import Slider from "./components/Slider/Slider";
import SliderDot from "./components/SliderDot/SliderDot";

const App = () => {

  return (
    <>
      <GlobalStyle />
      <Styled.Container>
        <Slider/>
        <SliderDot />
      </Styled.Container>
    </>
  );
};

export default App;

1.슬라이더 이미지박스인 Slider컴포넌트와 아래 SliderDot컴포넌트를 분리하였다. (각자의 기능이 컴포넌트안에서 연관이 없었기에 코드가 길어지는 것보다 나누는것이 나을것으로 판단하였다.)

2. 왼쪽,오른쪽 이동 함수 만들기

const App = () => {
  const images = [
    { pic: img1, id: 1 },
    { pic: img2, id: 2 },
    { pic: img3, id: 3 },
    { pic: img4, id: 4 },
    { pic: img5, id: 5 },
  ];

  const [translateValue, setTranslateValue] = useState<number>(0);

  const moveRight = (): void => {
    if (translateValue !== 70 * (images.length - 1)) {
      setTranslateValue((prev) => prev + 70);
    } else {
      setTranslateValue(0);
    }
  };

  const moveLeft = (): void => {
    if (translateValue !== 0) {
      setTranslateValue((prev) => prev - 70);
    } else {
      setTranslateValue(70 * (images.length - 1));
    }
  };
  
  return (
    <>
      <GlobalStyle />
      <Styled.Container>
        <Slider/>
        <SliderDot />
      </Styled.Container>
    </>
  );
};

1.슬라이더의 width가 70vw이기 때문에 useState를 이용하여 translate하게될 값을 더하기와 빼기로 구분하였다.
2.제일 마지막 image로 갈시에 처음으로 돌아가게 하기 위하여 images.length로 마지막 image를 찾아 계산하였다.
3이렇게 만든 함수는 setInterval과 clickEvent등 2회이상 사용하게 될것같아서 함수로 만들었다.

3. Slider컴포넌트 클릭 이벤트 적용하기

//Slider.tsx

type SliderProps = {
  translateValue: number;
  images: { pic: string; id: number }[];
  moveRight: () => void;
  moveLeft: () => void;
};

const Slider: React.FC<SliderProps> = {
  translateValue,
  images,
  moveRight,
  moveLeft,
}) => {
  const clickRight = (e: React.MouseEvent<HTMLElement, MouseEvent>): void => {
    moveRight();
  };

  const clickLeft = (e: React.MouseEvent<HTMLElement, MouseEvent>): void => {
    moveLeft();
  };

  return (
    <Styled.Slider>
      <Styled.ImageBox
        translateValue={translateValue !== 0 ? translateValue : null}
      >
        {images.map((picture, idx) => {
          return (
            <Styled.Image
              key={picture.id}
              src={picture.pic}
              alt={"dog" + idx}
            />
          );
        })}
      </Styled.ImageBox>
      <Styled.ArrowBox>
        <Styled.Arrow
          className='fas fa-chevron-left'
        ></Styled.Arrow>
        <Styled.Arrow
          className='fas fa-chevron-right'
        ></Styled.Arrow>
      </Styled.ArrowBox>
    </Styled.Slider>
  );
};

1.처음에 만들었던 함수를 그대로props로 넘겨주어 화살표 왼쪽과 오른쪽 각각 클릭시에 moveLeft()와 moveRight()를 적용시켜 css의 translate를 움직이게 하였다.

4. 마우스 드래그 슬라이더 만들기

//Slider.tsx
type SliderProps = {
  translateValue: number;
  images: { pic: string; id: number }[];
  moveRight: () => void;
  moveLeft: () => void;
};

const Slider: React.FC<SliderProps> = ({
  translateValue,
  images,
  moveRight,
  moveLeft,
}) => {
  const [mouseDownClientX, setMouseDownClientX] = useState<number>(0);
  const [mouseUpClientX, setMouseUpClientX] = useState<number>(0);
  const [cursorOn, setCursorOn] = useState<boolean>(false);

  const clickRight = (e: React.MouseEvent<HTMLElement, MouseEvent>): void => {
    moveRight();
  };

  const clickLeft = (e: React.MouseEvent<HTMLElement, MouseEvent>): void => {
    moveLeft();
  };
  const onMouseDown = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
    setMouseDownClientX(e.clientX);
    setCursorOn(true);
  };
  const onMouseUp = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
    setMouseUpClientX(e.clientX);
    setCursorOn(false);
  };

  useEffect(() => {
    const dragSpace = Math.abs(mouseDownClientX - mouseUpClientX);

    if (mouseDownClientX !== 0) {
      if (mouseUpClientX < mouseDownClientX && dragSpace > 100) {
        moveRight();
      } else if (mouseUpClientX > mouseDownClientX && dragSpace > 100) {
        moveLeft();
      }
    }
  }, [mouseUpClientX]);

  return (
    <Styled.Slider
      onMouseDown={onMouseDown}
      onMouseUp={onMouseUp}
      cursorOn={cursorOn}
    >
      <Styled.ImageBox
        translateValue={translateValue !== 0 ? translateValue : null}
      >
        {images.map((picture, idx) => {
          return (
            <Styled.Image
              key={picture.id}
              src={picture.pic}
              alt={"dog" + idx}
            />
          );
        })}
      </Styled.ImageBox>
      <Styled.ArrowBox>
        <Styled.Arrow
          onClick={clickLeft}
          className='fas fa-chevron-left'
        ></Styled.Arrow>
        <Styled.Arrow
          onClick={clickRight}
          className='fas fa-chevron-right'
        ></Styled.Arrow>
      </Styled.ArrowBox>
    </Styled.Slider>
  );
};

export default Slider;

1.마우스 down했을때와up했을때 clientX값을 useState에 넣어주었다.
2.useEffect안에 두 값을 비교하여 down보다 up이 작을경우 왼쪽으로 클경우 오른쪽으로 슬라이더를 적용하였다.
3.마우스 up,down간격이 굉장히 좁을때도 적용이 되는듯하여 두이벤트의 간격이 100이 넘으면 적용될수있게 Math.abs()를 사용하였다.

5. 이미지 위치 확인하는 dot만들기

//SliderDot.tsx

type SliderDotProps = {
  images: { pic: string; id: number }[];
  translateValue: number;
  moveRight: () => void;
};

const SliderDot: React.FC<SliderDotProps> = ({
  images,
  translateValue,
  moveRight,
}) => {
  const [imageIndex, setImageIndex] = useState<number>(0);

  useEffect(() => {
    setImageIndex(translateValue / 70);
  }, [translateValue]);

  return (
    <Styled.DotBox>
      {images.map((picture, idx) => {
        return (
          <Styled.Dot key={picture.id} className='fas fa-circle'></Styled.Dot>
        );
      })}
      <Styled.CurrentDot
        className='fas fa-circle'
        imageIndex={imageIndex}
      ></Styled.CurrentDot>
    </Styled.DotBox>
  );
};

export default SliderDot;

1.image의 갯수만큼 dot을 만들어주었고, 그위에 한개의 dot을 다른색으로 덮어위치를 확인할수있게 준비하였다.
2.image가 넘어갈때마다 translateValue가 바뀔것이고 width값인 70을 나누면 index를 알수있다. index값에 맞춰서 dot을 translate할수있게 css에 적용하였다.

5. 자동넘김 적용하기

useEffect(() => {
  setImageIndex(translateValue / 70);
  const imageInterval = setInterval(() => {
    moveRight();
  }, 3000);
  return () => {
    clearInterval(imageInterval);
  };
}, [translateValue]);

1.똑같이 translateValue가 바뀔때 setInterval을 사용하여 3초에 한번씩 넘어갈수있뀔때 setInterval을 사용하여 3초에 한번씩 넘어갈수있게 하였으며 return함수를 적용하여 clearInterval로 다시 삭제 하였다.

profile
내가 기억하기위한 블로그

0개의 댓글