React 스크롤 특정 영역에서 애니메이션 효과 주기

물개핫도그·2022년 9월 1일
1

React

목록 보기
2/2

영화 소개를 주제로 리액트 페이지를 하나 만드는 중이다.
영화 시리즈가 총 4개니까 스크롤을 내려서 특정 영역에 도달할 때 마다 포스터와 소개글이 나타나도록 만들려고한다.

주의할 점은 반응형으로 제작해서 모바일 화면일 땐 사라지는 콘텐츠가 있다는 것.

이거때문에 코드 몇 번이나 수정했는지..ㅡㅡ...

OffsetTop 이용하기

렌더링 되자마자 애니메이션 효과 주고 싶을 때

offsetTop은 document로부터 얼마나 떨어져있는지를 말해준다.
$('').offset().top 을 vanilla js 로 바꾼 것.


  let [change,setChange]=useState('');
  let [srr,setSrr]=useState('');
  
  useEffect( ()=>{
    handlescroll();
  } );
  
  const handlescroll = ()=>{
    let wrap=document.getElementById('wrap');
    if (wrap.offsetTop >= 50) {
      setChange('left_ani');
      setTimeout(()=>{setSrr('btn_ani')},1000);
    } 
  }
  
  return 내부 코드는 
  
          <div className='col-md-6' id='wrap'>
          <div className="d-none d-md-block">
            <div className={'fire '+change}>
              <img src='img/panem.jpg' width="100%" height="100%"/>
              <div className="d-none d-md-block" id="btnbox">
                <button className={'btn1 '+srr}>more view</button>
              </div>
            </div>
          </div>
        </div>

useEffect를 이용해 애니메이션 효과를 주는 함수가 실행되도록 했다.

wrap이 document으로부터 떨어진 길이가 50보다 크거나 같으면 애니메이션 효과가 실행된다.

처음 페이지가 딱 렌딩됐을 때 wrap은 당연히 document 상단으로부터 50보다 더 떨어져있다.
일부러 터무니없이 작은 숫자로 그냥 설정했다.
반대로 터무니없이 큰 숫자 (5000같은)를 넣어보면 죽을때까지 wrap은 나타나지 않는다.ㅋ

문제점

대부분 웹페이지의 경우 document 길이와 뷰포트 길이가 일치하지 않는다..
첫 페이지 랜딩이 되자마자 보이는 콘텐츠의 경우엔 위의 방법을 쓰면 되는데
아래쪽에 있어서 스크롤을 내려야지만 보이는 콘텐츠의 경우엔 위의 방법을 쓰면 문제가 된다.
왜냐? 사용자가 구경하면서 스크롤 내리기 전에 이미 지 혼자 애니메이션이 실행됨. 사용자가 스크롤 내렸을 땐 이미 모든 애니메이션이 끝난 상태가 된다.
그래서 필요한 것이 스크롤을 내려서 해당 영역에 도달했을 때
효과가 나타나는 것.

addEventListener 이용

이건 제이쿼리 버전, vanilla js 버전 두 가지가 있다.
나중에 기억 안나면 다시 찾아와서 복습하기 쉽게 두 개 다 올려놓음

먼저 순수 js 버전

useEffect( ()=>{
    window.addEventListener('scroll',handlescroll);
    return( ()=>{
      window.removeEventListener('scroll',handlescroll);
    } )
  } );

  var isVisible = false;
  
  const handlescroll = ()=>{
    let demo = document.getElementById('wrap_right3');

    if(checkVisible(demo)&&!isVisible){
      실행 할 코드 집어넣기
      isVisible=true;
    }
  }
  
  //이 아래 부분은 해석만 할 줄 알면 됨. 복붙하세요..

  function checkVisible(elm, eva) {
    eva = eva || "object visible";
    var viewportHeight = window.innerHeight, // Viewport Height
    scrolltop = document.documentElement.scrollTop, // Scroll Top
    y = elm.offsetTop,
    elementHeight = elm.style.height; //elm의 높이
    
    if (eva == "object visible") return ((y < (viewportHeight + scrolltop)) && (y > (scrolltop - elementHeight)));
    if (eva == "above") return ((y < (viewportHeight + scrolltop)));

  }

checkVisible 함수 내부 변수 설정하는 데 순수 자바스크립트 이용.
넓높이 다시 복습해야겠다.
다 구글링해서 찾음..ㅡ.ㅡ;;

checkVisible 함수 변수 부분 jQuery버전

 function checkVisible(elm, eva) {
    eva = eva || "object visible";
    var viewportHeight = $(window).height(), // Viewport Height
    scrolltop = $(window).scrollTop(), // Scroll Top
    y = $(elm).offset().top,
    elementHeight = $(elm).height();
    
    if (eva == "object visible") return ((y < (viewportHeight + scrolltop)) && (y > (scrolltop - elementHeight)));
    if (eva == "above") return ((y < (viewportHeight + scrolltop)));

  }

사실 제이쿼리가 더 읽기는 쉽다.
하지만 이제 제이쿼리는 저무는 태양이기때문에..
웬만하면 쓰지 않으려 노력중이다.

그리고 리액트에서 제이쿼리 사용하려면
터미널에서 npm install jqeury
상단에 import $ from 'jquery';
갖다붙이면 끝

이렇게 하면 뷰포트 기준으로 애니메이션 효과를 줄 수 있다.

결과

코멘트

꼭 만들어보고 싶은 효과였다.
반응형으로 욕심내서 만드느라 이틀동안 너무 힘들었고..
그리고 만드는데 자꾸 가로스크롤이 생겨서 도대체 뭐가 문제인지..
스크롤 내려서 애니메이션 효과 다 끝나면 또 스크롤이 없어짐..
아무리 화면을 뒤져봐도 화면 밖으로 뭔가 삐져나온게 없는데?
기묘한 현상에 새벽 3시까지 잠 못이루었는데..
드디어 찾아냄ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

more rivew 버튼을 position absolute로 주고
포스터에 relative를 걸어버린게 원인이었다.

useEffect로 전/후 css를 따로 주는바람에..
className={'start ' + end}
이렇게 하고 end에다가 relative를 걸었다.
useEffect 내부의 함수가 실행되어야지 start가 end로 바뀌는걸 깜빡했다.
그러니까 효과가 실행되기 전에는 end의 css는 없는존재다.
없는 존재에다 relative 걸어버렸으니..
버튼은 자동으로 길을 잃고...화면 밖 어딘가로 사라져 스크롤을 남겨버림..

버튼이 있던 공간을 남기기 싫어서 일부러 absolute를 줬는데..
할 수 없이 relative로 위치 조정함..
더 좋은 방법이 생각나면 다시 고쳐봐야겠다.

그래도 원하던 코드 구현해봤으니 만족ㅋ

다음엔 좀 더 역동적인 효과(?)를 줄 수 있도록...연구해봐야겠다.

profile
비전공자의 개발 공부, 프론트엔드가 목표입니다. https://blog.naver.com/somv12 에서 이사 중입니다.^^ 포스트 이사 예정!

0개의 댓글