프론트엔드/컴포넌트

부드러운 가로 스크롤링 (2)

syleemomo 2023. 7. 5. 07:20
728x90

기본적인 아이디어는 아래 링크로 걸어둔 Easing 기법을 이용한다. 

https://spicyyoghurt.com/tools/easing-functions

 

Easing Functions for JavaScript - Animation Tool | Spicy Yoghurt

Get easing function for JavaScript and try them out on your own custom motions, using the animation tool. You can create animations in an interactive way and see the effects of using different easing functions. When satisfied, look-up the matching equation

spicyyoghurt.com

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>자바스크립트 연습</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <div class="actions">
      <span class="prev"><</span>
      <span class="next">></span>
    </div>
    <div class="carousel">
      <div class="card">1</div>
      <div class="card">2</div>
      <div class="card">3</div>
      <div class="card">4</div>
      <div class="card">5</div>
      <div class="card">6</div>
      <div class="card">7</div>
      <div class="card">8</div>
      <div class="card">9</div>
      <div class="card">10</div>
      <div class="card">11</div>
      <div class="card">12</div>
      <div class="card">13</div>
      <div class="card">14</div>
      <div class="card">15</div>
      <div class="card">16</div>
      <div class="card">17</div>
      <div class="card">18</div>
      <div class="card">19</div>
      <div class="card">20</div>
    </div>
  </div>
 
  <script src='app.js'></script>
</body>
</html>
.container{
  width: 50%; height: 300px;
  position: relative;
}
span{
  position: absolute;
  top: 50%; transform: translateY(-50%);
  color: white;
  font-size: 2rem;
  background-color: rgba(0, 0, 0, .3);
  width: 50px; height: 50px;
  border-radius: 50%;
  text-align: center;
  cursor: pointer;
}
span.prev{
  left: 1rem;
}
span.next{
  right: 1rem;
}
.carousel{
  width: 100%; height: 100%;
  display: flex;
  grid-gap: 10px;
  background-color: bisque;
  overflow-x: scroll;
}
.card{
  min-width: 200px; height: 100%;
  background-color: aquamarine;
  text-align: center;
  line-height: 300px;
  font-weight: bold; 
  font-size: 10rem;
  color: white;
  /* border: 1px solid red; */
}

아래와 같이 setInterval 타이머를 걸어두고 스크롤이 끝나기 전에 다시 next 버튼을 클릭하면 타이머가 새로 생성되면서 timer id 가 새로 할당된다. 이렇게 되면 스크롤이 끝났을때 새로 생성된 타이머는 해제가 되지만 이전에 생성된 타이머는 ID 값을 더이상 조회할 수 없기 때문에 해제되지 않고 백그라운드에서 계속 실행된다. 그래서 스크롤이 멈추지 않고 반복적으로 계속 실행된다. 

const prevBtn = document.querySelector('.prev')
const nextBtn = document.querySelector('.next')
const carousel = document.querySelector('.carousel')
let timerId = null 
let currentTime = 0

function changePosition(unitTime, start, destination){
  currentTime += unitTime
  if(currentTime < 1){
    console.log('ongoing')
    carousel.scrollLeft = easeOutExpo(currentTime, start, destination, 1)
    // nextBtn.removeEventListener('click', toNextSlides)
  }else{
    console.log('end')
    currentTime = 0
    clearInterval(timerId)
    // nextBtn.addEventListener('click', toNextSlides)
  }
}
function toNextSlides(){
  timerId = setInterval(() => changePosition(0.03, 0, 500), 100)
}
prevBtn.addEventListener('click', function(){

})
nextBtn.addEventListener('click', toNextSlides)

function easeOutExpo (t, b, c, d) {
  return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
}

이런 문제를 해결하려면 스크롤이 되고 있는중에는 사용자가 버튼을 클릭하지 못하도록 잠깐 클릭 이벤트를 해제했다가 스크롤이 끝났을때 다시 등록해야 한다. 

const prevBtn = document.querySelector('.prev')
const nextBtn = document.querySelector('.next')
const carousel = document.querySelector('.carousel')
let timerId = null 
let currentTime = 0

function changePosition(unitTime, start, destination){
  currentTime += unitTime
  if(currentTime < 1){
    console.log('ongoing')
    carousel.scrollLeft = easeOutExpo(currentTime, start, destination, 1)
    nextBtn.removeEventListener('click', toNextSlides)
  }else{
    console.log('end')
    currentTime = 0
    clearInterval(timerId)
    nextBtn.addEventListener('click', toNextSlides)
  }
}
function toNextSlides(){
  timerId = setInterval(() => changePosition(0.03, 0, 500), 100)
}
prevBtn.addEventListener('click', function(){

})
nextBtn.addEventListener('click', toNextSlides)

function easeOutExpo (t, b, c, d) {
  return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
}
728x90