CSS 연습 - button 03
1. 구현해야 할 것
오늘 구현해볼 button이다. 이 버튼에서 구현해야할 것은 다음과 같다.
- 버튼의 기본 디자인
- 마우스를 올릴 경우 버튼 속의 글자가 한 글자씩 내려오는 애니메이션
- 마우스로 클릭할 경우 버튼이 눌리는 듯한 모습
- 마우스를 버튼에서 뗄 경우 글자가 위로 떠오르는 애니메이션
2. 구현
2-1. 버튼의 기본 디자인
가장 먼저 버튼의 기본 디자인을 구현해보았다. 버튼의 width와 height 값은 Page Ruler Redux 확장 프로그램으로 측정한 값을 이용하였다.
import styled from "styled-components";
const bgColor = "#0071E1";
const shadowColor = "#0D5DAB";
const Container = styled.div`
width: 80%;
min-width: 200px;
height: 200px;
border: 2px solid black;
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
`;
const MyButton = styled.div`
width: 180px;
height: 60px;
background-color: ${bgColor};
border-radius: 15px;
box-shadow: 0px 7px 1px ${shadowColor};
display: flex;
justify-content: center;
align-items: center;
position: relative;
`;
const Texts = styled.div`
position: absolute;
width: 100%;
height: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
justify-content: center;
align-items: center;
color: white;
font-weight: bold;
`;
export default function Button03() {
return (
<>
<Container>
<MyButton>
<Texts>
<span className="alp-1">H</span>
<span className="alp-2">O</span>
<span className="alp-3">V</span>
<span className="alp-4">E</span>
<span className="alp-5">R</span>
</Texts>
</MyButton>
</Container>
</>
);
}
버튼의 width, height, background-color, box-shadow의 색은 적당히 측정된 값을 이용하였다. box-shadow( box-shadow: 0px 7px 1px ${shadowColor};)에서 blur의 값은 1px로 주어 원래 버튼의 그림자를 최대한 흉내내었다. 그리고 버튼 안의 문자열은 "TONY STARK"에서 "HOVER"로 바꾸었으며 알파벳 한 글자마다 span태그로 감싼 후 클래스를 주었다. 왜냐하면 마우스를 올릴 경우, 한 글자씩 씩 따로 애니메이션을 적용해야하기 때문이다.
2-2. 마우스를 올릴 경우 버튼 속의 글자가 한 글자씩 내려오는 애니메이션
이 다음으로 구현한 것은 글자가 떨어지는 애니메이션이다.
// 나머지 생략
const Texts = styled.div`
// 나머지 생략
@keyframes dropAlp {
from {
transform: translateY(-50px);
}
to {
transform: translateY(0px);
}
}
&:hover {
& .alp-1 {
animation: dropAlp 0.1s linear;
}
& .alp-2 {
animation: dropAlp 0.2s linear;
}
& .alp-3 {
animation: dropAlp 0.3s linear;
}
& .alp-4 {
animation: dropAlp 0.4s linear;
}
& .alp-5 {
animation: dropAlp 0.5s linear;
}
}
&:active {
& span {
}
}
`;
// 나머지 생략
Texts에 hover 이벤트를 적용하였는데, 이는 Texts라는 div가 width와 height가 버튼의 width, height와 같으면서 이를 전부 채우고 있기 때문이다. 그리고 위에서 아래로 떨어지는 애니메이션을 구현하기 위해 @keyframes dropAlp를 이용하였다. 이는 처음에는 50px 위로 이동시킨 후, 원래 자리로 옮기는 애니메이션이다. 그리고 알파벳 가장 왼쪽 알파벳에는 이 keyframes를 0.1초동안 동작하게 하고 가장 마지막 알파벳은 0.5초동안 동작하게 하였다. 이를 통해 시간차를 가지며 떨어지는 알파벳을 구현할 수 있었다. 그리고 버튼을 클릭했을 때 버튼이 눌리는 듯한 모습을 구현하였다.
2-3. 마우스로 클릭할 경우 버튼이 눌리는 듯한 모습
const MyButton = styled.div`
width: 180px;
height: 60px;
background-color: ${bgColor};
border-radius: 15px;
box-shadow: 0px 7px 1px ${shadowColor};
display: flex;
justify-content: center;
align-items: center;
position: relative;
&:active {
box-shadow: none;
transform: translateY(7px);
}
`;
active 가상 선택자를 이용해 버튼이 눌렸을 경우를 구현하였다. 우선, box-shadow를 없앴다. 이를 통해 버튼이 푹 들어갔다는 느낌을 줄 수는 있었지만, 단순히 그림자가 없어졌을 뿐, 위치가 변하지 않았기에 밋밋한 느낌을 준다. 그래서 transform을 이용해 7px 밑으로 내렸다. 딱 7px을 내린 이유는 단순히 box-shadow의 y-position이 7px이기 때문이다. 이를 통해 버튼이 눌리는 듯한 모습을 구현할 수 있었다. 하지만 마우스를 버튼에서 뗄 경우 글자가 위로 떠오르는 애니메이션을 구현하려고 했는데, 여기서 막혔다. 마우스를 뗄 경우 이용할 수 있는 가상 선택자가 있는지 구글링으로 확힌해봤지만 찾을 수 없었다. 그렇기에 샘플 버튼의 코드를 확인해보았다.
3. 샘플 버튼 분석
import styled from "styled-components";
const SampleButton = styled.button<{ alt: string }>`
display: flex;
align-items: center;
justify-content: center;
height: 50px;
position: relative;
padding: 0 20px;
font-size: 18px;
text-transform: uppercase;
border: 0;
box-shadow: hsl(210deg 87% 36%) 0px 7px 0px 0px;
background-color: hsl(210deg 100% 44%);
border-radius: 12px;
overflow: hidden;
transition: 31ms cubic-bezier(0.5, 0.7, 0.4, 1);
&:before {
content: attr(alt);
display: flex;
align-items: center;
justify-content: center;
position: absolute;
inset: 0;
font-size: 15px;
font-weight: bold;
color: white;
letter-spacing: 4px;
opacity: 1;
}
&:active {
box-shadow: none;
transform: translateY(100%);
opacity: 0;
}
&:hover:before {
transition: all 0s;
transform: translateY(100%);
opacity: 0;
}
& i {
color: red;
font-size: 15px;
font-weight: bold;
letter-spacing: 4px;
font-style: normal;
transition: all 2s ease;
transform: translateY(-20px);
opacity: 0;
}
&:hover i {
transition: all 0.2s ease;
transform: translateY(0px);
opacity: 1;
}
&:hover i:nth-child(1) {
transition-delay: 0.045s;
}
&:hover i:nth-child(2) {
transition-delay: calc(0.045s * 3);
}
&:hover i:nth-child(3) {
transition-delay: calc(0.045s * 4);
}
&:hover i:nth-child(4) {
transition-delay: calc(0.045s * 5);
}
&:hover i:nth-child(6) {
transition-delay: calc(0.045s * 6);
}
&:hover i:nth-child(7) {
transition-delay: calc(0.045s * 7);
}
&:hover i:nth-child(8) {
transition-delay: calc(0.045s * 8);
}
&:hover i:nth-child(9) {
transition-delay: calc(0.045s * 9);
}
&:hover i:nth-child(10) {
transition-delay: calc(0.045s * 10);
}
`;
export default function Button03() {
return (
<>
<Container>
<SampleButton alt="tony stark">
<i>t</i>
<i>o</i>
<i>n</i>
<i>y</i>
<i> </i>
<i>s</i>
<i>t</i>
<i>a</i>
<i>r</i>
<i>k</i>
</SampleButton>
</Container>
</>
);
}
위의 코드에서 TONY STARK라는 문자열은 버튼 자식 i(이하 i)들과 button::before의 content(이하 before)로 두 개가 존재한다. 그래서 이를 구분하기 위해 i의 색을 빨간색으로 바꾸었다. 우선, before의 역할에 대해 분석해보았다. before는 마우스가 올려져있지 않을 때의 글자를 보여준다. 그리고 이 TONY STARK라는 문자열을 content: attr(alt)를 통해 가져왔는데, 이는 상황에 따라 적절히 변경하면 될 것이다. 또한 font-size, letter-spacing 등을 보면 i와 전부 같은데, 이는 글자 애니메이션을 자연스럽게 하기 위함으로 보인다. 그리고 마우스가 올려졌을 때, &:hover:before의 코드를 통해 완전히 투명하게 만든다. 그리고 이 사라진 글자를 i가 채운다.
i는 마우스가 올려졌을 때, 위에서 하나씩 내려올 문자열들이다. 그렇기에 처음에는 버튼 속 문자열(before)보다 위에 있어야 한다. 그리고 이를 & i 속의 코드에 transform: translateY(-20px)로 구현되었다. 그리고 마우스가 hover되었을 때, before가 사라지고 i가 내려와야 한다. 이는 &:hover i 속 transform: translateY(0px)로 구현되었다. 그리고 글자들은 왼쪽부터 내려오는데, 이는 nth-child에서 transition-delay를 통해 구현되었다. 마지막으로, 마우스를 버튼을 벗어날 경우, 글자가 올라가는 애니메이션이 존재하는데, 이는 i가 처음에 (hover가 아닐 때) transform: translateY(-20px)이었기에, 다시 이 상태로 돌아가는 것이 보이는 것이다. 요약하자면, button 속 알파벳 한 글자마다 따로 i 태그로 감싸준 후, 처음 위치를 transform: translateY(-20px), opacity: 0으로, hover일 때 transform: translateY(0px)로 설정한다. 그리고 button::before에 똑같은 문자열을 넣어준 후, hover되었을 때 transform: translateY(100%), opacity: 0으로 설정하여 버튼의 자식 문자열이 보이게 한다.
https://uiverse.io/barisdogansutcu/loud-mayfly-89
https://github.com/als982001/css-challenge/blob/main/src/Components/Buttons/Button03.tsx