본문 바로가기

송송 DEV

송송은 뚠뚠 오늘도 뚠뚠 열심히 ~ 코딩 하네 뚠뚠

css display none 애니메이션 오류 수정하기

오늘의 문제


display:none; display:block; 에 transition 애니메이션이 적용되지 않는다!

 

See the Pen display none animation1 by ylem76 (@ylem76) on CodePen.

 

메뉴를 마우스 오버했을 때 하위 메뉴가 보이고,  마우스 아웃하면 사라지는 흔한 메뉴.

간단할 것 같아서 css transition으로 구현하려고 하는데, 애니메이션이 뚝뚝 끊기고 안 예쁘다..!!!

어째서 이런 현상이 발생하는 건지 원인을 알아보고 해결 방법을 고민해보았다.

 


문제 분석


display none에서만 생기는 트랜지션 오류

화면에 보이지 않게 만드는 방법은 여러 가지가 있다.

 

display:none;
opacity:0;
visibility:hidden;
transform:scale(0);
z-index:-1;

 

기타 등등등... 그중에서 display none 처리했을 때에만 transition 애니메이션이 제대로 동작하지 않는다.

다른 것과 어떤 차이가 있길래?!


원인 분석


2. transition을 이용한 애니메이션의 특징

 

먼저 transition 애니메이션에 대해 살펴보자.

transition은 이전 프로퍼티의 상태와 이후 프로퍼티의 상태를 비교해서 그 차이를 부드럽게 이어준다. 일반적으로 숫자 연산 가능한 속성에 사용한다.

 

opacity : 1~ 0.1로 변화

 

top : 0~ 100px로 변화

 

그러나 displayvisibility는 숫자로 연산이 안된다. 따라서 이 둘은 transition 효과도 불가능.

 

아니 근데 내가 바보도 아니고 display:noneopacity를 같이 설정했다. 그러니 display에 설정한 transition효과는 안 먹더라도 opacity는 제대로 동작해야 하는 거 아닌가?

 

이걸 이해하려면 브라우저 렌더링을 이해해야 했다... 고난의 길 스타뜨...

 


3. 주요 렌더링 경로 Critical Rendering Path

 

인터넷 브라우저가 화면 상에 코드를 그릴 때 한 번에 짠! 하는 것처럼 보이지만 내부적으로는 여러 단계를 거친다. 이 일련의 단계들을 중요 렌더링 경로(Critical Rendering Path)라고 부른다.

관련 내용 중심으로 간략하게 아래의 4단계로 나누어 볼 수 있다.

이미지 출처 : https://dev.to/codesensei/critical-rendering-path-39m

 

  1. DOM, CSSOM 트리 생성 : 파싱 한 HTML과 CSS를 이용해 document object model과 css object model을 각각 생성한다.
  2. 렌더링 트리 생성 : 앞 단계에서 생성한 트리 정보를 바탕으로, '화면에 표시'할 요소들의 정보를 정리해 렌더링 트리를 만든다
  3. 레이아웃(혹은 리플로우) : 요소가 실제 화면(뷰포트) 상에 너비, 높이, 위치가 어떻게 될 것인지 계산(만)한다. (아직 화면에는 보이지 않음)
  4. 페인트 : 실제로 화면에 픽셀을 그림 쨔잔

display:none 요소는 화면 상에 아예 보이지 않기 때문에 렌더링 트리에서 누락되게 된다. 따라서 렌더링 트리 X, 레이아웃 계산 X, 페인트 X

display block이 되었을 때가 되어야 비로소 렌더링 트리에 들어가게 되고 그제야 레이아웃과 페인트가 일어난다. transition 애니메이션을 이용하려면 none 상태의 opacity와 block 상태의 opacity값의 차이를 비교할 수 있어야 하는데, none상태의 opacity는 애초에 존재한 적이 없으니(렌더링 트리 X), 상태를 비교할 값이 없어 transition 애니메이션을 사용할 수 없는 것.

 


해결을 위한 방법들(순수 CSS)


1. transition이 아니라, keyframe animation 활용

 

transition이 비교할 초기값이 없어 애니메이션이 불가능 한 반면에 키프레임 애니메이션은 초기값을 유저가 직접 할당하고 실행시키기 때문에 동작이 가능하다.

그러나 이는 완벽하지는 않다.

display:none → block으로 가는 애니메이션은 초기값 설정으로 애니메이션 구현이 가능하지만

display:block → none으로 가는 애니메이션은 불가능하다.

none이 되었을 때 렌더 트리에서 바로 삭제해버리니까 애니메이션이 재생할 틈이 없는 것.

사라질 때 애니메이션이 필요 없다면 추천~!

 


2. display none을 안 쓰고 구현하자

 

none 속성이 아니라 다른 방법으로 화면 상에서 보이지 않게 처리한다면 transition을 활용한 애니메이션도 활용 가능하다. 다양한 여러 방법들이 가능하겠지만, 두루두루 적용할 수 있는 방법 두 가지.

 

2_1. display 대신에 visibility 이용

display:none과 달리 visibility:hidden은 화면에 보이지 않아도 공간을 차지하고 있다고 한다. 위의 렌더링 과정을 이용해 다시 말하자면 visibility는 3단계 레이아웃 계산을 끝마쳤다는 것과 같은 의미이다. 따라서 visibility:hidden 상태와 visibility:visible 상태를 서로 비교가 가능하고, transition 애니메이션을 사용할 수 있다!

  • visibility:hidden 처리된 요소는 display:none처럼 접근성 트리에도 삭제된다.
    (애니메이션을 이용할 생각이라면 어쩌면 display:none보다 더 나을 수도 있겠다.)
  • transition-property를 all로 해야만 적용된다. 주의!

2_2. pointer-event 활용

근데 애초에 display none을 왜 굳이 썼을까? 돔 트리 렌더 트리 어려운 얘기는 차치하고 실제로 구현하고자 했던 것은 이거 아니었을까?

화면 상에 안 보이고, 높이값도 안 차지하고 그 자리에 클릭 이벤트도 안 걸렸으면 좋겠다!!

 

화려한 애니메이션이 들어갈 것도 아니고 화면 상에서 보이지 않게만 처리할 거라면 그냥 opacity:0 지정하고 마우스 클릭 이벤트를 없애버려도 된다.

이때 사용할 수 있는 CSS 속성이 있는데, 바로 pointer-events이다.

해당 요소에 pointer-events 속성을 none으로 지정하면, 화면 상에서 클릭했을 때 해당 영역은 잡히지 않고 크롬 개발자 도구에서도 클릭으로 잡을 수 없다.

다시 화면상에 나타날 때에 pointer-events:auto;로 지정하면 된다.

 

 


 

위의 방법들을 실제 코드로 확인해보세요!

 

See the Pen display none animation by ylem76 (@ylem76) on CodePen.

 

 

렌더 과정..? 이런 거 굳이.. 알아야 할까..?라고 생각했지만

결국 알아야 하는 순간이 오는 것 같네요.

원리를 숙지해야 문제의 원인이 정확히 파악 가능하네요.

같은 문제로 헤매셨던 분이 있다면 도움이 되었으면 좋겠습니다!!