Tabs

Circle Animated Tabs on scroll

19 Sep 2022

Circle Animated Tabs on scroll
HTML
SCSS
PostCSS
JS
                            <section class="circle-section">
      <div id="trigger1" class="spacer s0"></div>

      <div class="cont circle-section__container">
        <div class="circle-section__item">
          <div class="circle-block">
            <div class="circle-block__circle active rotateMinus">
              <div class="circle-block__title">
                <p>Lorem Ipsum</p>
              </div>
            </div>
            <div class="circle-block__circle rotateMinus">
              <div class="circle-block__title">
                <p>Lorem Ipsum</p>
              </div>
            </div>
            <div class="circle-block__circle rotateMinus">
              <div class="circle-block__title">
                <p>Lorem Ipsum</p>
              </div>
            </div>
            <div class="circle-block__circle rotateMinus">
              <div class="circle-block__title">
                <p>Lorem Ipsum</p>
              </div>
            </div>
            <div class="circle-block__circle rotateMinus">
              <div class="circle-block__title">
                <p>Lorem Ipsum</p>
              </div>
            </div>
            <div class="circle-block__circle rotateMinus">
              <div class="circle-block__title">
                <p>Lorem Ipsum</p>
              </div>
            </div>

            <div class="circle-block__container">
              <div class="circle-block__item active">
                <h4>Ipsum</h4>
                <ul>
                  <li>
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit
                  </li>
                  <li>
                    sed do eiusmod tempor incididunt ut labore et dolore magna
                    aliqua.
                  </li>
                  <li>In fermentum posuere urna nec.</li>
                </ul>
              </div>
              <div class="circle-block__item">
                <h4>Lorem</h4>
                <ul>
                  <li>
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit
                  </li>
                  <li>
                    sed do eiusmod tempor incididunt ut labore et dolore magna
                    aliqua.
                  </li>
                  <li>In fermentum posuere urna nec.</li>
                </ul>
              </div>
              <div class="circle-block__item">
                <h4>Ipsum</h4>
                <ul>
                  <li>
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit
                  </li>
                  <li>
                    sed do eiusmod tempor incididunt ut labore et dolore magna
                    aliqua.
                  </li>
                  <li>In fermentum posuere urna nec.</li>
                </ul>
              </div>
              <div class="circle-block__item">
                <h4>Lorem</h4>
                <ul>
                  <li>
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit
                  </li>
                  <li>
                    sed do eiusmod tempor incididunt ut labore et dolore magna
                    aliqua.
                  </li>
                  <li>In fermentum posuere urna nec.</li>
                </ul>
              </div>
              <div class="circle-block__item">
                <h4>Ipsum</h4>
                <ul>
                  <li>
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit
                  </li>
                  <li>
                    sed do eiusmod tempor incididunt ut labore et dolore magna
                    aliqua.
                  </li>
                  <li>In fermentum posuere urna nec.</li>
                </ul>
              </div>
              <div class="circle-block__item">
                <h4>Lorem</h4>
                <ul>
                  <li>
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit
                  </li>
                  <li>
                    sed do eiusmod tempor incididunt ut labore et dolore magna
                    aliqua.
                  </li>
                  <li>In fermentum posuere urna nec.</li>
                </ul>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
                        
                            /* Variables */
:root {
  --black: #161626;
  --white: #ffffff;
  --primary: #ffde33;
  --secondary: #088ded;
  --font-main: "Montserrat", sans-serif;
}

.circle-section {
  height: ac(2800px, 2300px);
  width: 100%;
  margin-top: 138px;
  margin-bottom: 50px;
  display: flex;
  flex-direction: column;

  &__container {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    height: 100%;
    width: 100%;
  }

  &__item {
    display: flex;
    pointer-events: none;
  }

  .circle-block {
    position: relative;
    z-index: 5;
    margin: 100px auto 0;
    flex-shrink: 0;
    width: 50vw;
    height: 50vw;
    max-width: 1000px;
    max-height: 1000px;
    min-width: 420px;
    min-height: 420px;
    border: 5px solid #ed145b;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;

    @include media(769) {
      margin-left: -95px;
    }

    @include media(360) {
      min-width: 380px;
      min-height: 380px;
    }

    &__circle {
      position: absolute;
      background: transparent;
      width: 100%;
      height: 100%;
      transform-origin: center;
      transform: rotate(0deg);
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
      font-weight: 700;
      cursor: pointer;

      &.active {
        .circle-block__title {
          background: var(--secondary);
        }
      }
    }

    &__title {
      background: var(--primary);
      border: ac(4px, 2px) solid var(--white);
      border-radius: 50%;
      width: 13vw;
      height: 13vw;
      max-width: 250px;
      max-height: 250px;
      min-width: 120px;
      min-height: 120px;
      position: absolute;
      top: 0;
      transform: translateY(-50%);
      transform-origin: top;
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
      padding: 10px;

      & > * {
        color: var(--black);
        padding: 0;
        font-weight: 600;
        font-family: var(--font-main);
        font-size: ac(26px, 14px);
      }
    }

    &__container {
      position: relative;
      width: 20.83vw;
      height: 20.83vw;
      max-width: 400px;
      max-height: 400px;
      min-width: 180px;
      min-height: 220px;
      z-index: 9;

      @include media(360) {
        min-width: 160px;
        margin-left: 5px;
      }
    }

    &__item {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      width: 100%;
      height: 100%;
      opacity: 0;
      transition: all 0.3s ease;
      font-family: var(--font-main);
      font-size: ac(20px, 14px);
      font-weight: 400;
      font-style: normal;
      line-height: 130%;

      h4 {
        font-size: ac(24px, 16px);
      }

      ul {
        list-style: none;

        li {
          padding-left: 0;
          position: relative;
          font-size: inherit;
          font-weight: inherit;
          line-height: inherit;
          letter-spacing: inherit;
          margin-bottom: ac(15px, 5px);
        }
      }

      @include media(360) {
        font-size: 12px;

        h4 {
          font-size: 14px;
        }
      }

      &.active {
        opacity: 1;
        transition: all 0.3s ease;
      }
    }
  }
}

.spacer {
  width: 100%;
  border: 1px dashed transparent;
  transform: translateY(380px);
}
                        
                            /* Mixin */
@define-mixin media $width {
  @media only screen and (max-width: $(width)px) {
    @mixin-content;
  }
}

/* Variables */
:root {
  --black: #161626;
  --white: #ffffff;
  --primary: #ffde33;
  --secondary: #088ded;
  --font-main: "Montserrat", sans-serif;
}


.circle-section {
  height: ac(2800px, 2300px);
  width: 100%;
  margin-top: 138px;
  margin-bottom: 50px;
  display: flex;
  flex-direction: column;

  &__container {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    height: 100%;
    width: 100%;
  }

  &__item {
    display: flex;
    pointer-events: none;
  }

  .circle-block {
    position: relative;
    z-index: 5;
    margin: 100px auto 0;
    flex-shrink: 0;
    width: 50vw;
    height: 50vw;
    max-width: 1000px;
    max-height: 1000px;
    min-width: 420px;
    min-height: 420px;
    border: 5px solid #ed145b;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;

    @mixin media 769 {
      margin-left: -95px;
    }

    @mixin media 360 {
      min-width: 380px;
      min-height: 380px;
    }

    &__circle {
      position: absolute;
      background: transparent;
      width: 100%;
      height: 100%;
      transform-origin: center;
      transform: rotate(0deg);
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
      font-weight: 700;
      cursor: pointer;

      &.active {
        .circle-block__title {
          background: var(--secondary);
        }
      }
    }

    &__title {
      background: var(--primary);
      border: ac(4px, 2px) solid var(--white);
      border-radius: 50%;
      width: 13vw;
      height: 13vw;
      max-width: 250px;
      max-height: 250px;
      min-width: 120px;
      min-height: 120px;
      position: absolute;
      top: 0;
      transform: translateY(-50%);
      transform-origin: top;
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
      padding: 10px;

      & > * {
        color: var(--black);
        padding: 0;
        font-weight: 600;
        font-family: var(--font-main);
        font-size: min(max(14px, 1.35vw), 26px);
      }
    }

    &__container {
      position: relative;
      width: 20.83vw;
      height: 20.83vw;
      max-width: 400px;
      max-height: 400px;
      min-width: 180px;
      min-height: 220px;
      z-index: 9;

      @mixin media 360 {
        min-width: 160px;
        margin-left: 5px;
      }
    }

    &__item {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      width: 100%;
      height: 100%;
      opacity: 0;
      transition: all 0.3s ease;
      font-family: var(--font-main);
      font-size: min(max(14px, 1.05vw), 20px);
      font-weight: 400;
      font-style: normal;
      line-height: 130%;

      h4 {
        font-size: min(max(16px, 1.25vw), 24px);
      }

      ul {
        list-style: none;

        li {
          padding-left: 0;
          position: relative;
          font-size: inherit;
          font-weight: inherit;
          line-height: inherit;
          letter-spacing: inherit;
          margin-bottom: ac(15px, 5px);
        }
      }

      @mixin media 360 {
        font-size: 12px;

        h4 {
          font-size: 14px;
        }
      }

      &.active {
        opacity: 1;
        transition: all 0.3s ease;
      }
    }
  }
}

.spacer {
  width: 100%;
  border: 1px dashed transparent;
  transform: translateY(380px);
}

                        
                            function rotateCircleOnScroll() {
    if (document.querySelector(".circle-section")) {
      const controller = new ScrollMagic.Controller();
      const scrollLength = 1720;
      let FLAG_FOR_TOGGLE_TABS = false;

      const ourScene = new ScrollMagic.Scene({
        triggerElement: "#trigger1",
        duration: scrollLength,
      })
        .setClassToggle(".circle-section__item", "sticky")
        .setPin(".circle-section__item")
        // .addIndicators()
        .addTo(controller);

      let classFlag = false;

      const observer = new MutationObserver(function (mutations) {
        mutations
          .filter(function (mutation) {
            return (
              mutation.type === "attributes" &&
              mutation.attributeName === "class"
            );
          })
          .forEach(function (mutation) {
            if ($(mutation.target).is(".sticky")) {
              classFlag = true;
            } else {
              classFlag = false;
            }
          });
      });

      observer.observe($(".circle-section__item").get(0), {
        attributes: true,
      });

      function getCoords(elem) {
        // crossbrowser version
        var box = elem.getBoundingClientRect();

        var body = document.body;
        var docEl = document.documentElement;

        var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
        var scrollLeft =
          window.pageXOffset || docEl.scrollLeft || body.scrollLeft;

        var clientTop = docEl.clientTop || body.clientTop || 0;
        var clientLeft = docEl.clientLeft || body.clientLeft || 0;

        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;

        return { top: Math.round(top), left: Math.round(left) };
      }

      function isInViewport(el) {
        const rect = el.getBoundingClientRect();
        return (
          rect.top >= 0 &&
          rect.left >= 0 &&
          rect.bottom <=
            (window.innerHeight || document.documentElement.clientHeight) &&
          rect.right <=
            (window.innerWidth || document.documentElement.clientWidth)
        );
      }

      (function () {
        var throttle = function (type, name, obj) {
          var obj = obj || window;
          var running = false;
          var func = function () {
            if (running) {
              return;
            }
            running = true;
            requestAnimationFrame(function () {
              obj.dispatchEvent(new CustomEvent(name));
              running = false;
            });
          };
          obj.addEventListener(type, func);
        };
        throttle("scroll", "optimizedScroll");
      })();

      const $scrollableElement = document.querySelector(
        ".circle-section__container"
      );

      let circle = document.querySelector(".circle-block"),
        rotateMinus = document.querySelectorAll(".rotateMinus");
      const section = document.querySelector(".circle-section");
      const sectionCoords = getCoords(section).top;
      let circleItemsArr = document.querySelectorAll(`.circle-block__item`);

      let defaultRotate = 0; // in deg default rotate

      let circleRotateFlag = 0;
      let countToggle = 0;
      let lastScrollTop = 0;
      let rotateValDefault = defaultRotate;
      let activeOne = true;
      const step = 360 / rotateMinus.length;

      rotateMinus.forEach((el, i) => {
        const rotateVal = (360 / rotateMinus.length) * i + defaultRotate;
        el.style.transform = `rotate(${rotateVal}deg)`;
        el.children[0].style.transform = `rotate(${Math.min(
          -rotateVal,
          -defaultRotate
        )}deg) translateY(-50%)`;
        window.addEventListener("optimizedScroll", () => {
          if (window.pageYOffset >= sectionCoords) {
            scrollAnimate(el, i);
          }
        });
      });

      window.addEventListener("optimizedScroll", function (e) {
        const st = window.pageYOffset || document.documentElement.scrollTop;
        if (classFlag) {
          const circleAim = rotateMinus[0];
          const rotateVal = Number(
            circleAim.style.transform.replace(/[^0-9.]/g, "")
          );

          if (st > lastScrollTop) {
            // downScroll code
            if (rotateVal >= rotateValDefault + step) {
              const activeIndex =
                ((rotateValDefault - defaultRotate) / step) %
                (rotateMinus.length - 1);
              toggleTab(activeIndex);
              rotateValDefault += step;
            }
          } else if (st < lastScrollTop) {
            // upScroll code
            if (rotateVal <= rotateValDefault - step) {
              rotateValDefault -= step;
              const activeIndex =
                ((rotateValDefault - defaultRotate) / step - 1) %
                (rotateMinus.length - 1);
              toggleTab(activeIndex);
            }
          }
        } else {
          if (
            st < lastScrollTop &&
            window.pageYOffset < sectionCoords + 150 &&
            !activeOne
          ) {
            toggleTab(circleItemsArr.length - 1);
            activeOne = true;
            rotateValDefault -= step;
            rotateMinus.forEach((el, i) => {
              const rotateVal = (360 / rotateMinus.length) * i + defaultRotate;
              el.style.transform = `rotate(${rotateVal}deg)`;
              el.children[0].style.transform = `rotate(${Math.min(
                -rotateVal,
                -defaultRotate
              )}deg) translateY(-50%)`;
            });
          }
        }
        lastScrollTop = st <= 0 ? 0 : st;
      });
      function toggleTab(index) {
        document
          .querySelector(`.circle-block__circle.active`)
          .classList.remove("active");
        document
          .querySelector(`.circle-block__item.active`)
          .classList.remove("active");
        let activeIndex =
          (circleItemsArr.length - index - 1) % circleItemsArr.length;
        if (activeIndex !== 0) activeOne = false;
        rotateMinus[activeIndex].classList.add("active");
        circleItemsArr[activeIndex].classList.add("active");
      }
      function rotateCircle(el, rotateVal, defaultTransition) {
        el.style.transform = `rotate(${Math.max(
          rotateVal + defaultTransition,
          defaultTransition
        )}deg)`;
        el.children[0].style.transform = `rotate(${Math.min(
          -rotateVal - defaultTransition,
          -defaultTransition
        )}deg) translateY(-50%)`;
      }
      function scrollAnimate(el, index) {
        if (classFlag === true) {
          const step = 360 / rotateMinus.length;
          const rotateValDefault = step * index + defaultRotate;
          const rotateVal = (window.pageYOffset - sectionCoords) / 5;
          rotateCircle(el, rotateVal, rotateValDefault);
        }
      }
    }
  }
  rotateCircleOnScroll();

// Adaptive calculation for pcss
const maxWidth = 1440;
const minWidth = 768;

export function ac( startSize, endSize, minBreakpoint = minWidth, maxBreakpoint = maxWidth) {

const startSizeFormatted = startSize.replace("px", "");
const endSizeFormatted = endSize.replace("px", "");

const difference = startSizeFormatted - endSizeFormatted;

    if (difference > 0) {
        return `min(max(calc(${endSize} + ${difference} * ((100vw - ${minBreakpoint}px) / ${maxBreakpoint - 
        minBreakpoint})),${endSize}),${startSize})`;
    } else {
        return `min(max(calc(${endSize} + ${difference} * ((100vw - ${minBreakpoint}px) / ${maxBreakpoint - 
        minBreakpoint})),${startSize}),${endSize})`;
    }
}
                        

In order to get this tabs in proper way, we use popular library Scroll Magic

Use this files below

plugins.zip

0