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