CSS animation/ JS animation

devAnderson·2022년 1월 28일
0

TIL

목록 보기
45/105

브라우저의 랜더링 과정

브라우저가 HTML을 파싱하면 DOM 트리가 만들어진다.
이때, 만들어지는 DOM 객체는 스타일링이 적용이 되지 않은 모든 태그들이 반영된 객체이다.
(즉, css적으로 display:none와 같은 것이 적용되지 않은 상태다)
이 후에 브라우저는 이 DOM과, CSS를 파싱하여 만들어지는 CSSOM을 결합하여 랜더링을 하기 위한 요소들만 모아놓은 "랜더 트리" 를 만든다.

그리고, 그 내용 가운데에서도 특별히 별도 레이어로 구별되야 하는 레이어들을 따로 모아서 "렌더 레이어 트리" 를 만든다.
스크린샷 2022-01-28 오전 10 43 14

레이어란, 포토샵에서의 레이어 개념을 생각하면 좋을 것 같다.
즉, 층별로 나뉘어진 논리적인 의미들의 집합이다. 이런 논리적인 집합들이 나누어지게 되는 경우의 수는 다음과 같다.

  • 3D(translate3d, preserve-3d)나 perspective transform을 사용하는 경우
  • VIDEO, CANVAS 태그를 사용하는 경우
  • Flash나 ActiveX를 사용하는 경우
  • CSS Animation, CSS filter를 사용하는 경우
  • 자식엘리멘트가 Layer로 구성되어 있는 경우
  • z-index가 낮은 형제 엘리멘트이거나 layer로 구성되어있을경우 해당 엘리먼트도 layer로 구성됨

위의 내용 중에 자주 보게 될 만한 케이스가 볼드체의 내용일 것이다.
특히, 특정 형제들 가운데 하나만이 더 위에 올라가게 만들도록 하는 z-index 옵션은 이 레이어를 생성하게 만드는 대표적인 이유중 하나이다.

예를들어, 아래와 같은 HTML 문서가 있다고 가정할 때,

<html>
  <body>
    <div>DOM1 - RendersLayer</div>
    <div>DOM2 - RendersLayer</div>
    <div>DOM3 - RendersLayer</div>
    <div
      style="-webkit-transform: rotateY(30deg) rotateX(-30deg); width: 400px;"
    >
      DOM4 - RendersLayer - GraphicLayer (transform : rotate)
    </div>
    <div style="display:none">DOM5</div>
    <div>DOM6 - RendersLayer</div>
  </body>
</html>
스크린샷 2022-01-28 오전 11 41 20 HTML의 파싱 과정은 위와 같다.
  1. Parse HTML : html 문서를 분해하여 DOM 트리를 만든다.
  2. Recalculate Style : CSS를 분해하여 CSSOM을 만들고, 해당 내용을 DOM 트리와 결합하여 랜더트리를 만든다. (렌더 레이어트리 포함)
  3. Layout : 그 과정에서, 만약 Layout이 변경되는 사항이 존재한다면 레이아웃을 위한 계산을 실행한다. (javascript로 인한 변경도 포함)
  4. Paint : 브라우저에 해당 내용을 반영한다. (렌더레이어, 렌더 레이어레이어 별도로)
  5. Composite Layers : 렌더레이어와 렌더 레이어레이어의 결과물이 합쳐진다.

이처럼 레이어가 나누어지는 이유는, 별도의 레이어에서 해당 부분만이 별도로 관리되도록 만들기 위함이다(포토샵 레이어와 개념이 동일)

각 레이어의 변경점은 각 레이어만 가지고 있고, 나중에 해당 내용들을 병합(composite) 하여 최종 결과를 만들기 때문에 전체적인 paint와 layout 계산을 하지 않게된다.

브라우저 입장에서는 Layout 계산과, Paint 과정의 비용이 몹시 비싸므로, 되도록이면 css 애니메이션으로 이런 값들을 사용해 변경하는 행위는 피하는 것이 좋다. (즉, 되도록이면 GPU를 활용한 composite layer로 처리를 하는편이 좋다)

예를들어,

 @keyframes move {
            50% {
                top: 200px;
                left: 130px;
            }
 }
  @keyframes move {
            50% {
                transform: translate(100px, 100px);
            }
   }

위의 두 애니메이션은 둘 다 동일하게 요소를 이동시킨다. 하지만, 한쪽은 layout계산을 계속하게 만드는 top과 left 옵션이 들어가있고,

다른 한쪽은 composite layer을 만들어서 별도관리되는 애니메이션 translateX, translateY가 들어가 있다.

즉, 전자의 경우는 계속해서 CPU를 사용해서 계산하는 애니메이션으로 부하가 가지만, 이런 내용을 GPU로 옮겨서 사용하는 옵션을 이용하면 더 빠르고 쾌적한 성능을 보장할 수 있다.

CSS animation vs JS animation

  1. CSS animation
    css animation은 transform:translate과, keyframes를 이용하여 애니메이션을 만들 수 있다.
    어떤 옵션을 쓰는지에 따라 CPU와 GPU를 번갈아가면서 활용할 수 있기 때문에 효율적이다.
    하지만, 특정 상태에 따라서 애니메이션이 바뀌거나 변동되야 할 때에는 한계점이 보인다. (이는 styled-component를 이용하면 손쉽게 해결이 가능하긴 하다)

  2. JS animation
    setInterval과 RequestAnimationFrame을 이용하여 애니메이션을 만드는 방식이다.
    기존 setInterval로 애니메이션을 직접 만들 때에는 이른바, n초 단위로 요소의 style에 접근하여 값을 바꾸는 방식으로 하였기 때문에 불필요한 콜스텍이 엄청나게 늘어나는 단점이 있었다. 게다가 매번 콜스택마다 애니메이션이 진행되므로 뚝뚝 끊기는 느낌도 더해지게 된다. (비동기적으로 실행되기 때문에)

따라서 이를 위해 1초에 60프레임을 유지하는 형태로 최적화하여 애니메이션을 만들 수 있도록 한 것이 requestAnimationFrame이다.
형태는 마치 while과 같은 느낌처럼 실행된다. (솔직히 작성하면서 계속 내리는 결론이지만, 이것을 쓸 일이 있을까 싶긴 하다...)

// requestAnimationFrame

<div class="outside">movingBox</div>

    
<script>
        let count = 0;
        const el = document.querySelector(".outside") ;
        el.style.left = "0px"
         
        function run(){
            if(count>40){ //count가 40을 넘으면 return
                return
            };

            count += 2;
            el.style.left = parseInt(el.style.left) + count + "px";
            window.requestAnimationFrame(run); 
        }
    </script>
profile
자라나라 프론트엔드 개발새싹!

0개의 댓글