• [JS] 스크롤 페이드인 효과

    2019. 12. 5.

    by. 나나 (nykim)

    320x100

     

    이래나 저래나 원페이지가 많은 가운데, 여기에 쓰이는 스크롤 효과도 참 많습니다.

    그중에 가장 유명(?)하고 대중적인 건 스크롤 할 때 요소가 페이드인 되는 효과인 듯 합니다.

    그래서 이걸 직접 구현하려고 했..으나 이 효과를 이해하기 위해서는 우선 스크롤과 관련된 속성을 알아야 합니다.

    아하 그래서 어려운 거군요 \(T∇T )/

     

     


     

     

    후새드... 눈물 닦고 테스트용 페이지를 만들어 봅니다.

    아이템 4개로 이루어진 페이지인데, 각 아이템의 높이는 500px입니다.

     

    .item {
      width: 100%;
      height: 500px;
    }

     

    마진과 패딩이 하나도 없다고 할 때, 이 문서의 총 높이는 500*4 = 2000이 되겠군요.

     

    그리고 윈도우의 높이는 딱 500px이라고 합시다.

    그럼 첫번째 아이템만 보이는 상태겠네요.

     

     

    우선 4번째 아이템 하나만 콕 집어서 효과를 줘봅시다.

    Item4가 화면 상에 보이려는 바로 그때!!! 스윽 페이드인 효과를 주자는 거죠.

     

     

    그러면 한 이쯤 ( ・_・)?? 윈도우의 끝이 Item 4의 위쪽과 닿을 때쯤이 되겠네요.

     


     

     

    좋습니다. 우선 이 상태에서 알 수 있는 값을 정리해봅시다.

     

    /* 윈도우의 높이 */
    var winH = window.innerHeight; //500
    
    /* 스크롤 값 */
    var scrollY = window.scrollY; //0
    
    /* item 4의 top 값 */
    var posFromTop = item4.getBoundingClientRect().top; //1500
    
    /* item 4의 절대좌표 값 */
    var absolutePos = scrollY + posFromTop; //1500

     

    이렇게 총 4개네요! 하나하나 살펴봅시다.

     

     

    우선, winH입니다. window.innerHeight는 말그대로 윈도우 높이입니다. 500으로 해놨으니 500이겠죠.

     

    다음은 scrollY인데요, window.scrollY는 수직 스크롤을 얼마나 했는지 나타냅니다.

    스크롤바를 움직이지 않았을 경우 0, 스크롤바를 내릴 수록 값은 커집니다. 또, 문서 높이에 비해 윈도우 크기가 작을 수록 값은 더 크게 늘어나겠죠.

     

    그리고 posFromtop은 해당 요소가 뷰포트로부터 얼마나 떨어져 있는지를 나타냅니다.

    뷰포트가 뭔지 잘 모르겠다 싶을 땐, 그냥 윈도우에서 얼마나 멀리있는가라고 생각해도 될 것 같아요.

     

     

    위 그림을 보면 이해가 좀 더 쉽습니다.

    윈도우의 최상단과 item4의 최상단간의 거리를 확인해 보세요. item4 위로 아이템이 총 3개 있었죠. 따라서 500*3 = 1500만큼의 거리가 있는 셈입니다. 잊지 마세요, 최하단이 아니라 최상단입니다!

     

    그렇다면 스크롤을 내려가면 이 값은 어떻게 변할까요?

     

     

    바로 이렇게 바뀝니다. 윈도우 최상단과 Item 4의 최상단이 더 가까워졌죠?

    따라서 스크롤을 내릴수록 이 값은 줄어든다는 것을 알 수 있습니다.

    달리 말하면 이는 '상대좌표 값'이라고 부를 수 있겠네요.

     

    깜짝 퀴즈(?) 시간! 그렇다면 여기서 Item1.getBoundingClientRect().top의 값은 뭘까요? 답은 -500입니다 ;)

     

    덧붙여, 요소의 상대좌표를 알아내는 또다른 API가 있는데요 바로 offsetTop입니다.

    이는 부모로부터 얼마나 떨어져있는가를 나타내는데, 여기서 부모는 position: relative가 적용된 대상입니다.

    따라서 상위 요소에 relative가 없을 경우 조상을 거슬러 올라가거나, 끝내는 body를 기준으로 두고 계산을 합니다.

    그렇기에 어떤 CSS 속성을 갖고 있는지 모르는 상태라면 쓰기 애매한 속성이긴 합니다.

     

     

    마지막으로 절대좌표 값은 스크롤을 해도 변하지 않는 절대적 값입니다. 즉, 문서로부터 얼마나 떨어져있는가를 나타냅니다.

    item 4 위로는 500px의 아이템이 총 3개가 있죠. 그럼 얘의 절대적 Y값은 무조건 500*3=1500이겠네요.

    근데 이건 우리가 알고 있는 사실로부터 추론한 거라, 자바스크립트한테도 '자 다 더해봐^^'라고 할 수는 없잖아요?

     

    다행히도 이 값은 우리가 지금까지 구한 값으로 얻어낼 수 있습니다.

    scrollY(0) + posFromTop(1500) = absolutePos(1500)가 된다는 사실 눈치채셨나요? (전 못챘습니다)

     

    scrollY는 스크롤을 내릴 수록 커진다고 했습니다.

    posFromTop은 스크롤을 내릴 수록 작아지고요.

    그리고 이 두 값은 절대 좌표 내에서 서로 주거니 받거니 하면서 값이 변합니다.

     

     

    바로 이렇게 말이죠!

    그래서 scrollY + posFromTop이 절대좌표가 된 것입니다.

     

     


     

    후 길어따 이제 본론으로 돌아갑시다.

    스크롤을 내리지 않은 상태일 때, 우리가 구한 값들은 아래와 같았습니다.

     

     

    이제 스크롤을 500px만큼 내렸다고 가정해 보죠.

     

     

     

    winH = 500;
    absolutePos = 1500;
    
    scrollY = 500;
    posFromTop = 1000;

     

    윈도우 높이와 절대좌표는 변하지 않으니까 냅두고, scrollY랑 posFromTop을 보면 500만큼 늘고 줄어든 것을 볼 수 있습니다.

    여기서 스크롤을 더 내리면 scrollY는 더 커지고, posFromTop은 더 작아지겠죠?

    이제 한 500px 정도만 더 내리면 Item 4 머리카락이 보일락 말락 할 것 같네요.

     

     

     

    네!! 바로 여깁니다!!! (흥분) 바로 이즈음에 item4에다가 페이드인 효과를 주려고 합니다.

    이 시점에서 각 값들은 어떻게 됐죠?

     

    winH = 500;
    absolutePos = 1500;
    
    scrollY = 1000;
    posFromTop = 500;

     

    여기서 주목해야 할 값은 posFromTop 입니다.

    이 값이 윈도우 높이(500)보다 작아지기 시작할 때부터 화면에 나타난다는 뜻이거든요!

     

    확실한 이해를 위해 윈도우 높이를 줄여서 한 번 더 짚어봅시다. 

     

     

    아~까 보았던 이미지랑 달라진 게 거의 없어요!

    윈도우 크기만 줄었을 뿐입니다. posFromTop도 영향을 받지 않았고요.

    왜냐면 이건 윈도우 최상단 사이를 계산하기 때문에, 윈도우 크기를 줄여도 아무 영향이 없습니다. 

    그림 상의 빨간 부분이 아까 윈도우 높이가 500px일 때와 동일하다는 걸 볼 수 있습니다.

     

     

    자, 그럼 이 상태에서 스크롤을 쭉쭉 내려봅니다.

    아까 posFromTop = 500일 때가 우리가 원하는 지점이었죠?

     

     

     

    ...뭔가 이상하네요.

    아까랑 다르게 Item 4 머리가 안 보입니다.

    스크롤을 더 내려야겠어요.

     

     

     

    이제야 Item 4가 보이기 시작하네요!

    이 시점의 값들은 아래와 같습니다.

     

    winH = 250;
    absolutePos = 1500;
    
    scrollY = 1250;
    posFromTop = 250;

     

    아까 살펴본 것처럼, posFromTop이 winH보다 작아지기 시작하는 시점에서 컨텐츠가 보이네요.

     

     

    그럼 우리가 페이드인 효과를 줄 시점이 명확해졌습니다.

     

    바로, winH > posFromTop 이죠.

    달리 표현하면 posFromTop-winH < 0 이라고도 할 수 있겠네요.

     

    posFromTop이 스크롤에 의해 유동적으로 변하기 때문에 scrollY 값은 굳이 구하지 않아도 됩니다.

     

    이제 이걸 코드로 표현하면 이렇습니다.

     

    var animation = function () {
      var items, winH;
    
      var initModule = function () {
        items = document.querySelectorAll(".text");
        winH = window.innerHeight;
        _addEventHandlers();
      }
    
      var _addEventHandlers = function () {
        window.addEventListener("scroll", _checkPosition);
        window.addEventListener("load", _checkPosition);
        window.addEventListener("resize", initModule);
      };
    
      var _checkPosition = function () {
        for (var i = 0; i < items.length; i++) {
          var posFromTop = items[i].getBoundingClientRect().top;
          if (winH > posFromTop) {
            items[i].classList.add("active");
          }
        }
      }
    
      return {
        init: initModule
      }
    }
    
    animation().init();

     

    출처: Codepen

     

    See the Pen Animate on Scroll with Vanilla JS by Edd Yerburgh (@eddyerburgh) on CodePen.

     

     

     

    이와 같이 winH, scrollY, posFromTop, absolutePos 간의 관계를 알아두면 요로케조로케 써먹을 수 있겠네요!

    다음에 여유가 되면 스크롤 이벤트를 최적화하는 작업도 진행해볼까 합니다.

     

     

    그럼 오늘도

    죠아아앗쓰 (*•̀ᴗ•́*)و ̑̑

     

     

     

     

    728x90

    댓글