노는게 제일 좋습니다.

fade-in/out 적용된 modal 본문

fade-in/out 적용된 modal

노는게 제일 좋습니다. 2020. 8. 10. 00:55

요약

코드보러가기 링크

목차

 

배경


이전에 모달창을 다음과 같이 구현했었다.

메인 컨테이너가 숨겨져있을 때는 modal, hidden 두 개의 클래스를 가지고 있다가, 나타날 때는 hidden을 빼는 방식이었다.

See the Pen modal - legacy by wonjinyi (@wonjinYi) on CodePen.

 

 

위 코드에 대해 모달이 나타날 때 페이드인, 숨겨질 때 페이드아웃 효과를 주려고 하였으나, 간단한 해결이 어려웠다.

 

문제


기존 코드의 hidden 클래스는 display:none 을 적용하는데, 이 때문에 페이드 인아웃(opacity에 대한 transition)을 적용하는데는 적절하지 않았다. display:none이 있는 경우는 정상적으로 transition을 적용할 수 없다. 

 

내가 원하는 모달창을 만들기 위한 요건은 다음과 같다.

  • 모달창은 비활성화 되어있을 때에는 아예 없는 것처럼 동작해야한다. (투명도만 0인상태로 다른 요소를 가리면 안된다)
  • 모달창은 활성화 될 때 페이드인, 비활성화 될 때 페이드아웃 애니메이션을 표시해야 한다.
  • 페이드인/아웃(transition : opacity __; )효과의 적용은 display:none -> display:flex 만으로는 안된다

특히 3번에 대한 고민과 실험에 상당히 많은 시간을 들였는데, 그나마 간단한 방법으로 해결하기로 했다. 

 

해결


modal의 모양을 정하는 클래스 외에, 투명도와 실제 스테이지위치 상태를 결정하는 클래스를 정의한다. (각각 opaque, unstaged)

  • fade-in의 경우는 opacity와 unstaged를 각각 토글한다.
  • fade-out의 경우는 opaque를 토글 한 뒤, 적절한 때에 transitionend 이벤트를 사용하여 unstaged를 토글한다.

자세한 설명은 아래와 같다.

 

모달창이 가지는 클래스 상태는 세 가지이다.

  • modal unstaged : 화면에 존재하지 않음 + 투명함 [초기상태]
  • modal opaque : 화면에 존재하며 눈에 보임
  • modal : 화면에 존재하며 눈에 보이지 않음

unstaged는 컨테이너를 사용자가 보지 못하는 영역으로 치워버리고, opaque는 컨테이너의 opacity를 1로 설정한다.

이 모달 코드는 다음과 같은 순서에 따라 작동한다

(초기상태)

→ (열기 버튼 클릭) → unstaged 제거 & opaque 추가 → opacity transition진행 → modal열림

→ (닫기버튼 클릭) →opaque 제거 → opacity transition 진행 → [transition종료 이벤트] → unstaged추가

→ (초기상태)

See the Pen modal - opacity transition by wonjinyi (@wonjinYi) on CodePen.

 

 

<!-- HTML -->

<body>
  <a id="modal-open">click me</a>	

  <div id="modal-container" class="modal unstaged">
    <div class="modal-overlay">

    </div>
    <div class="modal-contents">
      <div class="modal-text">
        this is text
      </div>
      <a id="modal-close" class="modal-close">X</a>		
    </div>
  </div>
</body>
.modal{
	opacity : 0;
	
	position: fixed;
	top: 0px; left: 0;
	width: 100%; height: 100%;
	
	max-width : 100%;
	max-height : 100%;

	display: flex;
	justify-content: center;
	align-items : center;
	
	transition : opacity 0.5s;
}
.modal.opaque{
	opacity : 1;
	transition : opacity 0.5s;
}
.modal.unstaged {
	top:-100px;
	height : 0;
}

.modal-overlay{
	position : absolute;
	
	width : 100%;
	height : 100%;
	background-color : RGBA(0, 0, 0, 0.6);
}

.modal-contents{
	display:flex;
	flex-direction : column;
	align-items : center;
	position: relative;
	
	padding: 50px 100px;
	width: auto;
	max-width : 80%;
	max-height : 80%;

	text-align: center;
	background-color: rgb(255,255,255);
	border-radius: 6px;
}

.modal-text{
	text-align:left;
	overflow: auto;
}

.modal-close{
	margin-top : 20px;
	padding : 5px 20px;
	
	color : #FFFFFF;
	font-weight : bolder;
	font-size : 30px;
	background-color : #ffaa00;
	border-radius : 7px;
	cursor : pointer;
	
	transition : all 0.5s;
}
.modal-close:hover{
	background-color : #ffd500;
}
//javascript

  //open modal
  document.getElementById('modal-open').addEventListener("click", function(e){
    document.getElementById('modal-container').classList.toggle('opaque');
      document.getElementById('modal-container').classList.toggle('unstaged');
  });

  //close modal
  document.getElementById('modal-close').addEventListener("click", function(e){
    document.getElementById('modal-container').classList.toggle('opaque');

    document.getElementById('modal-container').addEventListener('transitionend', function(e){
      this.classList.toggle('unstaged');
      this.removeEventListener('transitionend',arguments.callee);
      });
    });

 

Comments