이전 애니메니션
※ 이 부분은 작성 예정입니다(사정상 순서가 뒤로 미뤄졌습니다.)
미리 보기
이번에 구현하는 애니메이션은 이전 시간에 이어서 무한 슬라이드 입니다. 이전과 다른 점이 있다면, 이전 방식은 첫 번째 슬라이드의 이미지를 확대하는 방식이었다면, 이번 슬라이드는 가운데 슬라이드 카드를 기준으로 게단식으로 깊이감이 생기는 애니메이션 입니다. 진짜 별거 없으니 잠깐 시간내셔서 알고 가세요. 분명 쓰이는 곳이 많을 겁니다.
HTML
마크업에서 핵심은 container 태그 안에 슬라이드 역할을 하는 div 태그를 7 개 정도 만들어주기만 하면 됩니다. 버튼은 슬라이드를 통제하는 버튼으로 굳이 추가적으로 언급할 것은 없으므로 아래 형식을 갖춰주시고 다음으로 넘어 갑시다.
<div class="container">
<div class='slide'>
</div>
<div class='slide'>
</div>
<div class='slide'>
</div>
<div class='slide'>
</div>
<div class='slide'>
</div>
<div class='slide'>
</div>
<div class='slide'>
</div>
</div>
<div class='buttons'>
<button class='prev'>
뒤로
</button>
<button class='next'>
앞으로
</button></div>
CSS
초기화 작업
제가 시리즈를 하면서 무조건 해주는 초기화작업입니다. margin 과 padding 을 0으로 조정하여 여백을 제거해주고, box-sizing을 border-box 로 지정하여, 콘텐츠의 사이즈를 결정할 때 여백(margin, padding)을 합산한 영역을 콘텐츠가 차지하는 사이즈가 되도록 설정해줍니다.
* {
margin:0;
padding:0;
box-sizing:border-box;
}
body 설정
이 부분도 늘 해주는 작업입니다. width 와 height 를 각각 100% 와 100vh 으로 지정하여 뷰포트 전체를 body 요소가 차지하도록 해주고, overflow 를 hidden 으로 지정하여 body 요소 내의 자식 요소가 부모인 body 바깥으로 삐죽 나오면서 생기는 스크롤을 미연에 방지해줍니다.
body {
width:100%;
height:100vh;
overflow:hidden;
}
슬라이드의 컨테이너 요소 설정
.container {
width:100%;
height:100%;
perspective:500px;
transform-style: preserve-3d;
}
여기서 주요한 속성은 perspective 와 transform-style : perserve-3d 입니다. perspective 속성은 실제 사용자가 멀리 있는 사물을 바라볼 때, 원근감을 표현하는 속성으로 500px 로 지정하는 경우 500px 지점 떨어진 사물을 사용자가 바라볼 때의 원근감을 표현하는 것으로 이해하면 됩니다.
transform-style의 perserve-style은 해당 .container 에 적용된 perspective 효과를 해당 요소가 가지고 있는 모든 자식요소에 상속하여 적용한다는 의미입니다.
컨테이너 역할을 하는 해당 요소의 경우도 body 와 마찬가지로 뷰포트의 전체영역에 가득찰 수 있도록 100% 씩 지정해주고 넘어 갑시다.
슬라이드 위치 초기화
이번에는 슬라이드 역할을 하는 요소의 사이즈와 위치를 설정하는 구간입니다.
.slide {
transition:0.5s;
width:200px;
box-shadow:0 10px 50px gray;
height:300px;
background:white;
border:1px solid rgba(0,0,0,0.1);
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%)
}
여기서 주요한 부분은 position을 absolute 로 지정하여 해당 요소가 현재 레이아웃의 흐름에 영향을 받지 않고 절대적인 위치(즉, 뷰포트의 좌측 최상단의 (0,0) 좌표 기준)에서 요소의 위치를 설정할 수 있게 해줍니다.
그리고 left와 top를 각각 50% 로 지정하여 (0,0) 좌표 기준으로 전체 뷰포트의 50% 만큼 위치를 이동시켜줍니다. 다만 이대로는 중간이 아니라 약간 더 나아가서 위치하게 되므로, 수직-수평 모두 중앙정렬이 되도록 transform의 translate(-50%,-50%) 을 지정해 줍니다. transform의 translate를 단어 그대로 옮기다 라는 의미가 있고, (x, y ) 의 값을 각각 인자로 받습니다.
나머지는 카드의 사이즈와 그림자 효과를 표현한 것인데, 크기의 경우에는 앞으로 설정할 속성에 오차적인 적용되도록 하기 위해서 그대로 따라와 주시는게 향후 진행에 더 편할 겁니다.
각 슬라이드의 위치 설정
이번에는 수평-수직으로 중앙정렬된 각 요소의 위치를 조작하여 깊이감을 만들어주는 작업을 할 것입니다.
일단 전체 코드는 아래와 같습니다.
.slide:nth-of-type(1),.slide:nth-of-type(2){
transform:translate(calc(-50%*5.1),-50%) translateZ(-100px);
}
.slide:nth-of-type(3){
transform:translate(calc(-50%*3.1),-50%) translateZ(-50px);
}
.slide:nth-of-type(4){
transform:translate(calc(-50%*1.1),-50%) translateZ(50px) ;
}
.slide:nth-of-type(5){
transform:translate(calc(-50%*-0.9),-50%) translateZ(-50px) ;
}
.slide:nth-of-type(6),.slide:nth-of-type(7){
transform:translate(calc(-50%*-2.9),-50%) translateZ(-100px) ;
}
우선 슬라이드1, 2는 모두 동일한 위치에 있도록 해주고, 좌측의 젤 끝 지점에 위치할 수 있도록 아래와 같이 설정해줍니다.
참고로 nth-of-type 은 동일한 유형(태그 이름)의 형제 간의 위치를 기준으로 요소를 일치시켜주는 의사 클래스 선택자 입니다. 여기서 translateZ는 요소의 앞과 뒤 위치를 이동시켜주는 메소드로 -100px 로 지정하는 경우 해당 요소가 뒤로 100px 정도 이동하게 됩니다.
.slide:nth-of-type(1),.slide:nth-of-type(2) {
transform:translate(calc(-50%*5.1),-50%) translateZ(-100px);
}
아래 부분들도 원리는 동일합니다. calc 메소드의 경우에는 CSS 에서 사용되는 계산기 함수로서 전달한 값을 계산한 결과를 반환해줍니다. 예를 들어 calc(250px*2) 가 있다면 500px 를 반환해주는 것이죠. 저의 경우는 이러한 원리를 기반으로 3.1, 1.1 등을 곱해주면서 위치를 찾아주는 작업을 일일이 해주었습니다. translateZ 값도 깊이감에 따라서 값을 50px 간격으로 지정해주었습니다.
.slide:nth-of-type(3){
transform:translate(calc(-50%*3.1),-50%) translateZ(-50px);
}
.slide:nth-of-type(4){
transform:translate(calc(-50%*1.1),-50%) translateZ(50px) ;
}
.slide:nth-of-type(5){
transform:translate(calc(-50%*-0.9),-50%) translateZ(-50px) ;
}
마지막 이 부분은 슬라이드 요소 1,2 를 같은 위치에 지정한 것과 동일한 이유입니다. 아래와 같이 지정해주고 넘어가 줍니다.
.slide:nth-of-type(6),.slide:nth-of-type(7){
transform:translate(calc(-50%*-2.9),-50%) translateZ(-100px) ;
}
슬라이드 통제 버튼 설정
버튼은 크게 설명할 부분이 없습니다 . 바로 사용할 수 있는 정도 까지만 꾸며 두었기에 본인의 취향에 맞춰서 꾸며주시면 됩니다.
/*버튼 */
.buttons {
position:absolute;
z-index:1;
bottom:1em;
left:50%;
transform:translate(-50%);
}
JS 작업
버튼과 컨테이너 요소 가져오기
컨테이너 역할을 하는 요소와 슬라이드를 통제하는 버튼들을 각각 가져와 줍니다.
const container = document.querySelector('.container');
const prev = document.querySelector('.prev')
const next = document.querySelector('.next')
이전 슬라이드 이동 버튼 설정
그 다음에 prev 버튼에 click 이벤트를 등록하고, 콜백함수 내부에 slide 요소들을 모두 가져오는 querySelectorAll 메소드를 사용해줍니다. 해당 메소드를 통해 반환되는 배열은 유사배열로서 NodeList 가 됩니다.
prev.addEventListener('click',()=>{
const slides = document.querySelectorAll('.slide')
container.append(slides[0])
})
container.append() 메소드를 호출하는 경우 slides[0]에 있는 요소가 container 태그의 마지막 자식 요소로 이동하게 됩니다. 이 때 변경사항을 즉시 반영해주기 위해, 콜백함수를 실행하고 종료됨과 동시에 다시 재호출 시 변경된 내역이 반영된 NodeList 를 받아와야 합니다.
따라서 콜백함수 내부에서 querySelectorAll 를 재호출함으로써 변경된 슬라이드 요소를 가져오는 작업을 수행 합니다.
다음 슬라이드 버튼 설정
이 경우도 마찬가지의 이유로 컨테이너 내부에서 querySelectorAll 을 사용하고 있습니다. 다른 점이 있다면 .prepend 메소드를 호출하고 있는 것 입니다. 해당 메소드는 전달한 요소를 부모요소의 첫 번째 자식 요소로 추가해주는 기능을 수행합니다. 즉, Next 버튼을 클릭하는 경우 마지막 자식 요소가 첫 번째 자식요소의 자리로 이동하게 됩니다.
next.addEventListener('click',()=>{
const slides = document.querySelectorAll('.slide')
container.prepend(slides[slides.length-1])
})
구현결과
여기 까지 오셨다면 이제 아래와 같은 결과물을 확인하실 수 있습니다. 코드를 글로 일일이 설명한 것을 읽기보다는 직접 코드를 하나하나 읽어가며 어떻게 동작하는지 살펴보는 것이 이해하기 쉬울 겁니다. 그러므로 여러 가지를 테스트 해보시면서 익숙해지는 시간을 가져보시길 권장드립니다. 정말 고생 많으셨습니다.
See the Pen 3d 슬라이드 by youngwan2 (@youngwan2) on CodePen.
다음 애니메이션 | 3D 슬라이드 카드 회전 애니메이션
'UI 디자인 애니메이션 연구소' 카테고리의 다른 글
CSS, JS를 이용한 웅장한 N-S(자석) 애니메이션 제작 (0) | 2024.06.07 |
---|---|
HTML,CSS 를 사용한 3D 카드 회전 애니메이션 만들기 (0) | 2024.06.01 |
HTML+CSS+JS로 초간단 무한 슬라이드 만들기 [1탄] (0) | 2024.05.28 |
HTML+CSS+JS 를 이용한 써클 프로그래스 애니메이션을 만들어 봅시다 (0) | 2024.05.20 |
CSS +JS 를 이용한 톡톡 튀는 폭죽(스파크) 애니메이션 만들기 (0) | 2024.05.09 |