개발 기술/css 애니메이션 (with js)

svg로 gooey animation 만들기

by GicoMomg 2021. 10. 18.

잠깐! 혹시 svg로 gooey 애니메이션 만들자(1)을 보시지 않으셨나요?
(1)편에서 소개하는 svg로 효과를 만드는 법을 알아야 아래 내용을 이해하기 쉽습니다!


1. 미리보기

See the Pen gooey-menu-1 by KumJungMin (@kumjungmin) on CodePen.




2. 코드 분석

1) html

(1) 기본 구조

  • html 구조는 아래 사진과 같다.
  • 전체를 감싸는 wrap안에 1개의 button과 4개의 menu가 존재한다.
  • 각각의 menu에는 icon이 포함되어 있다.

<div class="wrap">
  <div class="button"><img class="icon" src=""></div>
  <div class="menu menu-1"><img class="icon" src=""></div>
  <div class="menu menu-2"><img class="icon" src=""></div>
  <div class="menu menu-3"><img class="icon" src=""></div>
  <div class="menu menu-4"><img class="icon" src=""></div>
</div>
...

(2) svg 필터 효과 만들기

  • 1편에서 얘기했듯이 svg를 사용하여 그래픽 효과를 만들 수 있다.
  • 이번에 만들 효과는 사진처럼 menu가 서로 들러붙은 것처럼 보이게 하는 gooey 효과이다.


  • 먼저 svgfilter를 추가하고, id="gooey"로 지정한다.
...
<svg>
  <filter id="gooey"></filter>  <!--here!!-->
</svg>

  • feGaussianBlur를 추가해 블러효과를 만든다.
  • 이 효과는 원본에 적용하며(SourceGraphic), 블러의 표준편차를 7로 지정한다.
  • 그 다음, 블러효과의 결과(result)를 blur변수에 저장한다.
...
<svg>
  <filter id="gooey">
    <feGaussianBlur in="SourceGraphic" stdDeviation="7" result="blur" /> <!--here!-->
  </filter>
</svg>

  • feColorMatrix를 사용해 대조효과를 준다.
  • 블러를 한 대상(blur)에 대조효과를 주면 달라붙어있는 모습을 만들 수 있다.
  • 대조효과까지 준 결과(result)를 contrast변수에 저장한다.
...
<svg>
  <filter id="gooey">
    <feGaussianBlur in="SourceGraphic" stdDeviation="7" result="blur" />
    <feColorMatrix in="blur" type="matrix" values="1 0 0 0 0
                    0 1 0 0 0
                    0 0 1 0 0
                    0 0 0 18 -7" result="contrast" />  <!--here!-->
  </filter>
</svg>

  • 마지막으로 원본(SourceGraphic)과 효과(contrast)를 혼합하기 위해 feBlend를 사용한다.
...
<svg>
  <filter id="gooey">
    <feGaussianBlur in="SourceGraphic" stdDeviation="7" result="blur" />
    <feColorMatrix in="blur" type="matrix" values="1 0 0 0 0
                    0 1 0 0 0
                    0 0 1 0 0
                    0 0 0 18 -7" result="contrast" />
    <feBlend in="SourceGraphic" in2="contrast" />     <!--here!-->
   </filter>
</svg>






2) js

js에서는 버튼을 클릭했을 때 메뉴들이 나타나는 기능을 만든다.
원리는 button태그에 click 이벤트가 발생시, buttonactive클래스를 추가하는 것이다. 한 번 코드를 살펴보자

const button = document.querySelector(".button");       //[1]

button.addEventListener("click", function() {           //[2]
  const isActive = this.classList.contains('active');   //[3]
  if (isActive) this.classList.remove('active');        //[4]
  else this.classList.add('active');                    //[5]
})

[1] document.querySelector를 사용해 class="button"인 요소를 가져온다.
[2] addEventListener를 사용해 button에 클릭 이벤트가 발생할 시 함수를 정의한다.
[3] classList.contains를 사용해 buttonactive클래스가 존재하는지 체크한다.
[4] 만약 active클래스가 있다면, classList.remove를 사용해 active 클래스를 제거한다.
[5] active클래스가 없다면, classList.addactive 클래스를 추가해준다.






3) scss

(1) 배경색 지정하기

body {
  background: #1c1c1c;   /*[1]*/
  padding: 2rem 2rem;
}

[1] body에 background 속성을 사용해 배경색을 지정해준다.



(2) wrap에 svg효과 적용하기

 .wrap {               /*[0]*/
  width: 30rem;        /*[1]*/
  height: 10rem;       /*[1]*/
  position: relative;  
}

[0] wrap은 버튼과 메뉴를 감싸는 부모 요소이다.
[1] 먼저 wrap에 position과 width, height를 지정해준다.


 .wrap {
  ...
  filter: url("#gooey");   /*[2]*/
}

[2] 그 다음 (html에서) svg로 생성한 gooey효과를 적용한다.
[2] 방식은 위와 같이 filter속성에 url("#filter의 id명")를 적용하면 된다.



(3) button, menu의 기본 디자인 설정하기

.button,
.menu {
  position: absolute; /*[1]*/
  top: 0;             /*[1]*/
  left: 0;            /*[1]*/
  border-radius: 50%; /*[2]*/
  cursor: pointer;
}

[1] button, menu의 position: absolute로 두어 겹칠 수 있게 한다.
[1] 그 다음 top, left에 각각 0을 주어 좌측 상단에 위치하게 한다.
[2] border-radius: 50%을 주어 button, menu를 원 모양으로 만든다.


.button,
.menu {
  ...
  display: flex;           /*[3]*/
  align-items: center;     /*[3] 수직중앙*/
  justify-content: center; /*[3] 수평중앙*/
}

[3] button, menu내부에 위치한 icon이 수직, 수평 중앙 정렬이 되도록 속성을 지정해준다.



(4) button에 디자인 적용하기

.button {        /*[1]*/
  width: 5.3rem;
  height: 5.3rem;
  color: #1e1e1e;
  font-weight: 700;
  font-size: 1.7rem;
  cursor: pointer;
  background: linear-gradient(145deg, #323232, #3c3c3c);
  box-shadow:  5px 5px 5px #272727,
             -5px -5px -5px #494949;
  ...
}

[1] button에 높이, 넓이, 배경색 등 기본적인 디자인을 적용한다.


.button {
  ...
  &.active {
    transform: rotate(90deg);   /*[2]*/
  }
}

[2] buttonactive클래스가 추가될 경우, rotate(90deg)를 주어 회전 효과를 준다.


.button {
  ...
  .icon {               /*[3]*/
    width: 2.4rem;
    height: 2.4rem;
  }
}

[3] button 내부에 위치한 icon의 높이, 넓이를 지정해준다.



(5) menu에 디자인 적용하기

.menu {
  width: 4.5rem;     /*[1]*/
  height: 4.5rem;    /*[1]*/
  z-index: -1;       /*[2]*/
  transform: translate(0, 0) scale(0);  /*[3]*/
  ...  
}

[1] menu의 높이, 넓이값을 설정한다.
[2] menu의 경우 button뒤에 숨겨져 있어야 하므로 z-index: -1을 지정한다.
[3] button을 클릭하기 전에는, menu들의 위치는 (0, 0), 크기는 0이어야 한다.


.menu {
  ...
  .icon {  /*[4]*/
    width: 1.8rem;
    height: 1.8rem;
  }
}

[4] menu 내부에 위치한 icon의 높이, 넓이를 지정해준다.



(6) menu별로 다른 색상 주기

$colors: #B980F0, #FE9898, #F5E79D, #E5FBB8; /*[1]*/

@for $i from 1 through length($colors) {     /*[2]*/
  .menu-#{$i} {                             
    background-color: nth($colors, $i);      /*[3]*/
  }
} 

[1] 총 4개의 menu가 있으므로 $variable을 선언하여 색상 list를 만든다.
[2] scss에서 반복문이 가능한데, 1 ~ $colors의 길이까지 반복한다.
[3] nth(list, $i)를 하면 리스트(list)의 $i번째 값에 접근가능하다.



(7) button 클릭시 menu나타나게 하기

js에서 확인했듯이 button을 클릭하면 buttonactive가 추가된다.
그렇다면 buttonactive가 추가된 상태에서 menu들의 상태를 정의해야한다.
우리는 menu들이 숨겨지 있다가 나타나기를 바란다.
scss의 for문을 이용하여 menu들을 나타내고 원하는 위치로 이동시켜보자!

@for $i from 1 through 4 {        /*[1]*/
  $positionX: 4.2 * $i;           /*[2]*/
  .button.active ~ .menu-#{$i} {  /*[3]*/
    transition-delay: $i * 0.1s;  /*[4]*/
    transform: translate($positionX + rem , 4rem) scale(1); /*[5]*/
  }
}

[1] menu의 개수는 4이므로 1~4까지 반복한다.
[2] 각 menu의 x축 위치가 4.2, 8.4, 12.6, 16.8이 되도록 변수(positionX)를 만든다.
[3] .A ~ .B는, A클래스 다음에 위치한 B클래스에 대해 css를 지정할 수 있다.
[4] transition-delay를 줘서 메뉴들이 순차적으로 나타나게 한다.
[5] translate를 사용해 위치를 변경하고, scale(1)로 메뉴를 나타나게 한다.

반응형

댓글