아코디언이 열리고 접힐 때의 애니메이션을 적용해줘야 하는데
height 값에 따른 애니메이션 적용해야 할 때, height의 정확한 값이 존재해야 한다
참고자료
스택오버플로우
보통은 max-height 를 사용하거나, 임의의 height 값을 적용하는 방식으로 해결하는 것 같다
하지만 실제 height가 아니라 임의의 값을 사용한다는 점이 좀 걸렸다
height 속성에 정확한 값을 넣어줄 수 없는 이유는
height 가 얼마인지 알 수 없다그렇다면 아코디언이 열려 있을 때, 해당 height를 구해서 css에 적용시켜주면 된다
import { useEffect, useRef } from "react"
export const useAccordionHeight = <T extends HTMLElement>(
isOpen: boolean,
duration
) => {
const contentRef = useRef<T>(null)
useEffect(() => {
const element = contentRef.current
if (element === null || !element.parentElement) {
return
}
const { parentElement } = element
if (isOpen) {
parentElement.style.display = "block"
const height = element.style.getPropertyValue("--accordion-height")
if (height === "0" || !height) {
element.parentElement!.style.setProperty(
"--accordion-height",
`${element.clientHeight}px`,
)
}
} else {
setTimeout(() => {
parentElement.style.display = "none"
parentElement.style.setProperty(
"--accordion-height",
`0`,
)
}, duration)
}
}, [isOpen])
return contentRef
}
@keyframes accordionDown {
0% {
height: 0;
}
to {
height: var(--accordion-height);
}
}
@keyframes accordionUp {
0% {
height: var(--accordion-height);
}
to {
height: 0;
}
}
간단하게 말하면 height를 css 변수를 사용해서, 열리고 닫힐 때 해당 변수값을 변경하는데
isOpen이 true 이면 -> 해당 element의 display를 block으로 변경 후 clientHeight를 조사해서 --accordion-height 를 clientHeight로 변경한다
isOpen이 false면, 타이머를 통해 몇초 뒤에 display를 none으로 변경 후 --accordion-height 를 0으로 변경한다
export const AccordionContent = ({
children,
}: PropsWithChildren<{ duration?: number }>) => {
const { isOpen, value } = useAccordionItemProvider("accordionItem")
const contentRef = useAccordionHeight<HTMLDivElement>(isOpen, 150)
return (
<div
className={css({
display: "none",
overflow: "hidden",
border: "1px solid black",
animation: isOpen
? `accordionDown 0.2s cubic-bezier(.4,0,.2,1)`
: `accordionUp 0.2s cubic-bezier(.4,0,.2,1)`,
})}
data-state={isOpen ? "open" : "close"}
aria-controls={value}
>
<div ref={contentRef}>{children}</div>
</div>
)
}
타이머는 실제 애니메이션 동작 시간보다 작게 설정해야 한다.
애니메이션 0.2s - 타이머 0.15s 정도로 설정했는데 자연스러운 것 같다