개요

웹 사이트를 돌아다니다보면 광고 이미지가 한장 한장씩 일정 시간동안 보여지고 오른쪽으로 넘어가길 무한번 반복하는 것을 볼 수 있다.

어떻게 구현하는걸까?

원리

우선 이미지 슬라이드의 기본 원리는 이렇다.

이미지가 보일 공간을 만들고 바깥으로 빠져나오는 요소는 보이지 않게 한다.

overflow : hidden;을 사용하면 된다. 그러나 직접 보면서 작성하기 위해 일단은 사용하지 않은 모습이다.

그리고 요소들을 이어붙여서 마치 영화 필름과 같이 만든다.

스크린샷 2024-04-16 시간: 22 23 08

그리곤 overflow : hidden;을 적용하면 삐져나오는 요소들이 보이지않게된다.

그 다음엔 JS를 이용하여 조작해주면 된다.

이미지가 움직이는 것처럼 보이려면 ul을 div의 너비만큼 왼쪽으로 이동시키면 된다.

요소 자기 자신이 원래 정해진 위치에서 움직여야한다. -> 포지션 릴레이티브!

일단 시간을 지정하고 사용하지 않고 버튼을 누를시 작동하게 만들자면 이렇다.

스크린샷 2024-04-16 시간: 22 41 56

스크린샷 2024-04-16 시간: 22 42 07

overflow : hidden을 적용한 모습.

스크린샷 2024-04-16 시간: 22 43 27

애니메이션 적용

일정 시간 간격으로 정해둔 로직을 실행시키는 함수는 setInterval이 있다.

이를 이용해 애니메이션을 설정해본다면 이렇다.

<script>
    startSlide();
    function startSlide() {
    let count = 1
    setInterval(function(){
      const ul = document.querySelector('ul');
      ul.style.transition = '0.4s';
      ul.style.right = 100 * count + "px";
      count++;

      if(count === 3) {
        count = 0;
      }
    },1000);
 
    }   
  </script>

이렇게 무한 슬라이드가 구현되었다.

전체 소스코드

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <style>
    div {
      width : 100px;
      height : 100px;
      border : 1px solid black;
      overflow : hidden;   
      margin : 0px auto;
    }
    ul {
      position: relative;
      width : 300px;
      display : flex;
      padding : 0px;
      margin : 0px;
    }
    li {
      width : 100px;
      height : 100px;
      background-color: #ddd;
      list-style : none;
      border : 1px solid black;
      line-height : 100px;
      text-align : center;
    }
  </style>
</head>
<body>
  <div>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </div>
  <script>
    startSlide();
    function startSlide() {

    let count = 1
    setInterval(function(){
      const ul = document.querySelector('ul');
      ul.style.transition = '0.4s';
      ul.style.right = 100 * count + "px";
      count++;
      if(count === 3) {
        count = 0;
      }
    },1000);
 
    }   
  </script>
</body>
</html>

세상은 거짓말 투성이야

위의 방식처럼 애니메이션을 적용해보면 문제없이 작동한다.

그러나 살짝 아쉬운 부분이 있는데 바로 슬라이드의 맨 마지막에서 제일 처음의 슬라이드로 넘어갈 때 다소 부자연스러운 동작이 나온다는 것이다.

동작의 순서를 보면 1 -> 2 -> 3 -> 2 -> 1의 순서로 넘어간다.

3 -> 2 -> 1은 원래 1초에 한 슬라이드씩(+100px) 넘어가 right값이 300px인 것이 짧은 시간에 0px로 바뀌니 후다닥 넘어간다.

이를 자연스럽게 만들기위해 일종의 트릭을 이용해야한다.

일단 제일 처음의 슬라이드를 복사한뒤 맨 마지막 슬라이드 다음에 붙여넣는다.

그러면 1 -> 2 -> 3 -> 1 의 흐름이 만들어진다.

그런 다음 setTimeout을 이용하여 0.41s후 0.4s로 설정되어있는 transition을 0s로 만들고 위치를 제일 첫번째 슬라이드로 바꾼다.

즉, transition의 효과가 끝나고 마지막 슬라이드에 이어붙인 첫번째 슬라이드가 자리잡은 뒤 0.01초 후 transition을 끄고 제일 처음 슬라이드로 이동시키는 트릭을 사용할 수 있다.

그럼 transition이 사라진 상태에서 제일 처음의 슬라이드로 이동하기 때문에 1 -> 2 -> 3 -> 1로 왔을 때 애니메이션 효과가 사라지고 제일 처음의 슬라이드를 보여줌으로써 유저의 입장에서는 다음과 같이 보이게 된다.

1 -> 2 -> 3 -> 1 -> 2 -> 3 -> …. 무한 반복

이런 방법으로 자연스러운 무한 슬라이드를 구현할 수 있다.

최종 코드

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <style>
    div {
      width : 100px;
      height : 100px;
      border : 1px solid black;
      margin : 0px auto;
      overflow : hidden;
    }
    ul {
      position: relative;
      width : 400px;
      display : flex;
      padding : 0px;
      margin : 0px;
    }
    li {
      width : 100px;
      height : 100px;
      background-color: #ddd;
      list-style : none;
      border : 1px solid black;
      line-height : 100px;
      text-align : center;
    }
  </style>
</head>
<body>
  <div>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </div>
  <script>
    startSlide();
    function startSlide() {
    let count = 1
    const ul = document.querySelector('ul');
    let firstEl = ul.firstElementChild.cloneNode(true);
    ul.appendChild(firstEl);
    setInterval(function(){
      ul.style.transition = '.4s';
      ul.style.right = 100 * count + "px";
      count++;
      if(count === 4) {
       setTimeout(function() {
          ul.style.transition = '.0s';
          ul.style.right = "0px"
        }, 401);
        count = 1;
      }
    }, 1000);
    }   
  </script>
</body>
</html>