스크롤 애니메이션 구현

민경찬·2022년 7월 31일
4

스크롤을 내리고 올릴 때 div가 커지는 애니메이션을 만들어보고 싶었다. 스크롤을 잘 안다루는 내 특성상 나중에 까먹을 것 같아 미래의 나를 위해 이렇게 글로 남긴다.

순서

  1. 애니메이션이 시작하길 원하는 y위치를 정한다.
  2. 애니메이션이 끝나길 원하는 y위치를 정한다.
  3. 내 스크롤이 시작과 끝 y위치사이에서 몇%에 위치하는지 구한다.
  4. 애니메이션의 속도를 정한다.
  5. style을 적용한다.

예시 코드

html

    <div class="parent_div">
        <div class="child_div">
            
        </div>
    </div>

css

<style>
    .parent_div{
        margin-left: auto;
        margin-right: auto;
        margin-top: 1000px;
        width: 500px;
        height: 500px;
        background-color: grey;
        box-shadow: 4px 4px 4px 3px rgba(0, 0, 0, 0.4);
    }
    .child_div{
        margin: 0% auto;
        width: 300px;
        height: 300px;
        background-color: black;
    }
</style>

목표

  • 페이지를 아래로 스크롤 했을 때 부모div(회색 박스)를 만났을 때 검정색 크기가 점점 커지도록 만들어보자.

1. 애니메이션 시작 위치

애니메이션 시작 위치는 viewport 가장 하단 부가 부모div를 만나는 시점이다.

  • 그림을 보며 이해해보자

우리가 스크롤을 내리면서 화면이 아래로 점점 내려갈 때 뷰포트부터 parent div까지의 높이가 점점 작아진다. 그 높이가 뷰포트 높이가 되었을 때 애니메이션을 시작한다고 생각하자.

<script>
    const parentDiv = document.querySelector('.parent_div');
    const viewportHeight = window.innerHeight;
    const fromViewportToParentHeight = parentDiv.getBoundingClientRect().top;
    const 스크롤이넘어간정도 = viewportHeight - fromViewpoerToParentHeight;
</script>

viewportHeight - fromViewportToParentHeight가 0이 되면 시작인 것이다. 이를 스크롤이넘어간정도변수에 저장해두자.

2. 애니메이션 끝 위치

끝 위치는 자기가 정하는 나름대로이다. 일단 parent div가 내 화면에 다 보일때로 가정하겠다.

  • 이미지를 보며 이해하자.

스크롤이내려간정도divHeight와 같을 때 애니메이션이 끝난다

<script>
    const divHeight = parentDiv.clientHeight;
</script>
  • 이제 필요한 재료는 모두 구했다. 스크롤이 내려간 정도를 %로 구해보자.

3. 스크롤 위치를 %로 구하기

애니메이션이 시작하고 싶은 위치까지 스크롤을 내리면 0%, 끝나는 지점에서는 100%를 출력하도록 코드를 짜보자

<script>
    const scrollRate = 스크롤이넘어간정도/divHeight * 100;
</script>

재료준비는 끝났다.
이제 이 변수들을 활용해서 parent div에 대한 스크롤 비율을 0~100사이의 퍼센트로 나타내보자

<script>
    window.onscroll = ()=>{
        const parentDiv = document.querySelector('.parent_div');
        const viewportHeight = window.innerHeight;
        const fromViewportToParentHeight = parentDiv.getBoundingClientRect().top;
        const 스크롤이넘어간정도 = viewportHeight - fromViewportToParentHeight;
        const divHeight = parentDiv.clientHeight;
        let scrollRate = 스크롤이넘어간정도/divHeight * 100;
        if(스크롤이넘어간정도/divHeight * 100 <0){
            scrollRate = 0;
        }else if(스크롤이넘어간정도/divHeight * 100 > 100){
            scrollRate = 100;
        }
        console.log(scrollRate);
    }
</script>


4. 애니메이션 속도 정하기

애니메이션 속도는 0~100이 선형으로 움직인다는 점을 이용해서 개발자 입맛대로 바꿔주면 된다. 2차함수나 3차함수... 등등
스타일 속성에 따라 음수가 되어야 할 수도 있으니 scrollRate를 입맛에 맞게 변형하자

  • 나는 일정한 속도로 커지게 하기 위해 scrollRate를 그대로 사용할 것 이다.

5. 스타일 적용하기

style 속성에 맞춰 값을 변형시켜줘야 한다.
scale은 0~1사이의 값이니 100으로 나눠주어 적용한다.

<script>
    window.onscroll = ()=>{
        const parentDiv = document.querySelector('.parent_div');
        const viewportHeight = window.innerHeight;
        const fromViewportToParentHeight = parentDiv.getBoundingClientRect().top;
        const 스크롤이넘어간정도 = viewportHeight - fromViewportToParentHeight;
        const divHeight = parentDiv.clientHeight;
        let scrollRate = 스크롤이넘어간정도/divHeight * 100;
        if(스크롤이넘어간정도/divHeight * 100 <0){
            scrollRate = 0;
        }else if(스크롤이넘어간정도/divHeight * 100 > 100){
            scrollRate = 100;
        }

        //스타일 적용하는 부분
        const childDiv = document.querySelector('.child_div');
        childDiv.style.transform = `scale(${scrollRate/100})`;
    }
</script>
  • 자 그럼 이제 결과를 보자

6. 팁

팁은 크게 두가지로 나눈다.
1. 뷰포트의 height가 element의 height보다 작을경우의 팁
2. transform 애니메이션에 대한 팁

6-1 뷰포트의 height가 element의 height보다 작을경우

이 경우 애니메이션이 끝나지 않아 불편을 겪을 수도 있다. 이럴 때는 애니메이션 끝나는 지점을 바꿔주면 된다.
이미지로 이해해보자

이렇게 이미 parentDiv는 내 화면에 가득 찼다. 하지만 아직 애니메이션은 끝나지 않았다... 이런 경우에는 divHeight의 값을 수정해서 애니메이션이 끝나는 위치를 적절히 바꿔주면 된다.

<script>
    window.onscroll = ()=>{
        const parentDiv = document.querySelector('.parent_div');
        const viewportHeight = window.innerHeight;
        const fromViewportToParentHeight = parentDiv.getBoundingClientRect().top;
        const 스크롤이넘어간정도 = viewportHeight - fromViewportToParentHeight;
        //parentDiv의 height가 뷰포트 높이보다 크면 뷰포트로, 아닐경우 parentDiv로
        let divHeight = parentDiv.clientHeight>viewportHeight ? viewportHeight : parentDiv.clientHeight;     
        let scrollRate = 스크롤이넘어간정도/(divHeight) * 100;
        if(스크롤이넘어간정도/divHeight * 100 <0){
            scrollRate = 0;
        }else if(스크롤이넘어간정도/divHeight * 100 > 100){
            scrollRate = 100;
        }
        //스타일 적용하는 부분
        const childDiv = document.querySelector('.child_div');
        childDiv.style.transform = `scale(${scrollRate/100})`;
    }
</script>

6-2 childDiv의 커지는 위치 수정

꽤 간단하게 해결할 수 있다. transform-origin을 통해 기준 위치를 정해주면 된다.

<style>
    .child_div{
        transform-origin: 50% 0%;
    }
</style>


이렇게 부모 div 상단에 딱 붙어서 scale이 커진다.


결론

애니메이션 시작과 끝을 정해 퍼센트로 나타낼 수만 있으면 나머지는 개발자 마음대로 조리할 수 있다.
추천 : sticky를 사용하면 더 맛깔난 웹사이트를 만들어볼 수도 있다.

0개의 댓글