Web Animation

raccoonback·2020년 6월 10일
1

boost course

목록 보기
3/10
post-thumbnail

애니메이션은 반복적인 움직임 처리를 의미하며, CSS3의 transition과 transform 속성 및 javascript 이용해서 구현이 가능하다.

일반적으로 CSS 이용하는 것이 javascript 이용한 동작보다 빠르다.

FPS(Frame Per Second)

  • 1초 동안 화면에 표현할 수 있는 프레임 수
  • 일반적으로 60fps, 즉 16.666 ms 미만 간격마다 frame 생성되면 매끄럽게 느껴진다.
  • 따라서 Javascript, CSS3 두 가지 방법으로 사용 가능한데 강의에서는 아래와 같이 정의하였다.
    • 간단하고 규칙적인 애니메이션 ⇒ CSS
    • 세밀한 조작이 필요한 애니메이션 ⇒ Javascript

javascript 이용한 구현

javascript 로는 setTimeout, setInterval 이용해 구현이 가능하다. 하지만, 이벤트 콜백의 지연으로 인해 정해진 시간에 함수가 실행된다는 것을 보장할 수 없다.

이러한 지연을 브라우저에서 방지하고 가장 적절한 타이밍에 비동기 함수를 실행하는requestAnimationframe() 함수를 권장한다.

이벤트 콜백이란 무엇일까?

브라우저는 setTimeout, AJAX, DOM Event 과 같은 비동기 호출을 처리하기 위해 Web API 영역을 정의한다. 일반적인 함수 호출은 스택을 통해 처리가 되지만, 비동기 호출이 필요한 함수는 Web API 영역에서 비동기 기능을 수행하고 Task Queue 에 Callback 함수를 등록한다. 이후, Event Loop 는 스택이 모두 비워진 시점에 Tack Queue 에서 해당 Callback 함수를 스택에 하나씩 쌓아 처리한다.

예제를 통해 살펴보자.

function a() {
    console.log(111)
}

function b() {
    console.log(222)
}

function c() {
    console.log(333)
}

a();
setTimeout(b, 0);
c();

출력 결과는 어떻게 될까? 정답은 111 -> 222 -> 333 순이다.

스택에 쌓이는 함수 호출 구조를 생각해보면 이해가 된다.

처음 a() 함수가 스택에 쌓이고 실행된다. 이후 setTimeout() 함수는 Timer 이벤트를 요청한 후 바로 스택에서 제거되고, c() 함수가 스택에 쌓여 호출되는 것이다. setTimeout() 함수가 실행된 후에는 Callback 함수가 Task Queue 에 등록되고, 스택에 비워지면 Event Loop 가 Callback 을 하나씩 스택에 올려 실행한다.

MDN 문서를 살펴보면 Event Loop 구조를 아래 코드와 같이 설명하였다.

while(queue.waitForMessage()){
  queue.processNextMessage();
}

또한 Promise 는 조금 다르게 동작을 하는데, Promise then() 함수에 전달하는 콜백 함수는 'Task Queue' 가 아닌 'Micro Task Queue' 에 등록된다. 'Micro Task Queue' 가 'Task Queue' 보다 우선순위가 높기 때문에, Event Loop 는 Promise 통해 등록된 콜백을 먼저 처리한다. 따라서 아래의 예제를 살펴보자.

function a() {
    console.log(111)
}

function b() {
    console.log(222)
}

function c() {
    console.log(333)
}

setTimeout(a, 0);
Promise
    .resolve()
    .then(b)
    .then(c);

앞서 말했듯이, a() 함수가 먼저 'Task Queue' 등록됐을지라도, 'Micro Task Queue' 우선순위가 높아 Promise then() 함수로 전달한 콜백이 먼저 순차적으로 실행된다.

결과적으로, 222 -> 333 -> 111 대로 출력된다.

따라서 위에서 말한 이벤트 콜백이 지연되는 이유는 Task Queue 에서 대기하는 시간으로 인해 발생하고, setTimeout() , setInterval() 함수를 이용해 정확한 시간 간격을 제공하지 못한다.

setTimeout, setInterval 사용

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        .setTimeoutBox {
            background-color: aquamarine;
            display: inline;
            position: relative;
        }
        .setIntervalBox {
            background-color: brown;
            display: inline;
            position: relative;
            top: 30px;
        }
    </style>
</head>
<body>
    <div class="setTimeoutBox">box</div>
    <div class="setIntervalBox">box</div>
</body>

<script>
    function animateBySetTimeout(time) {
        if(time < 10) {
            const box = document.querySelector('.setTimeoutBox');

            box.style.left = parseInt(box.style.left || 0) + 30 + 'px';
            setTimeout(animateBySetTimeout.bind(null, time + 1), 1000 / 60);
        }
    }

    let count = 0;
    function animateBySetInterval() {
            const box = document.querySelector('.setIntervalBox');
            box.style.left = parseInt(box.style.left || 0) + 30 + 'px';
            count += 1;

    }

    animateBySetTimeout(0);

    const interval = setInterval(animateBySetInterval, 1000 / 60);
    setTimeout(() => {
            clearInterval(interval);
    }, 1000);

</script>
</html>

requestAnimationFrame 사용

setTiemout(), setInterval() 사용하는 경우, 다음과 같은 이슈가 발생한다.

  • animation 주기를 16.6 미만하여 불필요한 프레임이 생성
  • 주기가 너무 길어져 매끄럽지 못하는 경우

이러한, requestAnimationFrame() 는 지연을 방지하고 가장 최적화된 타이밍에 콜백 함수를 실행하는 비동기 함수이다.

구체적으로, 브라우저가 리페인팅(HTML 문서의 전체 또는 일부 영역의 스타일이 변경되었을 때 브라우저가 변경된 스타일을 다시 적용하는 작업)이 진행되기 이전에 애니메이션을 업데이트하는 콜백 함수를 실행합니다.

기본적으로는 1초에 60번, 즉 60fps 콜백 함수를 호출하지만 모니터 주사율에 맞춰서 실행 주기가 결정됩니다. 모니터 주사율이 50fps 라면 1초에 50번 콜백 함수를 호출하는 것입니다.

setTimeout() 와 다르게, 콜백 함수는 실행 시점을 나타내는 Timestamp 값을 파라미터로 전달받아 애니메이션을 구현할 수 있으므로, 보다 제대로 된 애니메이션을 구현할 수 있습니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        .box {
            background-color: aquamarine;
            display: inline;
            position: relative;
            left: 30px;
        }
    </style>
</head>
<body>
    <div class="box">box</div>
</body>

<script>
    let start = null;
    function animate(timestamp) {
        if(!start) start = timestamp;
        const progress = timestamp - start;
        const box = document.querySelector('.box');
        box.style.left = Math.min(progress / 10, 200) + 'px';
        if(progress < 2000) {
            window.requestAnimationFrame(animate);
        }
    }
    window.requestAnimationFrame(animate);
</script>
</html>

CSS3 사용

css 이용한 애니메이션이 가장 빠르고 사용하기 간편하다는 점에서 권장된다.

css 은 transition , transform 을 지원하여 애니메이션을 처리할 수 있다.

  • transition : element를 부드럽고 점진적으로 변화시킨다.
  • transform : element 모양을 이동하거나 변경한다.

Transition

transitiontransition-property, transition-duration 두 가지 속성을 개별적으로 지정할 수 있지만, 편의를 위해 아래와 같이 한 번에 정의한다.

div {
  transition: [property] [duration] [timing-function] [delay];
}
  • property : transition 을 적용하기 위한 style 프로퍼티(ex, background-color or all)
  • duration : transition 의 지속 시간 (1s)
  • timing-function : transition 속도를 정의 (linear, ease, ease-in, ease-out, ease-in-out 값 사용)
  • delay : 시작되기까지의 지연시간 (1s)

Transform

transform 은 GPU 가속을 이용하는 rotate, translate, skew, scale 속성을 사용해서 element에 애니메이션을 적용할 수 있다.

div {
  transition: transform 1s;
}

div:hover {
  transform: translate(20px, 20px);
}

rotate, translate, skew, scale 함수는 메인 스레드를 방해하지 않고 GPU에게 애니메이션 처리를 위임하여 성능을 향상시킬 수 있다.

그럼 특정 요소에 hover 시, translate 이용해 좌상단에서 우하단으로 이동하는 예제를 보자.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        .box {
            background-color: aquamarine;
            display: inline;
            position: absolute;
            left: 30px;
            top: 30px;
            transition: transform 1s;
        }
        .box:hover {
            transform: translate(50px, 50px);
        }
    </style>
</head>
<body>
    <div class="box">box</div>
</body>
</html>

box 요소 위에 마우스가 올려졌을때 우하단으로 이동하는 것을 확인할 수 있다.

벤더 프리픽스

CSS3 표준으로 등록되기 이전까지는 각 브라우저 개발사에서 실험적으로 제공하는 기능은 '벤더 프리픽스'로 구분해 적용한다. CSS3에 표준이 되지 않은 속성은 브라우저에 따라 동작하지 않을 수 있기 때문에, 벤더 프리픽스를 사용해 적용해야 한다.

  • -webkit- : Chrome, Safari
  • ms : Internet Explorer, Microsoft Edge
  • -o- : Opera
  • -moz- : Firefox
-webkit-box-orient: vertical;
-moz-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
flex-direction: column;
-ms-flex-direction: column;

참고 자료

profile
한번도 실수하지 않은 사람은, 한번도 새로운 것을 시도하지 않은 사람이다.

0개의 댓글