Leeyanggoo
[JS] 자바스크립트로 슬라이드 만들기 4!! 끝없는 가로 슬라이드 본문
cloneNode()와 appendChild() 알고 가자!
#header ul a:hover {
background-color: #000;
color: #fff;
transform: scale(1.2);
transition: all 0.5s;
}
#header li {
margin: 0 2px;
}
/* slider__wrap */
.slider__wrap {
width: 100%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.slider__img {
/* 이미지가 보이는 영역 */
position: relative;
width: 800px;
height: 520px;
overflow: hidden;
}
.slider__inner {
/* 전체 이미지를 감싸고 있는 박스 : 움직이는 영역 */
display: flex;
flex-wrap: wrap;
width: 4800px;
height: 520px;
}
.slider {
/* 개별적인 이미지 */
position: relative;
width: 800px;
height: 520px;
}
<main id="main">
<div class="slider__wrap">
<div class="slider__img">
<div class="slider__inner">
<div class="slider"><img src="./img/sliderEffect06-min.jpg" alt="이미지1"></div>
<div class="slider"><img src="./img/sliderEffect07-min.jpg" alt="이미지2"></div>
<div class="slider"><img src="./img/sliderEffect08-min.jpg" alt="이미지3"></div>
<div class="slider"><img src="./img/sliderEffect09-min.jpg" alt="이미지4"></div>
<div class="slider"><img src="./img/sliderEffect10-min.jpg" alt="이미지5"></div>
</div>
</div>
</div>
</main>
// 선택자
const sliderWrap = document.querySelector(".slider__wrap");
const sliderImg = sliderWrap.querySelector(".slider__img"); //보여지는 영역
const sliderInner = sliderWrap.querySelector(".slider__inner"); //움직이는 영역
const slider = sliderWrap.querySelectorAll(".slider"); //개별 이미지
let currentIndex = 0; //현재 보이는 이미지
let sliderCount = slider.length; //이미지 개수
let sliderWidth = slider[currentIndex].offsetWidth; //이미지 가로값
let sliderClone = sliderInner.firstElementChild.cloneNode(true); //복제할 첫 번째 이미지 (cloneNode는 복사해서 붙여넣음 안 쓰면 그냥 이동함 ㄷㄷ)
//복사한 첫 번째 이미지 마지막에 붙여넣기
sliderInner.appendChild(sliderClone)
let sliderInterval = 2000; //이미지 변경 간격
이번 예제는 각각의 슬라이드가 가로로 넘어가는 형태입니다.
slider__inner의 div의 width 값을 모든 슬라이드 width 값의 총합으로 설정했습니다.
slider__img에 overflow: hidden을 주어 넘치는 슬라이드가 가려지도록 설정했습니다.
cloneNode() 메서드는 복제한 요소의 모든 하위 요소와 속성 등을 복사합니다.
인자로 true를 전달하면, 하위 요소를 포함한 모든 자식 요소를 복사합니다.
인자로 false를 전달하면, 해당 요소만을 복사합니다.
슬라이드가 끝난 뒤에도 첫 번째 슬라이드가 이어져야 하기 때문에 첫 번째 자식 요소를 반환하는 firstElementChild를 이용해 첫 슬라이드를 선택했습니다.
appendChild()는 부모 요소의 마지막 자식 요소로 추가하는 메서드입니다.
복사(clone)한 첫 번째 슬라이드를 마지막 자식 요소로 추가하기 위해 사용합니다.
다양한 스크립트를 활용해보자!!
Javascript
function sliderEffect(){
currentIndex++;
sliderInner.style.transition = "all 0.6s";
sliderInner.style.transform = `translateX(-${sliderWidth*currentIndex}px)`
//마지막 이미지가 나오면 애니메이션 효과 멈추기
if(currentIndex == sliderCount){
setTimeout(() => {
sliderInner.style.transition = "all 0s";
sliderInner.style.transform = `translateX(0px)`
currentIndex = 0;
}, 700);
};
};
setInterval(sliderEffect, sliderInterval)
offsetWidth를 이용해 가져온 슬라이드의 width 값을 현재 이미지의 인덱스인 currentIndex를 곱한 만큼 X 축을 이동시킵니다.
마지막 슬라이드는 cloneNode와 appendChild를 이용했기 때문에 부모 요소의 마지막에 추가된 첫 번째 슬라이드가 됩니다.
따라서 마지막 이미지가 나오면(currentIndex == sliderCount //현재 슬라이드와 슬라이드의 총 개수가 같아지는 때) 애니메이션 효과를 멈춘 뒤 첫 번째 슬라이드를 보여주고, 다시 두 번째 애니메이션으로 가야 하기 때문에 setTimeout() 메서드를 이용합니다.
setTimeout() 메서드는 지정한 시간 뒤에 콜백 함수를 실행합니다.
콜백 함수로 슬라이드를 원래의 위치로 돌리는 translateX(0)을 사용하고, 이미지의 인덱스인 currentIndex를 다시 0으로 되돌립니다.
jQuery
function sliderEffect(){
currentIndex++;
// sliderInner.style.transition = "all 0.6s";
// sliderInner.style.transform = `translateX(-${sliderWidth*currentIndex}px)`
$(".slider__inner").css("position", "relative");
$(".slider__inner").animate({left : -sliderWidth*currentIndex}, 600)
//마지막 이미지가 나오면 애니메이션 효과 멈추기
if(currentIndex == sliderCount){
setTimeout(() => {
// sliderInner.style.transition = "all 0s";
// sliderInner.style.transform = `translateX(0px)`
$(".slider__inner").animate({left : 0}, 0)
currentIndex = 0;
}, 700);
};
};
setInterval(sliderEffect, sliderInterval)
jQuery는 Javascript의 querySelector()와 querySelectorAll()을 $("선택자")의 문법으로 씁니다.
자바스크립트에서 사용했던 선택자 const sliderInner = sliderWrap.querySelector(".slider__inner")가 $(".slider__inner")로 쓰입니다.
jQuery는 translateX라는 CSS 속성을 사용할 수 없습니다.
따라서 left의 값을 뺌으로써 X 좌표 값을 뺀 효과를 줄 수 있습니다.
slider__inner가 slider의 부모 div이므로 CSS 속성 position = relative를 준 뒤에, width을 슬라이더의 width만큼 빼고 있습니다.
animate의 마지막 숫자는 애니메이션의 지속시간인 duration을 의미합니다.
이후엔 자바스크립트와 동일하게 setTimeout() 메서드를 이용해 마지막 슬라이드가 나오면 애니메이션 진행을 멈추고, 슬라이드 진행을 원래대로 되돌리고 있습니다.
GSAP
function sliderEffect(){
currentIndex++;
gsap.to(sliderInner, {
x : -sliderWidth*currentIndex,
duration : 0.6, //첫 번째 duration이 delay보다 크면 중첩 실행됨.
ease : "none",
});
//마지막 이미지가 나오면 애니메이션 효과 멈추기
if(currentIndex == sliderCount){
gsap.to(sliderInner, {
delay: 0.7, //setTimeout의 지연시간
x : 0,
duration : 0,
onComplete: () => { //gsap는 onComplete 함수를 이용해 변수 이용
currentIndex = 0;
}
});
};
};
setInterval(sliderEffect, sliderInterval)
GSAP(GreenSock Animation Platform)은 HTML5 애니메이션을 만드는 데 사용되는 JavaScript 라이브러리입니다. GSAP은 자바스크립트를 사용하여 요소의 위치, 크기, 회전, 투명도 등 다양한 속성을 애니메이션화하는 데 사용됩니다.
GSAP는 요소에 애니메이션을 만들고 실행하기 위해 gsap.to()라는 문법을 사용합니다.
첫 번째 인자로 애니메이션 대상 요소를 선택하고, 두 번째 인자로 애니메이션 속성들을 가진 객체를 전달합니다.
예제에선 자바스크립트의 요소 활용을 통해 sliderInner 선택자를 활용하고 있습니다.
gsap.to() 애니메이션의 기본 timing은 ease로 설정되어 있기 때문에 동일한 슬라이드 진행 속도를 위해 "none"을 입력했습니다.
GSAP에서는 translateX의 CSS 속성을 알파벳 소문자 x 하나로 입력합니다.
duration으로 애니메이션의 지속 시간을 입력했습니다.
GSAP의 delay는 setTimeout의 밀리초와 같은 기능을 합니다. 즉 delay의 시간만큼 지연되는 효과를 얻습니다.
onComplete() 콜백 함수는 해당 애니메이션이 완료된 후에 원하는 코드를 진행하기 위해 실행합니다.
currentIndex는 sliderInner의 요소 속성이 아니기 때문에 변수 값의 변경을 위해 사용합니다.
duration과 delay
만약 첫 번째 duration 값이 delay 값보다 크다면, 애니메이션의 시작 시간과 종료 시간이 중첩되어 애니메이션의 흐름이 이상해질 수 있습니다.
GSAP의 to() 메서드는 기본적으로 애니메이션 실행 중에 새로운 애니메이션 명령을 받으면 중첩되어 실행됩니다. 따라서 첫 번째 애니메이션의 duration이 delay보다 길 경우, 두 애니메이션이 중첩되어 실행되어 원하는 효과를 얻지 못합니다.
따라서 첫 번째 duration이 delay 값보다 큰 경우, 두 번째 gsap.to()가 제대로 실행되지 않기 때문에 바로 두 번째 슬라이드로 넘어가게 됩니다.
만약 첫 번째 gsap.to()에서 onComplete() 콜백 함수를 통해 if문을 사용할 순 있지만, 이렇게 되면 매 슬라이드 애니메이션 완료마다 실행되므로 따로 if문을 사용했습니다.