Headers
Tesla Menu
30 Sep 2022
Tesla Menu - це інтерактивне меню, яке використовується на сайті компанія "Тесла". Анімації реалізована за допомогою JavaScript, який змушує UI елемент слідкувати за курсором.
HTML
SCSS
JS
<nav class="navbar">
<span></span>
<a class="menu-link" href="#">About Us</a>
<a class="menu-link" href="#">Candidates</a>
<a class="menu-link" href="#">Clients</a>
<a class="menu-link" href="#">Insights</a>
<a class="menu-link" href="#">Contact Us</a>
</nav>
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
max-width: 800px;
margin: 100px auto;
span {
position: absolute;
background: rgba(38, 80, 188, 0.6);
-webkit-border-radius: 25px;
-moz-border-radius: 25px;
border-radius: 25px;
height: 90%;
z-index: -1;
opacity: 0;
left: var(--left-position-span);
width: var(--width-span);
transition: var(--span-transition);
&.active {
opacity: 1;
}
}
.menu-link {
padding: 10px;
}
}
// Tesla style menu
const navbar = document.getElementsByClassName(`navbar`)[0];
if (navbar) {
const navbarElements = navbar.querySelectorAll(`a`);
const spanElement = navbar.querySelector(`span`);
const activeMenuElement = navbar.querySelector(`a.active`);
function backgroundMenuPositionFunc(targetElement, flagMouseEnter) {
const navbarPosition = navbar.getBoundingClientRect();
const elementPosition = targetElement.getBoundingClientRect();
let spanPositionLeftStart = elementPosition.left - navbarPosition.left;
let spanWidthStart = elementPosition.width;
if (flagMouseEnter) {
spanElement.style.setProperty(
"--span-transition",
`0.5s cubic-bezier(0.75, 0, 0, 1)`
);
} else {
spanElement.style.setProperty(
"--span-transition",
`opacity 0.5s ease, visibility 0s 0s`
);
}
spanElement.style.setProperty("--width-span", `${spanWidthStart}px`);
spanElement.style.setProperty(
"--left-position-span",
`${spanPositionLeftStart}px`
);
}
if (activeMenuElement) {
backgroundMenuPositionFunc(activeMenuElement, true);
spanElement.classList.add("active");
navbarElements.forEach((elem) => {
elem.addEventListener("mouseenter", function (e) {
backgroundMenuPositionFunc(e.target, true);
});
navbar.addEventListener("mouseleave", function (e) {
backgroundMenuPositionFunc(activeMenuElement, true);
});
});
} else {
let flagMouseEnter = false;
navbarElements.forEach((elem) => {
elem.addEventListener("mouseenter", function (e) {
backgroundMenuPositionFunc(e.target, flagMouseEnter);
spanElement.classList.add("active");
flagMouseEnter = true;
});
});
navbar.addEventListener("mouseleave", function (e) {
spanElement.classList.remove("active");
flagMouseEnter = false;
spanElement.style.setProperty(
"--span-transition",
`opacity 0.5s ease, visibility 0s 0.5s`
);
});
}
}
PostCSS Code:
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
max-width: 800px;
margin: 100px auto;
span {
position: absolute;
background: rgba(38, 80, 188, 0.6);
-webkit-border-radius: 25px;
-moz-border-radius: 25px;
border-radius: 25px;
height: 90%;
z-index: -1;
opacity: 0;
left: var(--left-position-span);
width: var(--width-span);
transition: var(--span-transition);
&.active {
opacity: 1;
}
}
.menu-link {
padding: 10px;
}
}