본문 바로가기

UI 디자인 애니메이션 연구소

바닐라 JS/HTML/CSS 로 재밌는 애니메이션 효과 만들기 (3탄)

반응형

이전 애니메이션 | HTML/CSS 만으로 재밌는 마우스 호버 애니메이션 구현하기

 

HTML/CSS 만으로 재밌는 마우스 호버 애니메이션 구현하기(var 함수 활용)(2탄)

1탄 | JS/HTML/CSS 로 랜딩 애니메이션 구현 바닐라 자바스크립트로 재밌는 애니메이션 /효과 구현해보기 (1탄)결과 미리보기이번 포스팅을 잘 따라오시면 아래와 같은 애니메이션을 구현할 수 있

duklook.tistory.com

 

미리보기

이번 포스팅을 잘 따라오시면 아래와 같은 네비게이션 애니메이션을 구현하실 수 있는 능력을 얻을 수 있습니다.
 


HTML

<header>
  <nav>
    <ul>
      <li class="link"><a href="#">Home</a></li>
      <li class="link"><a href="#">About</a></li>
      <li class="link"><a href="#">Contact</a></li>
      <li class="link"><a href="#">Project</a></li>
      <li class="link"><a href="#">Post</a></li>
      <li class="link"><a href="#">Map</a></li>
      <li id="underline"></li>
    </ul>
  </nav>
</header>

 
우선 헤더 내 위치하는 네비게이션 메뉴인 만큼 시멘틱 태그를 이용하여 위와 같이 만들어 줍니다. 크게 설명할 내용은 없으므로 바로 CSS 작업으로 넘어 갑니다.
 

CSS

여백 제거 및 기본 스타일 속성 초기화

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

 
우선 html 내부에 기본적으로 적용되어 있는 여백을 제거하기 위해 margin 과 padding 을 모두 0으로 지정해줍니다. box-sizing 을 border-box 로 지정하여, 콘텐츠 요소의 넓이와 높이를 계산할 때 여백의 사이즈도 포함되도록 해줍니다. 
 

li {
  list-style: none;
}
a {
  text-decoration: none;
  color: black;
}

 
그 다음에는 li 태그와 a 태그에 기본적으로 적용된 스타일 속성을 제거하기 위해 li 태그에는 list-style 속성을 none 으로 지정하여 ● 이 보이지 않도록 해줍니다.
 
그 후 a 태그에 기본적용되는 밑줄 효과를 제거하기 위해 text-decoration 을 none 으로 지정합니다. 또한 기본 텍스트 color 가 blue 로 지정되어 있으므로 이를 black 으로 바꿔줍니다.
 

header 태그 꾸미기

header {
  height: 100vh;
  width: 100px;
  background: #f4f4f4;
}

 
그 다음에는 header 태그를 꾸며줍니다. 우선 앞서 미리보기 영상에서 처럼 만들기 위해 header 의 넓이를 100px 로 지정해주고, 높이(height)를 100vh 로 지정하여 뷰포트의 100% 를 차지하도록 설정해줍니다. background 는 입맛에 맞게 설정해주시면 됩니다. 저는 연한 그레이 색이 되도록 16진수로 표기하였습니다.
 

nav, li, a 태그 꾸미기

  nav {
    ul {
      position: relative;
      li {
        padding: 5px;

        a {
          z-index: 1;
          font-size: 1.2em;
        }
      }
    }

 
그 다음에는 실질적인 네비게이션에 해당하는 태그들을 꾸며주겠습니다. ul 태그에는 position 을 relative 로 설정하여 향후 자식 태그가 absolute 인 경우 절대적인 좌표(0,0)의 기준점이 되도록 설정 해줍니다.
 
li 태그에는 padding 을 5px 정도 주어서 태그 사이에 공간을 만들어줍니다.
 
a 태그에는 z-index: 1 로 지정하였는데, 향후 underline 으로 지정되는 li 태그 보다 레이어 순위를 높이기 위해 지정하였으나 사실 제거해주어도 무방합니다(없어도 동작이나 시각적인 부분에서 차이가 없습니다).
 
fond-size 는 1.2em 으로 지정하여 부모 태그의 font-size 의 설정값에 대한 상대적인 배수 크기로 텍스트 크기를 결정하도록 설정해줍니다. 예를 들어 a 태그의 상위 태그인 li 에 지정한 font-size 가 15px 인 경우에는 1em 의 크기는  15px 가 됩니다. 이 말은 1.2em 은 15px * 1.2 = 18px 가 a 태그의 font-size 로 설정된다는 것입니다.
 

underline 역할을 하는 li 태그 꾸미기

/* 네비게이션 아랫줄 */
#underline {
  transition: 1s;
  z-index: 0;
  user-select: none;
  top: 0;
  position: absolute;
  padding: 1.5px;
  border-radius: 20px;
  visibility: hidden;
  background: gray;
  opacity: 0;
}

 
그 다음에는 밑줄 효과를 적용하는 underline 을 꾸며보겠습니다. 우선 transition 을 1s 초 설정하여 향후 mousemove 시 자연스러운 가속도를 주어 따라오는 효과를 줄 수 있도록 합니다.
 
z-index 의 경우에는 0으로 지정되어 있는데, 굳이 필요한 속성은 아니므로 빼주어도 무방합니다. user-select 의 경우 none 으로 지정하여 향후 사용자가 underline 을 클릭하여 예기치 못한 레이아웃 깨짐이 발생하지 않도록 사용자의 선택이 불가능하도록 설정해줍니다.
 
position 의 경우 absolute 로 지정함으로써 앞서 relative 로 지정한 상위 부모 태그가 차지하는 공간을 기준으로 독립적인 레이어를 형성하게 함으로써, 절대적인 좌표축을 가지도록 설정 합니다.
 
마지막으로 visibility: hidden, opacity:0 으로 지정하여 ul 태그에 hover(마우스를 태그 위로) 하기 전에는 화면상에서 보이지 않도록 설정해줍니다. 이때 주의할 점은 이렇게 숨겨진 요소의 경우에도 스크린 리더기에는 요소가 잡히기 때문에 실제 프로젝트에 적용 시에는 aria-hidden 과 같은 ARIA 를 적절하게 적용해주어야 합니다.
 

ul 호버 시 underline 보이게 설정하기

ul:hover {
  #underline {
    opacity: 1;
    visibility: visible;
  }
}




마지막으로 link 역할을 하는 li 태그를 감싸고 있는 부모 태그인 ul 태그에 호버하는 경우 underline 이 보이도록 설정해주면 CSS 작업은 끝입니다.
 

JS

이제 자바스크립트 작업을 시작해봅시다.
 

a 태그 와 underline 역할을 하는 li 태그 가져오기

const linkList = document.querySelectorAll(".link a");
const underline = document.getElementById("underline");

 
우선 document 객체의 queyrSelectorAll 과 getElementById 메소드를 사용해서 각각 a 태그와 underline id 가 부여된 li 태그를 선택해서 가져와 줍니다. 이렇게 되면 linkList 와 underline 변수에는 각각의 태그에 대한 참조가 담기게 됩니다.
 
여기서 주의할 점은 linkList 에 담기는 배열은 NodeList 라고 불리는 유사배열 형태가 됩니다. 배열은 배열이므로 forEach, map 과 같은 메소드를 사용할 수 있으나, 결국 배열의 동작을 흉내낸 것이므로 pop, slice 와 같은 일부 메소드의 경우에는 지원하지 않는다는 특징이 있습니다.
 
 

a 태그 리스트 순회 및  traceLink 함수를 각 a  태그에 대한 mousemove 이벤트 핸들러에 등록하기

linkList.forEach((link) => {
  link.addEventListener("mousemove", traceLink);
});

 
그 다음에는 앞서 가져온 NodeList 에 담긴 a 태그를 forEach 메소드를 사용하여 순회하고, 매개변수로 입력받은 각 a 태그에 addEventListener("mousemove", traceLink) 를 적용하여 mousemove 이벤트가 발생할 때 traceLink 함수가 콜백으로 호출되도록 설정해줍니다.
 

traceLink 함수 구현하기 | x 와 y 값 구하기

그 다음에는 실질적인 애니메이션 동작 구현을 담당하는 traceLink 함수를 구현해줍니다.
 

x와 y 값 구하기

우선 mousemove 이 가로축과 세로축 이동 좌표에 대한 값을 추적하기 위해 x 와 y 를 구해주어야 합니다.

function traceLink(e) {
  const target = e.currentTarget;
  const x = target.offsetLeft;
  const y = target.offsetTop + target.getBoundingClientRect().height;
}


좌표를 구하기 앞서 해당 함수의 매개변수로 Event 객체를 첫 번째 자리에 받게 됩니다. 이는 mouse 이벤트에 대한 정보를 담고 있고, e.currentTarget 으로 접근하게 되면 해당 이벤트가 등록된 최상위 태그를 target 변수에 할당하게 됩니다. 예를 들어, 현재 이벤트가 등록된 대상은 a 태그 이므로 target 에는 <a herf="#"></a> 태그가 담겨있게 되는 것이죠.
 
이렇게 반환된 target 변수는 실제 a 태그에 대한 참조이므로 해당 태그가 가지고 있는 offsetLeft 속성과 offsetTop 이라는 속성에 접근할 수 있게 됩니다. 이는 각각 a 태그를 감싸고 있는 상위 태그의 좌측 및 상단 끝 모서리 부터 a 태그의 좌측 및 상단 모서리 까지의 거리를 나타내는 px 단위의 값을 나타냅니다.
 
이렇게 접근할 수 있는 offsetLeft 값은 x 변수에 담아주고, offsetTop 을 통해 구해진 값을 y 변수에 담아주어, 향후 mousemove 이벤트 호출 시 변화되는 값을 계속해서 추적할 수 있도록 설정 해줍니다.
 
y 변수에 할당되는 표현식을 잘 보시면,  target.getBoundingClientRect().height 가 보일 겁니다. 이는 target 이 되는 a 태그가 차지하는 실제 높이인 height 를 의미합니다. 이 값을 offsetTop 에 더해주는 이유는 offsetTop 만 사용하는 경우에는 underline 이 a 태그의 상단에 위치하기 때문에 a 태그의 높이 만큼 더해주어서 하단에 밑줄이 그어지도록 하기 위해서 입니다.
 

[참고 링크] 참고로, offset 과 getBoundingClientRect 대한  설명이 이해가 되지 않으신다면

 

HTMLElement: offsetLeft property - Web APIs | MDN

The HTMLElement.offsetLeft read-only property returns the number of pixels that the upper left corner of the current element is offset to the left within the HTMLElement.offsetParent node.

developer.mozilla.org

 

 

Element: getBoundingClientRect() method - Web APIs | MDN

The Element.getBoundingClientRect() method returns a DOMRect object providing information about the size of an element and its position relative to the viewport.

developer.mozilla.org

 

traceLink 함수 구현하기 | width 구하기 및 underline 에 추적 애니메이션 적용

마지막으로 앞서 구한 x 와 y 값을 underline id 가 부여된 li 태그의 css 로 적용해주면 됩니다.

function traceLink(e) {
  const target = e.currentTarget;
  const x = target.offsetLeft;
  const y = target.offsetTop + target.getBoundingClientRect().height;
  const width = target.getBoundingClientRect().width;
  underline.style.cssText = `
   left:${x}px;
   top:${y}px;
   width:${width}px;
   `;
}

 
여기서 width 가 추가되었는데요. 해당 값을 추가해준 이유는 각 a 태그에 호버되는 경우 a 태그가 실질적으로 차지하는 width 만큼 underline 이 표시되도록 하기 위해서입니다. 이렇게만 값을 구해서 모두 적용시켜 준다면, 모든 기능 구현이 마무리 됩니다.
 

구현 결과

구현된 모든 코드와 동작을 확인해보세요. 설명만 길었지 구현된 코드는 어려운 코드가 아니니 다양하게 응용하여 멋진 애니메이션을 적용해보는 시간을 가져보시면 좋을 것 같습니다. 고생하셨습니다.
 

See the Pen Untitled by youngwan2 (@youngwan2) on CodePen.

 

다음 애니메이션 | HTML/CSS 를 이용한 시차 로딩 스피너 만들기

 

HTML/CSS 를 이용한 로딩 스피너 만들기

3탄 | 마우스 호버 시 따라다니는 언더라인 만들기 바닐라 JS/HTML/CSS 로 재밌는 애니메이션 효과 만들기 (3탄)2탄 | HTML/CSS 만으로 재밌는 마우스 호버 애니메이션 구현하기 HTML/CSS 만으로 재밌는

duklook.tistory.com

 

반응형