반응형
자바스크립트를 사용한 슬라이드, requestAnimationFrame 사용, setTimeout / setInterval 사용하지 않음
좌우 버튼을 클릭해서 슬라이드 형태로 내용이 바뀌는 스크립트
html
<div class="imgwrap">
<div class="img_line">
<div class="img_area" :style="'transform:translateX('+ info.lx +'px)'">
<div class="imgbox">
<a href="#imglink" class="img_link" style="background-image:url(/img1.png)"></a>
<a href="#userlink" class="user_link">
<div class="img" style="background-image:url(/photo1.png)"></div>
<div class="userinfo">
<p class="title">제목1</p>
<p class="user">사용자1</p>
</div>
</a>
</div>
<div class="imgbox">
<a href="#imglink" class="img_link" style="background-image:url(/img2.png)"></a>
<a href="#userlink" class="user_link">
<div class="img" style="background-image:url(/photo2.png)"></div>
<div class="userinfo">
<p class="title">제목2</p>
<p class="user">사용자2</p>
</div>
</a>
</div>
<div class="imgbox">
<a href="#imglink" class="img_link" style="background-image:url(/img3.png)"></a>
<a href="#userlink" class="user_link">
<div class="img" style="background-image:url(/photo3.png)"></div>
<div class="userinfo">
<p class="title">제목3</p>
<p class="user">사용자3</p>
</div>
</a>
</div>
<div class="imgbox">
<a href="#imglink" class="img_link" style="background-image:url(/img4.png)"></a>
<a href="#userlink" class="user_link">
<div class="img" style="background-image:url(/photo4.png)"></div>
<div class="userinfo">
<p class="title">제목4</p>
<p class="user">사용자4</p>
</div>
</a>
</div>
</div>
</div>
<div class="btn_line">
<button class="arrowbtn left" :class="{disabled: info.imgNo === 0}" @click="showImg(-1)"><img src="/backward_img.svg" alt=""></button>
<button class="arrowbtn right" :class="{disabled: info.imgNo === info.imgLen-1}" @click="showImg(1)"><img src="/forward_img.svg" alt=""></button>
</div>
<div class="progress_line">
<div class="progress_bg">
<div class="progress_bar" :style="'width:'+ info.pcnt +'%'"></div>
</div>
<button class="btn_progress" :class="{on: info.playPause}" @click="bannerPlay"><img src="/play_img.svg" alt=""></button>
<p class="progress_txt"><span>{{info.imgNo + 1}}</span> / {{info.imgLen}}</p>
</div>
</div>
css
......
.btn_progress::after{content:"";position:absolute;left:0;top:0;display:flex;align-items:center;
justify-content:center;width:100%;height:100%;
background:url(/pause_img.svg) no-repeat center;opacity:0;
transition:opacity 0.2s ease;}
.btn_progress.on img{opacity:0;}
.btn_progress.on::after{opacity:1;}
......
js(vue)
const {createApp, ref, computed, onMounted} = Vue;
const visual = {
setup() {
// ref() 를 사용하면 js에선 .value로 찾아감. dom에는 .value 없음
// reactive() 는 항상 .value 필요없음
const info = ref({
imgNo: 0,// 이미지 위치
imgLen: null,// 전체 배너 개수
playPause: false, // 자동 슬라이드 여부
// 이미지 슬라이드 x위치
lx: computed(() => {
return -(info.value.imgNo * 400);// 400 = 이미지 영역 가로 길이
}),
// progressbar의 %
pcnt: computed(() => {
return Math.ceil((info.value.imgNo+1) / info.value.imgLen * 100);
}),
bannerPlay: null, // requestAnimationFrame 에서 timestamp 비교용
bannerPlayId: null, // requestAnimationFrame id
bannerTime: 4000, // delay time : 4초
})
const showImg = (no) => {
// 이전으로
if(no < 0){
info.value.imgNo < 0 ? 0 : info.value.imgNo -= 1;
}
// 다음으로
else{
info.value.imgNo > info.value.imgLen -1 ? info.value.imgLen -1 : info.value.imgNo += 1;
}
// 이전, 다음 버튼으로 이동 시 멈춤. 멈추면 timestamp 초기화해서 다시 n초를 처음부터
info.value.playPause = false;
info.value.bannerPlay = null;
cancelAnimationFrame(info.value.bannerPlayId);
}
const bannerPlay = () => {
// 자동 실행중이면 멈춤
if(info.value.playPause) {
info.value.playPause = false;
// 멈추면 timestamp 초기화해서 다시 n초를 처음부터
info.value.bannerPlay = null;
cancelAnimationFrame(info.value.bannerPlayId);
}
// 멈춰진 상태면 자동 실행
else {
info.value.playPause = true;
requestAnimationFrame(bannerMove);
}
}
bannerMove = (timestamp) =>{
if(!info.value.bannerPlay) info.value.bannerPlay = timestamp;
let timer = timestamp - info.value.bannerPlay;
//console.log(" : ", info.value.bannerPlay, " : ", timestamp, " : ", timer);
// n초마다 실행
if(timer < info.value.bannerTime){
info.value.bannerPlayId = requestAnimationFrame(bannerMove);
}
// n초 후
else {
// imgNo(현재 번호)가 전체 길이(예: 4)보다 작으면
if (info.value.imgNo < info.value.imgLen-1) {
// imgNo 증가
info.value.imgNo += 1;
}
// imgNo(현재 번호)가 전체 길이와 같으면
else if (info.value.imgNo === info.value.imgLen-1) {
// imgNo 초기화
info.value.imgNo = 0;
}
// 다시 n초를 적용하기 위해 timestamp 초기화
info.value.bannerPlay = timestamp;
requestAnimationFrame(bannerMove);
}
}
onMounted(() => {
// 임의로 tag의 개수를 찾아서 전체 개수를 체크하지만, 데이터를 받을 때 데이터 길이를 바로 넣어도 됨
// 전체 배너 개수
info.value.imgLen = document.querySelectorAll(".imgbox").length;
})
return {
info, showImg, bannerPlay,
}
}
};
createApp(visual).mount("#visual");
반응형
댓글