웹솔루션개발 25년 노하우! 해피CGI의 모든것

[해피CGI][cgimall] 탭바 애니메이션 Jello Tab Bar (Animated) 본문

웹프로그램밍 자료실/기타 자료

[해피CGI][cgimall] 탭바 애니메이션 Jello Tab Bar (Animated)

해피CGI윤실장 2025. 4. 8. 09:29

 

클릭 시 다양한 효과가 적용되는 탭바 애니메이션입니다.
하이라이팅, 컬러 변경, 상황에 따라 버튼이 추가되기도 합니다.
응용하여 다양하게 활용 가능합니다.


HTML 구조

<nav class="amazing-tabs">

  <div class="filters-container">

    <div class="filters-wrapper">

      <ul class="filter-tabs">

        <li>

          <button class="filter-button filter-active" data-translate-value="0">

            New

          </button>

        </li>

        <li>

          <button class="filter-button" data-translate-value="100%">

            Popular

          </button>

        </li>

        <li>

          <button class="filter-button" data-translate-value="200%">

            Following

          </button>

        </li>

      </ul>

      <div class="filter-slider" aria-hidden="true">

        <div class="filter-slider-rect">&nbsp;</div>

      </div>

    </div>

  </div>

  <div class="main-tabs-container">

    <div class="main-tabs-wrapper">

      <ul class="main-tabs">

        <li>

          <button class="round-button" data-translate-value="0" data-color="red">

            <span class="avatar">

             

user avatar

            </span>

          </button>

.
.
.

</nav>

 



CSS 소스

@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");

 

*,

*::before,

*::after {

  margin: 0;

  padding: 0;

  box-sizing: border-box;

}

 

:root {

  --background-color: #bbdefb;

  --blue-50: #e3f2fd;

  --blue-100: #bbdefb;

  --blue-A700: #2962ff;

  --green-50: #e8f5e9;

  --green-100: #c8e6c9;

  --green-A700: #00c853;

  --purple-50: #f3e5f5;

  --purple-100: #e1bee7;

  --purple-A700: #aa00ff;

  --orange-50: #fff3e0;

  --orange-100: #ffe0b2;

  --orange-A700: #ff6d00;

  --orange-700: #f57c00;

  --grey-900: #212121;

  --white: #ffffff;

  --round-button-active-color: #212121;

  --translate-main-slider: 100%;

  --main-slider-color: #e3f2fd;

  --translate-filters-slider: 0;

  --filters-container-height: 3.8rem;

  --filters-wrapper-opacity: 1;

}

 

html {

  font-size: 62.5%;

}

 

html,

body {

  height: 100%;

}

 

body {

  display: flex;

  flex-direction: column;

  justify-content: center;

  align-items: center;

  transition: background-color 0.4s ease-in-out;

  background-color: var(--background-color);

}

 

button {

  border: none;

  cursor: pointer;

  background-color: transparent;

  outline: none;

}

 

nav.amazing-tabs {

  background-color: var(--white);

  border-radius: 2.5rem;

  user-select: none;

  padding-top: 1rem;

}

 

.main-tabs-container {

  padding: 0 1rem 1rem 1rem;

}

 

.main-tabs-wrapper {

  position: relative;

}

 

ul.main-tabs,

ul.filter-tabs {

  list-style-type: none;

  display: flex;

}

 

ul.main-tabs li {

  display: inline-flex;

  position: relative;

  padding: 1.5rem;

  z-index: 1;

}

 

.avatar,

.avatar img {

  height: 4rem;

  width: 4rem;

  border-radius: 50%;

  pointer-events: none;

}

 

.avatar img {

  object-fit: cover;

}

 

.
.
.

 

JS 소스

// resources in description

const mainTabs = document.querySelector(".main-tabs");

const mainSliderCircle = document.querySelector(".main-slider-circle");

const roundButtons = document.querySelectorAll(".round-button");

 

const colors = {

  blue: {

    50: {

      value: "#e3f2fd"

    },

    100: {

      value: "#bbdefb"

    }

  },

  green: {

    50: {

      value: "#e8f5e9"

    },

    100: {

      value: "#c8e6c9"

    }

  },

  purple: {

    50: {

      value: "#f3e5f5"

    },

    100: {

      value: "#e1bee7"

    }

  },

  orange: {

    50: {

      value: "#ffe0b2"

    },

    100: {

      value: "#ffe0b2"

    }

  },

  red: {

    50: {

      value: "#ffebee"

    },

    100: {

      value: "#ffcdd2"

    }

  }

};

 

const getColor = (color, variant) => {

  return colors[color][variant].value;

};

 

const handleActiveTab = (tabs, event, className) => {

  tabs.forEach((tab) => {

    tab.classList.remove(className);

  });

 

  if (!event.target.classList.contains(className)) {

    event.target.classList.add(className);

  }

};

 

mainTabs.addEventListener("click", (event) => {

  const root = document.documentElement;

  const targetColor = event.target.dataset.color;

  const targetTranslateValue = event.target.dataset.translateValue;

 

  if (event.target.classList.contains("round-button")) {

    mainSliderCircle.classList.remove("animate-jello");

    void mainSliderCircle.offsetWidth;

    mainSliderCircle.classList.add("animate-jello");

 

    root.style.setProperty("--translate-main-slider", targetTranslateValue);

    root.style.setProperty("--main-slider-color", getColor(targetColor, 50));

    root.style.setProperty("--background-color", getColor(targetColor, 100));

 

    handleActiveTab(roundButtons, event, "active");

 

    if (!event.target.classList.contains("gallery")) {

      root.style.setProperty("--filters-container-height", "0");

      root.style.setProperty("--filters-wrapper-opacity", "0");

    } else {

      root.style.setProperty("--filters-container-height", "3.8rem");

      root.style.setProperty("--filters-wrapper-opacity", "1");

    }

  }

});

 

const filterTabs = document.querySelector(".filter-tabs");

const filterButtons = document.querySelectorAll(".filter-button");

 

filterTabs.addEventListener("click", (event) => {

  const root = document.documentElement;

  const targetTranslateValue = event.target.dataset.translateValue;

 

  if (event.target.classList.contains("filter-button")) {

    root.style.setProperty("--translate-filters-slider", targetTranslateValue);

    handleActiveTab(filterButtons, event, "filter-active");

  }

});

 


해당 사이트로 이동해서 전체 소스를 확인하실 수 있습니다.

 

Comments