Language/React

React 라이브러리 없이 캐러셀 구현하기

conqueror-G 2022. 11. 28. 22:00

애니메이션 없는 캐러셀

 

SKill 사용이유
Emotion CSS Prop을 사용하기 위해서
React -
Typescript 버그 방지

 

import { Fragment, ReactNode, useState } from "react"

// 더미 데이터
const dummyData = Array.from({ length: 22 }).map((_,index) =>({
  name: `obj${index + 1}`
}))

// 메인 컴포넌트
const Home = () => {
  // 슬라이드 넘길 때 showCount만큼 +
  const [ currentSlide, setCurrentSlide ] = useState<number>(0)
  // 총 데이터의 개수
  const slideCount = dummyData.length
  // 화면에 보여주고 싶은 개수
  const showCount = 4
  // 슬라이드 아이템 개수 조절
  const filterData = dummyData.filter((_, index) => index >= currentSlide && index < currentSlide + showCount)

  // 다음으로 넘기기 함수
  const handleArrowButtonNext = () => {
    setCurrentSlide(currentSlide + showCount)
  }

  // 이전으로 넘기기 함수
  const handleArrowButtonPrev = () => {
    setCurrentSlide(currentSlide - showCount)
  }

  return (
    <Card css={{
      position: 'relative',
      display: 'flex'
    }}>
      <PrevArrowButton currentSlide={currentSlide} onClick={handleArrowButtonPrev} />
      <Item data={filterData} />
      <NextArrowButton 
        currentSlide={currentSlide} 
        showCount={showCount} 
        slideCount={slideCount} 
        onClick={handleArrowButtonNext}
      />
    </Card>
  )
}

export default Home



interface CardProps {
  children: ReactNode
  className?: string
}

// Card( Wrapper )
const Card = ({ children, className }: CardProps) => {
  return (
    <div css={{
      boxSizing: 'border-box',
      padding: 10,
      borderRadius: 10,
      backgroundColor: '#d9d9d9',
    }}
      className={className}
    >
      {children}
    </div>
  )
}

interface ItemProps {
  data: {
    name: string
  }[]
}

// Item( 이미지나 등등 )
const Item = ({ data }: ItemProps) => {
  return (
    <Fragment>
      { data.map((el) => (
          <div
            key={el.name}  
            css={{
              position: 'relative',
              flex: 1,
              height: '100%',
              margin: '0 10px'
            }}>
            <div css={{
              position: 'absolute',
              left: 0,
              right: 0,
              top: 0,
              backgroundColor: '#00000099'
            }}>
              { el.name }
            </div>
            <div css={{
              height: 300,
              backgroundColor: 'lightgreen'
            }} />
          </div>
        )
      )}
    </Fragment>
  )
}

interface NextArrowButtonProps {
  /**
   * @optional
   *
   * @description
   *
   */
  className?: string
  /**
   * @requierd
   *
   * @description
   * Arrow button에 대한 onClick 기능
   */
  onClick: React.MouseEventHandler<HTMLButtonElement>
  /**
   * @requierd
   *
   * @description
   * 슬라이드 총 아이템 개수
   */
  slideCount: number
  /**
   * @requierd
   *
   * @description
   * 페이지네이션 같은 개념으로 이해
   * 0에서 시작
   * 한번 슬라이드를 넘기면,showCount 만큼 증가,
   * (slideCount % showCount === 0)이면, ( 최대 값이 slideCount - 5 )
   * (slideCount % showCount !== 0)이면, 최대 값이 slideCount
   */
  currentSlide: number
  /**
   * @required
   *
   * @description
   * 한 화면 안의 아이템 개수
   */
  showCount: number
}

// 슬라이드 다음으로 넘기기 버튼
const NextArrowButton = 
  ({ className, onClick, slideCount, currentSlide, showCount }: NextArrowButtonProps) => {
    /**
     * @description
     * 조건 1: 데이터가 딱 떨어지지 않을 때
     * !(slideCount - currentSlide < showCount)
     * 조건 2: 데이터가 딱 떨어질 때
     * currentSlide !== (slideCount as number) - showCount
     */
    return (
      <Fragment>
        {currentSlide !== (slideCount as number) - showCount &&
          !(slideCount - currentSlide < showCount) && (
            <button
              className={className}
              onClick={onClick}
              css={{
                zIndex: 10,
                position: 'absolute',
                top: '50%',
                right: 30,
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                width: 30,
                height: 30,
                borderRadius: '50%',
                border: 'none',
                backgroundColor: 'white',
                transform: 'translateY(-50%)',
                cursor: 'pointer',
              }}
            >
              {`>`}
            </button>
          )}
      </Fragment>
    )
  }

  interface PrevArrowButtonProps {
    /**
     * @description
     *
     */
    className?: string
    /**
     * @requierd
     *
     * @description
     * 페이지네이션 같은 개념으로 이해
     * 0에서 시작
     * 한번 슬라이드를 넘기면,showCount 만큼 증가,
     * (slideCount % showCount === 0)이면, ( 최대 값이 slideCount - showCount )
     * (slideCount % showCount !== 0)이면, 최대 값이 slideCount
     */
    currentSlide: number
    /**
     * @requierd
     *
     * @description
     * Arrow button에 대한 onClick 기능
     */
    onClick: React.MouseEventHandler<HTMLButtonElement>
  }
  
  // 슬라이드 이전으로 넘기기 버튼
  const PrevArrowButton = 
    ({ className, currentSlide, onClick }: PrevArrowButtonProps) => {
  
      return (
        <Fragment>
          {currentSlide !== 0 && (
            <button
              className={className}
              onClick={onClick}
              css={{
                zIndex: 10,
                position: 'absolute',
                top: '50%',
                left: 30,
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                width: 30,
                height: 30,
                borderRadius: '50%',
                border: 'none',
                backgroundColor: 'white',
                transform: 'translateY(-50%)',
                cursor: 'pointer',
              }}
            >
              {`<`}
            </button>
          )}
        </Fragment>
      )
    }

타입스크립트네.. 에이 어려워 넘겨!가 아니라 타입 부분만 제거하고 사용해보십시오.

주석을 달아놓긴 했으나, 코드가 너무 어렵다면 일단 복사해가서 이것저것 만져보세요!