관리 메뉴

공부기록용

슬라이드 만들기(slick.js 만들어보기02) 본문

💡깨달음💡/Javascript

슬라이드 만들기(slick.js 만들어보기02)

과부하가즈아 2024. 4. 4. 22:00

🔴무한슬라이드 구현하기

무한으로 슬라이드되도록 구현하고자 하는데 처음의 앞과 마지막의 뒤로 슬라이드가 더이상 이어지지 않는다.

 

이런 형태를 만들어주기위해 요소를 복제해 앞뒤로 넣어주도록 한다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="jquery-1.12.4.js"></script>
    <link rel="stylesheet" href="style.css" />
    <title>Document</title>
  </head>
  <body>
    <div class="contanier">
      <div class="slide">
        <ul>
          <li>
            <img src="/images/01.jpg" />
          </li>
          <li>
            <img src="/images/02.jpg" />
          </li>
          <li>
            <img src="/images/03.jpg" />
          </li>
          <li>
            <img src="/images/04.jpg" />
          </li>
          <li>
            <img src="/images/05.jpg" />
          </li>
        </ul>
      </div>
      <div class="btn">
        <button class="prev"><</button>
        <button class="stop">STOP</button>
        <button class="play">GO</button>
        <button class="next">></button>
      </div>
    </div>
    <script src="custom.js"></script>
  </body>
</html>
body {
  margin: 0;
}
ul,
li {
  margin: 0;
  padding: 0;
  list-style: none;
}
img {
  width: 400px;
}
button {
  padding: 0;
}
/*  */
body {
  position: relative;
  height: 100vh;
}
.contanier {
  width: 1240px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.slide {
  border: 5px solid #000;
  width: 100%;
  height: 250px;
  /* overflow: hidden; */
  position: relative;
}
ul {
  border: 1px solid red;
  overflow: hidden;
  /* width: 2080px; */
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}
li {
  float: left;
  margin-right: 20px;
}
li:last-child {
  margin-right: 0;
}
.btn {
  text-align: center;
  margin-top: 15px;
}
.btn > button {
  padding: 10px 15px;
  margin-left: -8px;
  width: 150px;
  border: 3px solid rgb(20, 98, 175);
  background-color: #fff;
  font-size: 18px;
  font-weight: 600;
}
.btn > button:hover {
  background-color: rgb(20, 98, 175);
  color: #fff;
}
.prev {
  margin-left: 0;
  border-radius: 10px 0 0 10px;
}
.next {
  border-radius: 0 10px 10px 0;
}

필요한 값 모두 변수로 지정

const prevBtn = document.querySelector(".prev");
const stopBtn = document.querySelector(".stop");
const nextBtn = document.querySelector(".next");

let slides = document.querySelector("ul");
let slide = document.querySelectorAll("ul li"); // li를 index로 가져옴
let currentIdx = 0;                             // 맨 처음 index는 0이니까 초기값으로 0을 잡아줌
let slideCount = slide.length;                  // li의 갯수를 판단함

const slideWidth = 400; // 설정한 너비값
const slideMargin = 20; // 너비의 margin-right값

 


A.cloneNode() -> A만 복사
A.cloneNode(true) -> A와 A의 자식요소들까지 모두 복사

A.appendChild(B) -> B를 A의 마지막 자식요소로 붙인다.
A.prepend(B) -> B를 A의 첫번째 자식요소로 붙인다.

 

복제를 위한 함수 만들기

function makeClone() {
  for(let i = 0; i < slideCount; i++){
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone")
    slides.appendChild(cloneSlide)
    
    //console.log(cloneSlide)
  }
}

for문으로 i를 돌림

i는 ul의 li의 갯수를 의미하는 slideCount - 1보다 작거나 같을 때까지 i를 늘려가면서 해당과정을 반복

(i는 index를 의미하므로 5째 슬라이드의 index는 4이므로)

해당과정

  • slide_즉, li의 배열의 0번째의 자식요소를 포함해 모두 복사
  • 복사된 그 요소에 clone라는 class를 부여
  • slides_즉, ul에 마지막 자식요소로 복사된 그요소를 붙여줌_이 과정의 반복
    • 0-1-2-3-4순으로 마지막이 바뀜

쭈륵 맨뒤로 붙어나간 것을 확인!


이제 앞으로도 붙여줘야함

function makeClone() {
  // 마지막뒤로 복사하기
  for (let i = 0; i <= slideCount - 1; i++) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.appendChild(cloneSlide);
    // console.log(cloneSlide)
  }

  // 앞의 앞으로 복사하기
  for (let i = slideCount - 1; i >= 0; i--) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.prepend(cloneSlide);
    // console.log(cloneSlide)
  }
}

// 함수를 자동으로 바로 실행하도록
makeClone();

역시 for문으로 돌림

이때는 반대로 i는 slideCount -1 즉, 4부터 시작 0과 같거나 클때까지 i_4에서 줄여가면서 해당과정을 반복

해당과정

  • slide_즉, li의 배열의 4번째의 자식요소를 포함해 모두 복사
  • 복사된 그 요소에 clone라는 class를 부여
  • slides_즉, ul에 첫번째 자식요소로 복사된 그요소를 붙여줌_이 과정의 반복
    • 4-3-2-1-0의 순으로 첫번째가 바뀌게 됨

html상 요소들이 복제는 되었지만 화면상에는 나타나지 않는다

 

왜냐하면 현재 ul의 너비가 li 5개를 품는 너비로 지정이 되어있기 때문이다


그래서 앞, 뒤로 복제한 요소를 포함하는 새로운 너비를 만들어주어야 한다.

 

새로운 너비 구하는 함수

const prevBtn = document.querySelector(".prev");
const stopBtn = document.querySelector(".stop");
const nextBtn = document.querySelector(".next");

let slides = document.querySelector("ul");
let slide = document.querySelectorAll("ul li"); // li를 index로 가져옴
let currentIdx = 0; // 맨 처음 index는 0이니까 초기값으로 0을 잡아줌
let slideCount = slide.length; // li의 갯수를 판단함

const slideWidth = 400; // 설정한 너비값
const slideMargin = 20; // 너비의 margin-right값

function makeClone() {
  ...
}
makeClone();

// 📌복사한 요소를 포함한 너비구하기
function updateWidth() {
  let currentSlide = document.querySelectorAll("ul li");
  let newSlideCount = currentSlide.length;

  let newWidth = (slideWidth + slideMargin) * newSlideCount - slideMargin + "px";

  slides.style.width = newWidth;
}

복제된 li를 가져와서 currentSlide로 지정을 해준다(배열로가져옴)

current의 길이로 li의 갯수를 파악

 

복제된 요소들을 포함한 전체 ul의 너비 newWidth구하기

(slideWidth + slideMargin) * newSlideCount - slideMargin + "px";

 

새로구한 newWidth을 ul의 slide의 style의 width로 지정해주기 

function makeClone() {
  // 마지막뒤로 복사하기
  for (let i = 0; i <= slideCount - 1; i++) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.appendChild(cloneSlide);
    console.log(cloneSlide)
  }

  // 앞의 앞으로 복사하기
  for (let i = slideCount - 1; i >= 0; i--) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.prepend(cloneSlide);
  }
  // 📌복사한 요소를 포함한 너비를 구하는 함수 불러주기
  updateWidth();
}

// 복사한 요소를 포함한 너비구하기
function updateWidth() {
  let currentSlide = document.querySelectorAll("ul li");
  let newSlideCount = currentSlide.length;

  let newWidth = (slideWidth + slideMargin) * newSlideCount - slideMargin + "px";

  slides.style.width = newWidth;
}

새로운 너비 구하는 함수를 요소를 복사한 구문 뒤로 넣어서 복제한 뒤 새로운 너비를 바로 구해서 slides의 너비로 지정해준다.

 

ul의 너비를 넓히니 복사한 요소들이 전부 보인다.

 

left는 0이기 때문에 이런 상태인 것이다.


원래의 요소를 향하도록 만들어줘야 한다.


ul의 위치를 아예 옮겨주어야 한다.

function setOriginPos() {
  let initialTranslateValue = -(slideWidth + slideMargin) * slideCount;
  
  slides.style.transform = "translateX(" + initialTranslateValue + "px)";
}

initialTranslateValue라는 변수에 앞에 붙은 5개의 요소의 전체 너비를 부여해준다.(마지막 요소의 margin-right도 포함)

-(slideWidth + slideMargin) * slideCount;

 

마지막으로 slides_ul의 위치를 옮겨주기 위해 X축, 즉 가로로 앞 요소의 전체너비 만큼 옮겨야 하므로

transform: translateX(-전체너비px);을 해준다.


이 역시 요소를 앞뒤로 복제하고 전체너비를 구한뒤 이 함수도 바로 실행시켜줘야 하므로 요소를 복제한 함수 안에서 전체너비를 구한 함수 뒤에 해당 함수를 넣어준다.

function makeClone() {
  // 마지막뒤로 복사하기
  for (let i = 0; i <= slideCount - 1; i++) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.appendChild(cloneSlide);
    console.log(cloneSlide)
  }

  // 앞의 앞으로 복사하기
  for (let i = slideCount - 1; i >= 0; i--) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.prepend(cloneSlide);
  }
  updateWidth();
  // 📌 앞으로 복사된 요소로 향한 slides_ul의 위치 원래의 요소로 향하도록
  setOriginPos();
}

// 복사한 요소를 포함한 너비구하기
function updateWidth() {
  let currentSlide = document.querySelectorAll("ul li");
  let newSlideCount = currentSlide.length;

  let newWidth = (slideWidth + slideMargin) * newSlideCount - slideMargin + "px";

  slides.style.width = newWidth;
}

// 📌복사한 요소가 아닌 원래의 요소로 slides_ul의 위치잡아주기
function setOriginPos() {
  let initialTranslateValue = -(slideWidth + slideMargin) * slideCount;

  slides.style.transform = "translateX(" + initialTranslateValue + "px)";
}

const prevBtn = document.querySelector(".prev");
const stopBtn = document.querySelector(".stop");
const nextBtn = document.querySelector(".next");

let slides = document.querySelector("ul");
let slide = document.querySelectorAll("ul li"); // li를 index로 가져옴
let currentIdx = 0; // 맨 처음 index는 0이니까 초기값으로 0을 잡아줌
let slideCount = slide.length; // li의 갯수를 판단함

const slideWidth = 400; // 설정한 너비값
const slideMargin = 20; // 너비의 margin-right값

새로 정비된 요소들에 맞는 슬라이드 동작함수를 만들어준다.

// 슬라이드 동작 함수
function moveSlide(num) {
  slides.style.left = -num * (slideWidth + slideMargin) + "px";
  currentIdx = num;
  console.log(currentIdx, slideCount);
}

// 버튼 동작 
nextBtn.addEventListener("click", function () {
  moveSlide(currentIdx + 1);
});

prevBtn.addEventListener("click", function () {
  moveSlide(currentIdx - 1);
});

currentIdx의 초기 값을 0으로 부여한 상태에서

 

nextBtn은 moveSlide(0 + 1) -> num_1 -> left: -1 * 420px  -420 -840``` 이 되어가고,

prevBtn은 moveSlide(0 - 1) -> num_-1 -> left: 1 *420px ``` 420 840 1260이 되어간다.

 

이때 num은 currentIdx에 부여해줄 수 있도록 한다.

 

슬라이드의 갯수는 li의 길이로 5값이므로 이 값을 기준으로 기존의 요소가 0 1 2 3 4 앞에 복사된 요소는 -5 -4 -3 -2 -1이될 것이고, 뒤에 복사된 요소는 5 6 7 8 9 10으로 뻗어간다.


// 슬라이드 동작 함수
function moveSlide(num) {
  slides.style.left = -num * (slideWidth + slideMargin) + "px";
  currentIdx = num;
  console.log(currentIdx, slideCount);

  // 마지막일때
  if (currentIdx == slideCount || currentIdx == -slideCount) {
    slides.style.left = "0px";
    currentIdx = 0;
  }
}

// 버튼 동작 
nextBtn.addEventListener("click", function () {
  moveSlide(currentIdx + 1);
});

prevBtn.addEventListener("click", function () {
  moveSlide(currentIdx - 1);
});


transition추가하기⭐

ul {
  border: 1px solid red;
  overflow: hidden;
  /* width: 2080px; */
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  transition: 0.5s; /*📌*/
}

ul에 transition을 주게 되면 로딩할때마다 해당 tansition이 계속해서 발생하게 되는데 


ul {
  border: 1px solid red;
  overflow: hidden;
  /* width: 2080px; */
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}
/*📌*/
ul.animated {
  transition: 0.5s;
}

class를 부여하는 방식으로 애니메이션을 관리하면 페이지 로딩 시에 불필요한 애니메이션을 피할 수 있다.


부여한 class를 사용해줘야 한다.

function makeClone() {
  // 마지막뒤로 복사하기
  for (let i = 0; i <= slideCount - 1; i++) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.appendChild(cloneSlide);
  }

  // 앞의 앞으로 복사하기
  for (let i = slideCount - 1; i >= 0; i--) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.prepend(cloneSlide);
  }
  // 복사한 요소를 포함한 너비
  updateWidth();
  // 앞뒤로 복사한 요소가 아닌 원래의 요소의 위치를 다시 잡아주기
  setOriginPos();

  /*📌*/
  setTimeout(function () {
    slides.classList.add("animated");
    console.log("0.1")
  }, 100);
}
makeClone();

처음에 앞뒤로 요소를 복사하고 복사한 요소들을 포함한 전체너비를 다시 구해주고 ul의 위치를 다시 잡은 뒤에 class를 부여해준다. 쨋든 ul에 class가 추가되면서 transition을 갖게 되는것이다. 


// 슬라이드 동작 함수
function moveSlide(num) {
  slides.style.left = -num * (slideWidth + slideMargin) + "px";
  currentIdx = num;

  // 마지막일때
  if (currentIdx == slideCount || currentIdx == -slideCount) {
    /*📌*/
    setTimeout(function () {
      slides.classList.remove("animated"); // transition빼주고
      slides.style.left = "0px";
      currentIdx = 0;
      console.log("0.5")
    }, 500);
    /*📌*/
    setTimeout(function () {
      slides.classList.add("animated");    // 0.1초뒤에 바로 transition넣어주고
      console.log("0.6")
    }, 600);
  }
}

nextBtn.addEventListener("click", () => {
  moveSlide(currentIdx + 1);
});

prevBtn.addEventListener("click", () => {
  moveSlide(currentIdx - 1);
});

ul의 위치가 처음과 끝에 도달했을때 맨 처음의 상태를 만들어 주는데 이때 시간차를 두어 class를 제거했다가 바로 0.1로의 시간의 텀을 두고 다시 class를 부여해서 transition을 가질 수 있게 해준다.

 

tansition을 가지고 left: 0이 된다면 처음과 끝에서 다시 0으로 돌아가는 그 과정도 transition을 가지게 되기때문에 없앴다가 바로 다시 class를 줌으로써 transition을 다시 가질 수 있게 하는 것이다.

const prevBtn = document.querySelector(".prev");
const stopBtn = document.querySelector(".stop");
const nextBtn = document.querySelector(".next");

let slides = document.querySelector("ul");
let slide = document.querySelectorAll("ul li"); 
let currentIdx = 0; 
let slideCount = slide.length; 

const slideWidth = 400; // 설정한 너비값
const slideMargin = 20; // 너비의 margin-right값

function makeClone() {
  // 마지막뒤로 복사하기
  for (let i = 0; i <= slideCount - 1; i++) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.appendChild(cloneSlide);
  }

  // 앞의 앞으로 복사하기
  for (let i = slideCount - 1; i >= 0; i--) {
    let cloneSlide = slide[i].cloneNode(true);
    cloneSlide.classList.add("clone");
    slides.prepend(cloneSlide);
  }
  // 복사한 요소를 포함한 너비
  updateWidth();
  // 앞 뒤로 복사한 요소가 아닌 원래의 요소의 위치를 다시 잡아주기
  setOriginPos();

  setTimeout(function () {
    slides.classList.add("animated");
    console.log("0.1")
  }, 100);
}
makeClone();

// clone으로 복사한 요소를 포함한 너비
function updateWidth() {
  let currentSlide = document.querySelectorAll("ul li");
  let newSlideCount = currentSlide.length;
  let newWidth = (slideWidth + slideMargin) * newSlideCount - slideMargin + "px";
  slides.style.width = newWidth;
}

// 복사한 요소가 아닌 원래의 요소의 위치잡아주기
function setOriginPos() {
  let initialTranslateValue = -(slideWidth + slideMargin) * slideCount;
  slides.style.transform = "translateX(" + initialTranslateValue + "px)";
}

// 슬라이드 동작 함수
function moveSlide(num) {
  slides.style.left = -num * (slideWidth + slideMargin) + "px";
  currentIdx = num;

  // 마지막일때
  if (currentIdx == slideCount || currentIdx == -slideCount) {
    setTimeout(function () {
      slides.classList.remove("animated");  // transition빼주고
      slides.style.left = "0px";
      currentIdx = 0;
      console.log("0.5")
    }, 500);
    setTimeout(function () {
      slides.classList.add("animated");  // 0.1초뒤에 바로 transition넣어주고
      console.log("0.6")
    }, 600);
  }
}

nextBtn.addEventListener("click", () => {
  moveSlide(currentIdx + 1);
});

prevBtn.addEventListener("click", () => {
  moveSlide(currentIdx - 1);
});
Comments