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

김현재·2022년 3월 11일

앞선 포스트의 디자인이 잘리고, 최종적으로 컨펌된 디자인은 화면안에서 배너가 컨베이어벨트처럼 움직여야 하는 디자인이 되었다.
계속 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
쉽게만 살아가면 재미없어 빙고!

1개의 댓글

comment-user-thumbnail
2023년 3월 3일

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

답글 달기