styled component에 keyframe을 이용해 animation을 적용하려면 keyframes를 따로 import 해줘야 한다.
import styled, { keyframes } from "styled-components";
그리고 다른 styled component를 선언할 때와 마찬가지로 keyframe을 만들어 준다.
const PlayingText = keyframes`
...
}
`;
이번에 개발한 기능은 음성 파일이 재생될 때 시간에 따라 (노래방 기계처럼) 색이 입혀지는 텍스트였다.
단순히 텍스트의 색을 지정하기 위해서는 css의 color 속성을 사용하면 된다.
하지만
이렇게 그라데이션을 넣거나,
이렇게 이미지를 텍스트의 배경으로 쓰고 싶다면 어떻게 해야 할까?
background-clip: text;
-webkit-background-clip: text;
color: transparent;
기본적으로 위와 같은 속성들을 작성해야 한다.
background-clip 속성은 요소의 배경이 테두리, 안쪽 여백, 콘텐츠 상자 중 어디까지 차지할지를 지정하는 속성이다. 이걸 text로 설정해 요소의 배경이 텍스트에만 적용되게 해야 한다. 그리고 텍스트에 색이 있으면 배경이 보이지 않기 때문에 color는 transparent로 설정한다.
재생 기능을 위해 내가 떠올린 아이디어는 다음과 같다.
이렇게 재생 후와 재생 전의 색을 이어 붙인 배경을 만들어, 투명한 텍스트 뒤에서 오른 쪽으로 움직이도록 하는 것이다. 이렇게 하면 마치 검은색의 텍스트가 주황색으로 칠해지는 것처럼 보일 것이다.
우선 저런 배경을 만들기 위해 linear-gradient를 사용했다.
https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient
linear-gradient() - CSS: Cascading Style Sheets | MDN
The linear-gradient() CSS function creates an image consisting of a progressive transition between two or more colors along a straight line. Its result is an object of the <gradient> data type, which is a special kind of <image>.
developer.mozilla.org
background-image: linear-gradient(to right, orange 50%, black 50% 100%);
이렇게 왼쪽 절반은 주황색, 오른쪽 절반은 검은색인 배경을 설정한 뒤
background-size: 200% 100%;
가로 너비를 두 배로 만들어 주면 애니메이션이 한 번 실행된 후 주황색 배경이 텍스트를 꽉 채우게 된다.
추가로 나는 각 단어가 실제 음성 파일과 같은 속도로 재생되는 것처럼 만들어야 했기 때문에, 각 단어의 재생 시간을 duration이라는 props로 받아 animation-duration 속성을 설정했다.
재생되는 텍스트의 전체 코드는 다음과 같다.
const PlayingText = keyframes`
from {
background-position-x: 0%;
}
to {
background-position-x: 100%;
}
`;
const Text = styled.span`
position: relative;
background-clip: ${(props) => (props.played === "playing" ? "text" : "")};
-webkit-background-clip: ${(props) =>
props.played === "playing" ? "text" : ""};
color: ${(props) =>
props.played === "playing"
? "transparent"
: props.played === "played"
? "orange"
: "black"};
background-image: ${(props) =>
props.played === "playing"
? "linear-gradient(to right, orange 50%, black 50% 100%)"
: ""};
background-size: 200% 100%;
background-position-x: 0%;
animation-name: ${(props) => (props.played === "playing" ? PlayingText : "")};
animation-duration: ${(props) => props.duration}s;
animation-timing-function: linear;
animation-iteration-count: 1;
animation-direction: reverse;
animation-fill-mode: forwards;
background-color: ${(props) => props.color};
margin-right: ${(props) => (props.continued ? "none" : "5px")};
padding-right: ${(props) => (props.continued ? "5px" : "none")};
text-decoration: ${(props) => (props.edited ? "underline" : "none")};
&:hover {
text-decoration: orange dashed underline;
}
`;
단순히 재생만 할 것이라면 상관없지만, 내가 개발 중인 스크립트는 특정 단어를 눌렀을 때 그 단어로 재생 바가 이동하기 때문에 현재 재생되는 단어 앞의 단어들도 다시 animation이 작동하는 문제가 생겼다. 이건 played props를 playing, played, not played 세 개로 구분해서 해결했다.
그리고 한참 고생했던 것 중에 animation-iteration-count를 infinite로 설정했을 때는 잘 작동하는데 1로 설정했을 때는 작동하지 않는 문제가 있었다.
animation-name: ${(props) => (props.played === "playing" ? PlayingText : "")};
이 부분에서 played를 체크하지 않았기 때문에 발생한 문제였다. not played 상태인 텍스트는 color가 black으로 지정되어 있기 때문에 애니메이션이 보이지 않는다. 그래서 저 부분에 둘의 구분을 두지 않았는데 played가 playing이 되었을 때 animation을 변경하는 과정이 없으면 다시 동작하지 않는다. infinite일 때는 color가 transparent로 바뀌어 뒤에서 움직이고 있던 배경이 보이게 되었을 뿐이었다.
이렇게 재생되는 텍스트를 성공적으로 완성했다!
'React' 카테고리의 다른 글
useEffect로 state 변경 바로 감지하기 (4) | 2023.08.08 |
---|---|
State 작동 방식 (0) | 2023.08.08 |
Error Boundary (0) | 2023.07.15 |
Styled component로 React Component 디자인 (0) | 2023.07.13 |
useState로 인스턴스 접근 (0) | 2023.07.13 |