움직이는 배너 슬라이드 만들기

김현재·2022년 3월 11일
3
post-custom-banner

앞선 포스트의 디자인이 잘리고, 최종적으로 컨펌된 디자인은 화면안에서 배너가 컨베이어벨트처럼 움직여야 하는 디자인이 되었다.
계속 DOM을 조작해야되다 보니 성능 저하가 우려되었지만..
우선 내가 아는 방법을 총 동원해 만들어보았다.

원리

역시 carousel처럼 wrapper로 영역을 감싼 뒤, setInterval에 맞추어 translateX가 작동되도록 했다.
배너 하나가 화면에서 사라지면 배너의 맨 끝 부분에 사라진 배너가 붙도록 하였다.

.code

Mark Up

<div className={styles.bannerBG}>
  <div className={styles.bannerWrapper}>
    <div className={styles.imgWrapper}>
      {imgSource.map((img, idx) => (
        <img key={idx} className={styles.wrapperImg} id=
          {`${idx}`} src={img} alt='bannerImg' />
          ))}
    </div>
  </div>
</div>

imgSource에 이미지 주소를 배열로 저장해놓고 그것을 map을 돌려 뽑아내는 형태로 구현했다.

css

.bannerBG {
  background-color: #fbfcfe;
  height: 200px;
  overflow: hidden;
}

.bannerWrapper {
  position: relative;
}

.imgWrapper {
  position: absolute;
  display: flex;
  transition: all 0.5s ease-out;
}

.wrapperImg {
  /* width: 100px; */
  height: 150px;
  margin: 30px 50px;
  background-color: #fbfcfe;
  mix-blend-mode: darken;
  opacity: 30%;
}

배너 이미지 사이즈를 고정해서 모두 균일한 사이즈를 가지도록 했다.
그렇게 해야 나중에 자바스크립트로 계산할 때 편하다.
(가로 사이즈가 균일해야 몇 픽셀을 넘어갔을 때 추가해야할 지 알기도 편하기 때문에)

jsx(javascript)

const IMG_SOURCE = [ // 이미지 주소 모아놓은 곳
  'image01.jpg',
  'image02.jpg',
  'image03.jpg',
  'image04.jpg',
  'image05.jpg',
  'image06.jpg',
  'image07.jpg',
  'image08.jpg',
  'image09.jpg',
  'image10.jpg',
  'image11.jpg',
  'image12.jpg',
  'image13.jpg',
  'image14.jpg',
  'image15.jpg',
  'image16.jpg',
  'image17.jpg'
]
const [imgSource, setImgSource] = useState(IMG_SOURCE)
const [pixel, setPixel] = useState(0)
const interval: { current: NodeJS.Timeout | null } = useRef(null)

useEffect(() => {
  interval.current = setInterval(() => {
    setPixel((prev) => prev + 1)
  }, 50)
  return () => clearInterval(interval.current as NodeJS.Timeout)
}, [])

useEffect(() => {
  if (pixel % 100 === 0) {
    // 하나 지나갈때마다 하나 추가
    const idx = (pixel / 100) % 17 // 총 이미지가 17개여서, 17로 나누어 인덱스 구함
    const newImgArr = [...imgSource]
    const img = newImgArr[idx]
    newImgArr.push(img)
    setImgSource(newImgArr)
  }
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [pixel])

setInterval을 통해서 움직여야할 픽셀 값을 찾아내고,
픽셀값을 활용해서 일정 사이즈(여기선 100)를 지나칠 때마다 이미지 배열 맨 끝에다가 새로운 이미지를 추가해주도록 구성했다.

final Markup

<div className={styles.bannerBG}>
  <div className={styles.bannerWrapper}>
    <div className={styles.imgWrapper} style={{ transform: `translateX
    (-${pixel}px)` }}>
      {imgSource.map((img, idx) => (
       <img key={idx} className={styles.wrapperImg} id={`${idx}`} src=
         {img} alt='bannerImg' />
      ))}
    </div>
  </div>
</div>

최종적으로 inline으로 translateX를 건드려서 이미지 배열이 움직이도록 한다

구현 결과


직접 보고싶다면 https://mediaswitch.kr/main 에서 확인할 수 있다

고민해보아야 할 점

translateX를 사용해도 계속 DOM을 건드리는 형태인데 보다 성능에 최적화된 방법이 없을지 고민이다.

profile
쉽게만 살아가면 재미없어 빙고!
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 3월 3일

혹시 무한 순환되는 것도 구현해보셨나요?ㅠㅠ

답글 달기