반응형
250x250
Notice
Recent Posts
Recent Comments
Link
관리 메뉴

ZeroToHunnit Coding

[Javascript] 다양한 기능들로 만든 멋진 scroll이벤트 본문

JAVASCRIPT

[Javascript] 다양한 기능들로 만든 멋진 scroll이벤트

0에서100 2024. 1. 4. 16:50
728x90
반응형

 

위와 같이 스크롤 함에 따라 큰 박스가 3개로 나눠지고 뒤집어져서 뒷면이 나오는 기능을 구현해봤다.

 

HTML

  <div class="section">
    <div class="wrapper">
      <div class="grid">
        <div class="card-box">
          <div class="card-front">
            <p>카드1</p>
          </div>
          <div class="card-back">
            <p>뒷면</p>
          </div>
        </div>
        <div class="card-box">
          <div class="card-front">
            <p>카드2</p>
          </div>
          <div class="card-back">
            <p>뒷면</p>
          </div>
        </div>
        <div class="card-box">
          <div class="card-front">
            <p>카드3</p>
          </div>
          <div class="card-back">
            <p>뒷면</p>
          </div>
        </div>
        <div class="front">스크롤 ㄱㄱ</div>
      </div>
    </div>
  </div>

 

CSS

.section{
  height: 6000px;
}

.wrapper{
  width: 100%;
  height: 100vh;
  position: sticky;
  top: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: black;
}

.grid{
  display: grid;
  width: 1200px;
  height: 50vh;
  grid-template-rows: 1fr;
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-columns: 1fr;
  align-content: center;
  justify-content: center;
  justify-items: center;
  margin-left: auto;
  margin-right: auto;
  position: relative;
}

.card-box{
  width: 100%;
  height: 100%;
  position: relative;
}

.card-front{
  width: 100%;
  height: 100%;
  background-color: aquamarine;
  transform-style: preserve-3d;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 16px;
  perspective: 300px;
  font-size: 60px;
}

.card-back{
  width: 100%;
  height: 100%;
  background-color: blue;
  transform-style: preserve-3d;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 16px;
  perspective: 300px;
  transform: rotateY(180deg);
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  font-size: 60px;
}

.front{
  position: absolute;
  top: 0;
  left: 0;
  background-color: aquamarine;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 60px;
}

 

우선 section으로 height를 크게 잡아주고 wrapper로 100vw, 100vh의 크기로 position: sticky를 걸어주었다.

그리고 grid 박스는 position:grid로 card-box를 세개로 나누어주었다.

card-box에 position:relative를 걸고 안에 card-front와 card-back을 넣어주고 card-back만 position:absolute를 하여 z-index를 -1로 하여 안보이게 한 후 rotateY로 180도 미리 회전시켜주었다.

그리고 맨 앞에 깔아줄 front 박스를 만들어 position:absolute로 띄어준다. 일종의 트릭이라고 할 수 있다.

 

* 사실 card-box 3개가 붙어있다가 단순히 떨어지는 것뿐만 아니라 front도 opacity로 조절해준다. 이렇게 한 이유는 지금은 단색으로 background색이 정해져있지만, 만약 사진이라고 가정하면 그냥 div 세개를 떼어냈다가 붙였다 하는 것보다 앞에 가려주고 opacity로 조절하는 것이 더 자연스러울 것이다.

 

JAVASCRIPT

window.addEventListener('scroll', function () {
  let frontOpacity = -1 / 200 * window.scrollY + 2.5;
  let gridGap = 3 / 20 * window.scrollY - 45;
  let cardRotate = 3 / 10 * window.scrollY - 210;

  if (window.scrollY >= 300 && window.scrollY <= 500) {
    document.querySelector('.front').style.opacity = frontOpacity;
    document.querySelector('.grid').style.gap = `${gridGap}px`;
  }
  if (window.scrollY >= 700 && window.scrollY <= 1300) {
    if (cardRotate >= 90) {
      document.querySelectorAll('.card-front').forEach(function (card) {
        card.style.zIndex = -1
      });
      document.querySelectorAll('.card-back').forEach(function (card) {
        card.style.zIndex = 1
      });
    } else {
      document.querySelectorAll('.card-front').forEach(function (card) {
        card.style.zIndex = 1
      });
      document.querySelectorAll('.card-back').forEach(function (card) {
        card.style.zIndex = -1
      });
    }
    document.querySelectorAll('.card-front').forEach(function (card) {
      card.style.transform = `rotateY(${cardRotate}deg)`
    });
    document.querySelectorAll('.card-back').forEach(function (card) {
      card.style.transform = `rotateY(${cardRotate + 180}deg)`
    });
  }
});

front박스의 opacity, grid박스의 gap, card요소들의 rotate값들을 일차방정식으로 먼저 구해서 변수로 등록해놓는다.

그리고 조건문으로 특정 scroll값이 되었을 때 실행시켜준다.

rotate의 값이 90이 넘어가면 자연스럽게 앞 뒷면이 바뀌게 조건문을 설정해준다.

 

- 주의사항

* card-back을 먼저 rotateY 180도를 해준 이유는 180도로 회전되었을 때의 div의 모양은 좌우가 반전된 모양으로 있기 때문에 먼저 돌려주고 javascript안에서도 card-back의 transform값을 cardRotate + 180으로 해준다. 그러면 좌우반전 안되고 제대로 보이는 카드를 만들 수 있다.

 

* card-front와 card-back을 보면 forEach반복문을 쓴 것을 볼 수 있는데, forEach 반복문은 애초에 array 자료에서 쓰는건데 왜 썼는가?

document.querySelectorAll('.card-front')를 console창에 출력해보면 nodelist라고 뜬다. 처음에 forEach 반복문으로 안쓰고 그냥 style을 변경하려 했을 때 오류가 뜨면서 작동하지 않았다. 이 nodelist를 forEach반복문으로 다루면 오류가 해결된다.

728x90
반응형