자바스크립트 슬라이드 배너 - jabaseukeulibteu seullaideu baeneo

2021. 7. 10. 18:28

요즘 쇼핑몰/영화/웹서비스... 홈페이지에 들어가 보면 아래와 같이 메인배너에 슬라이더를 넣는 경우를 많이 보실 수 있습니다. (CGV)

자바스크립트 슬라이드 배너 - jabaseukeulibteu seullaideu baeneo

이런 슬라이더를 구현한 오픈소스는 매우 많지만, 메인 배너인 만큼 디자이너의 요구사항에 맞게 화면을 만들어 주어야 하기 때문에, 화면의 스타일을 입맛에 맞게 잘 바꾸어 주어야 합니다.

스타일을 자유롭게 변경해야하기 때문에, 슬라이더는 각각 사이트마다 별도로 구현하는 경우가 많습니다. 그래서 이번 포스팅에서는 슬라이더의 뼈대를 만드는 방법을 설명하려고 합니다.

자바스크립트 슬라이드 배너 - jabaseukeulibteu seullaideu baeneo

슬라이더의 뼈대를 만드는 방법

우선 슬라이더는 디자이너가 만든 화면을 넘기면서 보여주는 화면이기 때문에, HTML 코드로 먼저 열심히 퍼블리싱을 한 다음에 적용해야 합니다. 제 옆에는 ... 디자이너분이 없기 때문에 매우 단순한 숫자 카드로 작업을 진행하도록 하겠습니다.

슬라이더에 넣을 웹 화면 작성

정말 예쁘지 않은 숫자카드의 CSS를 작성해줍니다.

<style>
.slider {
  width: 300px;
  height: 300px;
}
.box {
  text-align: center;
  line-height: 300px;
  font-size: 140px;
  color: #fff;
  font-weight: bold;
}
</style>

이어서 이에 맞는 HTML 코드를 작성해 줍니다. 이 때, 겉에 감싸고있는 section 태그와 그 하위에는 보여주고 싶은 메인 배너 div를 넣어주면 됩니다.

section 태그 - 배너 영역

div 태그 - 배너 한장

<section id="slider1" class="slider">
  <div class="box" style="background-color: #000;">1</div>
  <div class="box" style="background-color: #000;">2</div>
  <div class="box" style="background-color: #000;">3</div>
  <div class="box" style="background-color: #000;">4</div>
  <div class="box" style="background-color: #000;">5</div>
</section>

슬라이더의 상태관리 코드 추가

슬라이더에 필요한 상태값은 다음과 같습니다.

현재 페이지 (index)

슬라이더 이동이 완료되었는가? (isMoved)

슬라이더의 이동 속도는 얼마나 빠른가? (speed)

// 상태
let index = 1;
let isMoved = true;
const speed = 1000; // ms

슬라이더의 속도와 방향

슬라이더를 세로 / 가로 중에서 어떤 방향으로 진행할 것인지 정해야 하는데, 저는 입력을 받아서 방향을 결정하는 것으로 작성하였습니다.

// 속도
const transform = "transform " + speed / 1000 + "s";

// 방향
let translate = (i) => "translateX(-" + 100 * i + "%)";
if (type === "V") {
  translate = (i) => "translateY(-" + 100 * i + "%)";
}

슬라이더를 구성하는 요소 만들기

배너 슬라이더를 구성하는 돔 요소에는 배너 영역(slider) / 컨테이너(container) / 배너 여러장(boxes) 이 있습니다.

배너 영역 (slider)

target 은 배너 영역의 클래스(class) 또는 아이디(id) 값이 들어갑니다. 현재 HTML 코드에서는 #slider 가 될 것입니다.

// 슬라이더
const slider = document.querySelector(target);
const sliderRects = slider.getClientRects()[0];
slider.style["overflow"] = "hidden";

컨테이너 (container)

이전에 HTML 코드에서는 배너 영역 안에 바로 배너들을 집어넣어 주었습니다. 하지만 슬라이더를 만들러면 이 배너들을 감싸는 컨테이너를 하나 만들어서 포장해 주어야 합니다. 따라서, 돔을 만드는 함수인 document.createElement 함수를 사용하여 컨테이너를 만들어줍니다.

// 슬라이더 화면 컨테이너
const container = document.createElement("div");
container.style["display"] = "flex";
container.style["flex-direction"] = type === "V" ? "column" : "row";
container.style["width"] = sliderRects.width + "px";
container.style["height"] = sliderRects.height + "px";
container.style["transform"] = translate(index);

배너 여러장 (boxes)

숫자 카드가 1 ~ 5 까지 있다고 하면, 연속되는 슬라이더를 보여주기 위해서, 맨 앞에는 5를 맨 뒤에는 1을 추가하여 다시 배너 목록을 구성해 줍니다.

자바스크립트 슬라이드 배너 - jabaseukeulibteu seullaideu baeneo
// 슬라이더 화면 목록
let boxes = [].slice.call(slider.children);
boxes = [].concat(boxes[boxes.length - 1], boxes, boxes[0]);

// 슬라이더 화면 스타일
const size = boxes.length;
for (let i = 0; i < size; i++) {
  const box = boxes[i];
  box.style["flex"] = "none";
  box.style["flex-wrap"] = "wrap";
  box.style["height"] = "100%";
  box.style["width"] = "100%";
  container.appendChild(box.cloneNode(true));
}

이어서, 눈속임 이벤트를 2가지 적용합니다.

container.addEventListener("transitionend", function () {
  // 처음으로 순간이동
  if (index === size - 1) {
    index = 1;
    container.style["transition"] = "none";
    container.style["transform"] = translate(index);
  }
  // 끝으로 순간이동
  if (index === 0) {
    index = size - 2;
    container.style["transition"] = "none";
    container.style["transform"] = translate(index);
  }
});

화면이 5 에서 1(복사)로 이동한 후, 트랜지션의 애니메이션을 잠시 꺼두고 1(원본) 카드 위치로 순간이동하고,

// 처음으로 순간이동
if (index === size - 1) {
  index = 1;
  container.style["transition"] = "none";
  container.style["transform"] = translate(index);
}

화면이 1에서 5(복사)로 이동하면 트랜지션의 애니메이션을 잠시 꺼두고 5(원본) 카드 위치로 순간이동 합니다.

// 끝으로 순간이동
if (index === 0) {
  index = size - 2;
  container.style["transition"] = "none";
  container.style["transform"] = translate(index);
}

그런데, 이 순간이동하는 속도보다 슬라이드를 이동하는 함수가 더 빨리 실행된다면 눈속임을 구현할 수 없습니다. 그래서 애니메이션의 속도에 맞춰서 트랜지션이 시작함과 동시에 애니메이션 시간(speed) 동안은 슬라이드 이동 함수를 실행할 수 없도록 제약을 걸어줍니다.

// 처음/마지막 화면 눈속임 제약
container.addEventListener("transitionstart", function () {
  isMoved = false;
  setTimeout(() => {
  	isMoved = true;
  }, speed);
});

마지막으로 배너 영역(slider)에 컨테이너(container)를 붙여줍니다.

// 슬라이더 붙이기
slider.innerHTML = "";
slider.appendChild(container);

슬라이더 이벤트 함수 만들기

슬라이더에는 기본적으로 다음(next)/이전(prev)/특정배너이동(move) 이 세가지를 제공합니다. 슬라이더에 페이지를 표시하거나, 이동하는 화살표 버튼이 있는 경우도 많이 있는데, 그 부분은 페이지마다 너무 다르기 때문에, 뼈대 만들기에 어울리지 않다고 생각되므로 생략하겠습니다.

move: function (i) {
  if (isMoved === true) {
    index = i;
    container.style["transition"] = transform;
    container.style["transform"] = translate(index);
  }
},
next: function () {
  if (isMoved === true) {
    index = (index + 1) % size;
    container.style["transition"] = transform;
    container.style["transform"] = translate(index);
  }
},
prev: function () {
  if (isMoved === true) {
    index = index === 0 ? index + size : index;
    index = (index - 1) % size;
    container.style["transition"] = transform;
    container.style["transform"] = translate(index);
  }
}

See the Pen Pure javascript slider by 홍지성 (@lnbvocxe) on CodePen.