오늘의 문제
내가 한땀한땀 예쁘게 만든 애니메이션... 코드는 문제 없는데, 실제 화면에서는 왜 덜덜덜거릴까?
See the Pen jank-animation-before by ylem76 (@ylem76) on CodePen.
후후... 임베드한 코드펜에서는 왜 멀쩡해보일까? ^^...
이런 걸 뭐라고 하나요?
css를 이용해 애니메이션을 구현하다보면 예상치 못하게 덜덜덜 거리는 걸 볼 수 있다. 이런 문제를 두고 쟁크(jank)라고 하고, 브라우저 렌더링 단계 중 리플로우(레이아웃)와 관계가 있기 때문에 리플로우라고 검색해도 관련 내용을 찾을 수 있다.
원인
화면 주사율
스크린 기기는 1초에 화면을 60번씩 그린다. (화면주사율이 60Hz이라는 뜻은 이걸 말한다.)
스크린 기기는 1초에 60번을 그리는데, 브라우저에서 이를 따라잡지 못하면 애니메이션이 자연스럽지 못하고 버벅거린다.
그렇다면 왜 1초에 60프레임이 안나오는 걸까?
이는 렌더링 과정과 연관이 있다. 이전 포스팅에서 말했듯, 브라우저가 화면을 그리는 것은 여러 단계로 나뉜다. 화면에 위치한 요소가 변경되면 요소의 사이즈와 간격 크기 등등을 다시 계산하여 화면에 그리는 레이아웃(리플로우)와 리페인트 과정이 일어난다.
애니메이션 과정 없이 변경한다면 1번만 다시 그리면 되지만, 만약 transition이나 animation을 이용해 부드러운 움직임을 주었다면, 브라우저는 1초에 60번씩 계산하고 다시 그리는 과정을 반복해야 한다.
많은 요소를 한꺼번에 초당 60번 이상 계산하여 화면에 그리게 되면 브라우저에 많은 부담이 가중되어 제대로 움직이지 못하고 버벅거리는 jank 현상이 일어나는 것이다.
해결 : 브라우저 렌더링 최적화 하기
불필요한 reflow가 생기지 않도록 최적화 방법은 여러가지가 있다. 여기서는 모든 것을 소개하기 보다, 실제로 적용한 것들을 위주로 설명해보려 한다. 다양한 최적화 방법에 대해서는 하단 링크 참조
See the Pen menu-animation by ylem76 (@ylem76) on CodePen.
position:absolute로 수정하기
자연스럽게 쌓이게 만드는 레이아웃 구조는 만들기 쉽지만, 애니메이션을 넣으려고 할 때 jank문제가 발생하기 쉽다.
위치값을 absolute를 이용했을 때 어려웠던 점은, 개별 위치값을 하나하나 지정해주어야 했다는 것.
hover 효과 뿐만 아니라, 해당 메뉴임을 표시하는 on 클래스 스타일이 있었는데, on 클래스와 hover가 같이 있을 때 어떻게 해야하나 고민했었다.
해결 방법은 메뉴에 마우스를 올리지 않았을 때에만 메뉴 너비값이 커지도록 적용하는 것
css not 선택자를 활용하여, 메뉴 상에 마우스를 올리지 않았을 때에만 메뉴 너비 값이 적용되게 만들었다.
형제 선택자와 not:hover선택자를 활용
left값 기준으로 정렬했음. 위치가 바뀌어야하는 것은 .on이나 hover만 가로 사이즈가 늘어나기 때문에 이후 요소만 늘어난만큼 뒤로 밀림. 형제 선택자를 이용해 이후에 오는 요소들의 위치를 뒤로 이동시켰다.
그런데, on 때문에 기본 left값이 밀리는데, 이 부분은 :not(:hover)를 이용 마우스가 메뉴 상에 올라가지 않았을 때만, on 클래스 요소 뒤로 밀림 적용. 따라서 메뉴ul에 마우스가 올라가면 on 클래스를 이용한 위치 이동 무효화하고 hover에 의한 요소 이동만 적용
/* 초기 위치값 */
ul li:nth-child(1) a {
left: 0;
}
ul li:nth-child(2) a {
left: 222px;
}
ul li:nth-child(3) a {
left: 444px;
}
ul li:nth-child(4) a {
right: 0;
}
/* hover 및 on 위치에 따른 위치 이동*/
ul:not(:hover) .on ~ li:nth-child(2) a,
ul:hover li:hover ~ li:nth-child(2) a {
left: 560px;
}
ul:not(:hover) .on ~ li:nth-child(3) a,
ul:hover li:hover ~ li:nth-child(3) a {
left: 782px;
}
/* hover 및 on 사이즈 조정 */
ul li:hover a,
ul:not(:hover) li.on a {
width: 560px;
}
실제 작업물에서는 이렇게 정리해서 해결했다. 그러나 포스팅을 위해 메뉴 부분만 따로 떼어 확인해보니 다른 해결 방법이 있었다. 하나의 거대한 삽질을 뒤로 하고 나머지 방법은 다음 시간에 소개!
참고자료
[다음 웹표준] https://daumui.tistory.com/12
[괴발개발]https://post.naver.com/viewer/postView.nhn?volumeNo=8431285&memberNo=34176766
[구글 디벨로퍼]https://developers.google.com/speed/docs/insights/browser-reflow?hl=ko
[Beomy]https://beomy.github.io/tech/browser/critical-rendering-path/