문제 현상
- 중앙에 렌더링되는 모달이 사라졌다가 나타나는게 딱딱하다고 느껴져서, 트랜지션 속성을 통해 부드럽게 렌더링하려고 시도하였다.
- 조건부 렌더링에 따라 useState의 상태에 따라 'flex'와 'none'을 조건으로 transition을 주었는데, 적용이 되지 않았다.
원인
- 원인은 브라우저의 Render tree에 있었다.
- Render tree는 DOM과 CSSOM을 종합하여 화면에 렌더링 될 요소들을 결정하며, 보이지 않아도 되는 요소들(meta, script 등)은 렌더링하지 않는다.
- 이떄, 일부 노드도 CSS를 통해 렌더링되지 않게 되는데, display:none이 이 경우에 포함된다.
- display: none은 요소가 보이지 않는 것 뿐만 아니라, 레이아웃에서도 제외되기 때문에 Render tree에서 사라진다.
- 결론적으로, Transition 속성은 Render tree 내 존재하는 요소의 변화 과정에 적용하는 속성이지만, display: none은 Render tree 내에 존재하지 않기 때문에 제대로 동작하지 않는 것이다.
해결
- 따라서 display의 값에 따라 transition을 주는 방법 말고, 노드가 display:flex로 변경되어 Render tree 내 생성된 후, animation 속성을 부여하여 해결하였다.
스타일 코드
const Container = styled.div<{ display: string }>`
display: ${(props) => props.display || "none"};
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.6);
z-index: 1000; /* 다른 요소들보다 위에 위치하도록 설정 */
`;
const ModalBackground = styled.div`
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
`;
const ModalWindow = styled.div`
display: flex;
flex-direction: column;
align-items: center;
position: relative;
margin-bottom: 350px;
border-radius: 10px;
background: ${(props) => props.theme.modalBgColor};
padding: 40px 30px 30px 30px;
animation: modaldown 0.3s linear;
z-index: 1001; /* 모달 배경이 컨테이너 뒤에 위치하도록 설정 */
@keyframes modaldown {
from {
transform: translateY(-10%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
JSX
const CenterModal = ({
display,
controlFunc,
children,
}: {
display: string;
controlFunc: () => void;
children: React.ReactNode;
}) => {
return (
<Style.Container display={display}>
<Style.ModalBackground onClick={controlFunc} />
<Style.ModalWindow>
<p className="close" onClick={controlFunc}>
×
</p>
{children}
</Style.ModalWindow>
</Style.Container>
);
};
Vibility: hidden 트랜지션 적용
- 위에 언급했던 문제를 해결하기 위해 시도했던 방법이다.
- 이 경우에는 transition의 delay만 적용되었다.
- transition 속성은 opacity와 같이 수치로 나타낼 수 있는 속성에만 적용이 가능하기 때문에 발생한 현상이였다.
// delay만 적용
visibility: ${(props) => (props.isVisible? 'visible' : 'hidden')};
transition: visibility 5s ease-in-out 1s;
// 정상 적용
opacity: ${(props) => (props.isCreated ? 1 : 0)};
transition: opacity 1s ease-in-out;