1. Comece com a marcação HTML
Para desenvolver este componente, vamos definir um nav
elemento que conterá o menu agrupado dentro de um container. Por padrão, daremos a active
class ao primeiro item da lista, mas podemos igualmente atribuir esta classe a qualquer um deles.
Aqui está a estrutura necessária:
1 |
|
2 |
|
3 |
|
4 |
|
5 |
href="">Home
|
6 |
|
7 |
|
8 |
href="">Information About Us
|
9 |
|
10 |
|
11 |
href="">Clients
|
12 |
|
13 |
|
14 |
href="">Contact
|
15 |
|
16 |
|
17 |
|
18 |
|
“Informações sobre nós” é um rótulo estranho para um item de menu, mas demonstra a mudança de largura do efeito.
2. Adicione o CSS
Felizmente, só precisaremos de alguns estilos.
Como dissemos, o menu ficará dentro de um container com largura máxima de 1000px.
Para criar o elemento com o fundo em movimento que atuará como um indicador de menu ativo, não usaremos nenhum elemento HTML extra. Em vez disso, definiremos o ::before
pseudo-elemento do menu e, em seguida, atualize seu transform
e width
valores dinamicamente por meio de JavaScript.
Aqui estão os estilos de menu que mais nos interessam:
1 |
/*CUSTOM VATIABLES HERE*/
|
2 |
|
3 |
.menu { |
4 |
list-style: none; |
5 |
position: relative; |
6 |
display: inline-flex; |
7 |
background: var(--pink); |
8 |
padding: 10px; |
9 |
border-radius: 15px; |
10 |
box-shadow: rgba(17, 12, 46, 0.15) 0px 48px 100px 0px; |
11 |
}
|
12 |
|
13 |
.menu::before { |
14 |
content: ""; |
15 |
position: absolute; |
16 |
top: 10px; |
17 |
left: 0; |
18 |
transform: translateX(var(--transformJS)); |
19 |
width: var(--widthJS); |
20 |
height: calc(100% - 20px); |
21 |
border-radius: var(--active-link-border-radius); |
22 |
background: var(--light-pink); |
23 |
box-shadow: var(--active-link-box-shadow); |
24 |
transition: all 0.3s linear; |
25 |
}
|
26 |
|
27 |
.menu li a { |
28 |
display: inline-block; |
29 |
position: relative; |
30 |
padding: 10px 20px; |
31 |
font-size: 20px; |
32 |
font-weight: 500; |
33 |
z-index: 1; |
34 |
}
|
35 |
|
36 |
.menu li:not(:last-child) { |
37 |
margin-right: 20px; |
38 |
}
|
39 |
|
40 |
@media (max-width: 800px) { |
41 |
.menu, |
42 |
.menu li { |
43 |
display: inline-block; |
44 |
}
|
45 |
|
46 |
.menu li.active a { |
47 |
background: var(--light-pink); |
48 |
border-radius: var(--active-link-border-radius); |
49 |
box-shadow: var(--active-link-box-shadow); |
50 |
}
|
51 |
|
52 |
.menu::before { |
53 |
display: none; |
54 |
}
|
55 |
}
|
3. Aplique o JavaScript
Agora a parte interessante.
vamos especificar o doCalculations()
função que receberá como parâmetro o item ativo e fará estas coisas:
- Calcule sua largura e deslocamento à esquerda em relação à lista pai.
- Atualize de acordo com o
transformJS
ewidthJS
Variáveis CSS que, por sua vez, definirão otransform
ewidth
valores do cardápio::before
pseudo-elemento.
Esta função será executada nos seguintes casos:
- Quando o DOM estiver pronto. Nesse caso, o item ativo será aquele com o
active
aula. Por padrão, este será o primeiro. - Cada vez que clicamos ou passamos o mouse sobre um link de menu.
- À medida que redimensionamos a janela do navegador. Isso é importante porque lembre-se de que em telas menores seguimos uma abordagem diferente.
Aqui está o código JavaScript necessário para a animação ao clicar:
1 |
const menu = document.querySelector(".menu"); |
2 |
const menuLinks = menu.querySelectorAll("a"); |
3 |
const menuLinkActive = menu.querySelector("li.active"); |
4 |
const activeClass = "active"; |
5 |
|
6 |
doCalculations(menuLinkActive); |
7 |
|
8 |
for (const menuLink of menuLinks) { |
9 |
menuLink.addEventListener("click", function (e) { |
10 |
e.preventDefault(); |
11 |
menu.querySelector("li.active").classList.remove(activeClass); |
12 |
menuLink.parentElement.classList.add(activeClass); |
13 |
doCalculations(menuLink); |
14 |
});
|
15 |
}
|
16 |
|
17 |
function doCalculations(link) { |
18 |
menu.style.setProperty("--transformJS", `${link.offsetLeft}px`); |
19 |
menu.style.setProperty("--widthJS", `${link.offsetWidth}px`); |
20 |
}
|
21 |
|
22 |
window.addEventListener("resize", function() { |
23 |
const menuLinkActive = menu.querySelector("li.active"); |
24 |
doCalculations(menuLinkActive); |
25 |
});
|
Para a animação ao passar o mouse, substituiremos o click
evento com o mouseenter
um assim:
1 |
...
|
2 |
|
3 |
menuLink.addEventListener("mouseenter", function () { |
4 |
document.querySelector(".menu li.active").classList.remove(activeClass); |
5 |
menuLink.parentElement.classList.add(activeClass); |
6 |
doCalculations(menuLink); |
7 |
});
|
Conclusão
Feito! De forma rápida, conseguimos desenvolver uma animação de menu de navegação prática que pode ocorrer tanto ao clicar quanto ao passar o mouse. Uma implementação mais avançada dele coloca menus suspensos em jogo e cria uma navegação de acompanhamento como no site do Stripe.
Além da animação em si, outra coisa a ter em mente é a forma como usamos variáveis CSS para atualizar os estilos de pseudo-elementos. Esta é uma ótima fonte de conhecimento nos casos em que você tem dores de cabeça ao manipular pseudo-elementos por meio do JavaScript.
Mais uma vez, vamos relembrar nossas demos.
A primeira demonstração:
E o segundo:
Como sempre, muito obrigado pela leitura!