현재 CD 목록 페이지에서 3D 스와이퍼를 사용하여 슬라이더마다 제목과 가수명을 보여주고 있습니다.

만약 제목이 굉장히 길 경우에는 슬라이딩 에니메이션 처리를 해주고 싶었습니다.
구현결과 미리보기

초기에 생각해낸 방법은 제목의 길이가 10 이상일 경우,
제목이 우측에서 출발에서 좌측으로 사라지는 슬라이딩 에니메이션 효과를 주려 하였습니다.
아래는 커스텀한 슬라이딩 에니메이션 효과입니다.
--animate-slideTitle: slideTitle 10s linear infinite;
@keyframes slideTitle {
0% {
transform: translateX(100%); /* 화면 오른쪽에서 시작 */
}
100% {
transform: translateX(-100%); /* 화면 왼쪽으로 완전히 사라짐 */
}
}
🚨 문제 배경
길이가 긴 제목일 경우에 제목이 좌측으로 다 사라지지 않았음에도 에니메이션이 재시작되는 문제가 발생하였습니다.
export default function SlidingTitle({ text }: { text: string }) {
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
if (text.length > 10) setIsAnimating(true);
return () => setIsAnimating(false);
}, [text]);
return (
<div className='overflow-hidden whitespace-nowrap w-[427px] '>
<h1
className={`text-white text-[40px] font-bold ${
isAnimating ? 'animate-slideTitle' : ''
} `}>
{text}
</h1>
</div>
);
}
문제가 발생한 긴 제목 부분을 확인해본 결과, 제목이 길 경우
고정 너비가 지정된 부모 태그에 의해 h1 태그도 부모태그의 너비속성을 물려받아 제목이 길 경우 제목 자체가 잘려서 제목이 좌측으로 다 사라지지 않았음에도 에니메이션이 재시작되는것처럼 보인 것입니다
🔥 시도했던 방법들
1) 그렇다면 부모의 너비를 고정으로 두지말고 w-full로 주면 해결이 될까요?
제목의 길이에 따라 슬라이딩 애니메이션이 적용되는 너비가 달라지기 때문에 UI상 보기 좋지않습니다.
2) 제목의 너비와 부모의 너비를 비교해서 제목의 너비가 클 경우 애니메이션의 스크롤 속도를 동적으로 조절하고 h1태그를 postion:absolute를 주어 부모 태그 바로 우측에서 나오도록 설정합니다.
export default function SlidingTitle({ text }: { text: string }) {
const [isAnimating, setIsAnimating] = useState(false);
const titleRef = useRef(null);
const containerRef = useRef(null);
useEffect(() => {
if (!titleRef.current || !containerRef.current) return;
const titleElement = titleRef.current;
const containerElement = containerRef.current;
// 제목이 컨테이너보다 클 경우에만 애니메이션 적용
if (titleElement.scrollWidth > containerElement.offsetWidth) {
setIsAnimating(true);
const speed = titleElement.scrollWidth / 100; // 픽셀당 속도 조정
titleElement.style.animationDuration = `${speed}s`;
} else {
setIsAnimating(false);
}
return () => setIsAnimating(false);
}, [text]);
return (
<div
ref={containerRef}
className='truncate w-[200px] xl:w-[300px] 2xl:w-[470px] h-[60px] relative '>
<h1
ref={titleRef}
className={` text-white text-[25px] xl:text-[30px] 2xl:text-[40px] font-bold inline-block ${
isAnimating
? ' animate-slideTitle absolute top-0 right-0 z-[5] '
: 'relative '
}`}>
{text}
</h1>
</div>
);
}

거의다 되었습니다. 그러나 현재는 부모태그에 고정너비를 지정하였기 때문에 화면 비율이 달라졌을때 자연스러운 UI를 보여주기 어렵습니다.
3) 결국 화면 비율에 따라 달라지는 Swiper Slide의 너비를 따라서 부모 너비가 변해야합니다.
props로 전달받은 slide의 width를 동적으로 부모의 너비에 지정해주어 비율이 달라지더라도 Swiper Slide 너비에 맞게 슬라이딩 에니메이션이 적용되도록 합니다.
export default function SlidingTitle({
text,
width,
}: {
text: string;
width: number;
}) {
const [isAnimating, setIsAnimating] = useState(false);
const titleRef = useRef(null);
const containerRef = useRef(null);
useEffect(() => {
if (!titleRef.current || !containerRef.current) return;
// DOM 업데이트 후 정확한 크기 비교를 위해 setTimeout 사용
const titleElement = titleRef.current!;
const containerElement = containerRef.current!;
// 제목이 부모보다 클 경우 애니메이션 적용
if (titleElement.scrollWidth > containerElement.offsetWidth) {
setIsAnimating(true);
const speed = titleElement.scrollWidth / 70; // 애니메이션 속도 조정
titleElement.style.animationDuration = `${speed}s`;
} else {
setIsAnimating(false);
}
return () => {
setIsAnimating(false);
};
}, [text, width]);
return (
<div
ref={containerRef}
style={{ width: `${width}px` }} // ✅ Tailwind 대신 style 적용
className='overflow-hidden whitespace-nowrap items-center h-[60px] relative'>
<h1
ref={titleRef}
className={`text-white text-[25px] xl:text-[30px] 2xl:text-[40px] font-bold inline-block ${
isAnimating
? 'animate-slideTitle absolute top-0 right-0 z-[5]'
: 'relative'
}`}>
{text}
</h1>
</div>
결과

👀 이전 코드와 비교
이전 코드는 부모 너비를 고정으로 두고 제목 너비가 다르더라도 일정한 속도로 에니메이션 효과를 주어 제목의 너비가 짤려 중간에 에니메이션이 재시작되는 문제가 있었습니다.
수정된 코드는 부모의 너비가 Swiper Slide의 너비에 따라 동적으로 변경되고, 제목의 너비와 부모의 너
비를 비교하여 동적으로 에니메이션 속도를 처리해주어 부드러운 슬라이딩 에니메이션을 보여줍니다.
🤩 알게된 점
슬라이딩 에니메이션 처리를 해줄 경우에는 부모와 자식의 너비를 비교해서 동적으로 에니메이션 속도를 지정해주어야 정상 작동한다는 것을 알게되었습니다.