기록

CSS 연습 - button 03

als982001 2023. 6. 24. 21:56

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>&nbsp;</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