영화 소개를 주제로 리액트 페이지를 하나 만드는 중이다.
영화 시리즈가 총 4개니까 스크롤을 내려서 특정 영역에 도달할 때 마다 포스터와 소개글이 나타나도록 만들려고한다.
주의할 점은 반응형으로 제작해서 모바일 화면일 땐 사라지는 콘텐츠가 있다는 것.
이거때문에 코드 몇 번이나 수정했는지..ㅡㅡ...
렌더링 되자마자 애니메이션 효과 주고 싶을 때
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 길이와 뷰포트 길이가 일치하지 않는다..
첫 페이지 랜딩이 되자마자 보이는 콘텐츠의 경우엔 위의 방법을 쓰면 되는데
아래쪽에 있어서 스크롤을 내려야지만 보이는 콘텐츠의 경우엔 위의 방법을 쓰면 문제가 된다.
왜냐? 사용자가 구경하면서 스크롤 내리기 전에 이미 지 혼자 애니메이션이 실행됨. 사용자가 스크롤 내렸을 땐 이미 모든 애니메이션이 끝난 상태가 된다.
그래서 필요한 것이 스크롤을 내려서 해당 영역에 도달했을 때
효과가 나타나는 것.
이건 제이쿼리 버전, 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로 위치 조정함..
더 좋은 방법이 생각나면 다시 고쳐봐야겠다.
그래도 원하던 코드 구현해봤으니 만족ㅋ
다음엔 좀 더 역동적인 효과(?)를 줄 수 있도록...연구해봐야겠다.