< Voltar

Como criar o efeito de texto do landonorris?

Inspirado no site do piloto Lando Norris, criei esse efeito de texto com animação ao hover que dá um efeito de profundidade muito interessante. landonorris.com

  1. E
  2. F
  3. E
  4. I
  5. T
  6. O
  1. I
  2. N
  3. S
  4. A
  5. N
  6. O

botao LandonNrris

A Abordagem Manual vs Escalável

Sempre que você encontrar um padrões repetitivos no código, o ideal é sempre iterar sobre...

Neste caso, transformar hardcode em funções reutilizáveis economiza tempo, reduz erros e torna o código muito mais fácil de dar manutenção.

Hard Codar (não recomendada)

Você poderia fazer isso manualmente, colocando cada span na mão:

<li> <div> <span style="--time:1">B</span> <span style="--time:2">R</span> <span style="--time:3">U</span> <span style="--time:4">N</span> <span style="--time:5">O</span> </div> <div> <span style="--time:1">B</span> <span style="--time:2">R</span> <span style="--time:3">U</span> <span style="--time:4">N</span> <span style="--time:5">O</span> </div> </li>

Mas imagine fazer isso para cada palavra? Seria insano em 2025!

Solução Escalável (recomendada)

Por isso criei duas funções JavaScript que automatizam todo o processo:

// Transforma texto em spans com delay incremental const getChar = t => t.split('').map((t,i) => `<span style="--time:${i+1}">${t}</span>` ).join(''); // Duplica o texto para criar o efeito de camadas const groupChars = t => `<div>${getChar(t)}</div><div>${getChar(t)}</div>`; // Aplica em todos os <li> automaticamente document.querySelectorAll('li').forEach(li => li.innerHTML = groupChars(li.innerText) );

Agora basta escrever o HTML simples:

<ul> <li>BRUNO</li> <li>FRANCISCO</li> <li>CARDOSO</li> </ul>

E o JavaScript cuida do resto! E com o avanço dos browsers não será mais necessário adicionar a variavel --time nos spans programaticamente, a nova função do css sibling-index() (disponível apenas nos chrome/edge) retornará o indice de cada span e podemos usar como tempo de transição.

li { transition:calc(sibling-index() * .1s) ease-in-out all; }

Como Funciona

1. A Estrutura HTML

<ul class="text-3xl font-bold flex flex-col gap-2"> <li>BRUNO</li> <li>FRANCISCO</li> </ul>

2. O CSS com Custom Properties

li { position: relative; overflow: hidden; max-height: 2rem; /* 32px, altura de uma linha */ } /* Segunda camada fica desbotada */ li div:nth-child(2) span { opacity: 0.3; } /* Cada span tem transição baseada em sua posição */ li span { transition: calc(var(--time) * 0.1s) ease-in-out; display: inline-block; } /* Animação no hover */ li:hover span { translate: 0 -100%; opacity: 1 !important; }

3. O JavaScript que Monta Tudo

A mágica acontece em 3 etapas:

Etapa 1: getChar() divide o texto e adiciona custom property

"BRUNO"'<span style="--time:1">B</span>' + '<span style="--time:2">R</span>' + '<span style="--time:3">U</span>' + '<span style="--time:4">N</span>' + '<span style="--time:5">O</span>'

Etapa 2: groupChars() duplica em duas camadas

'<div>BRUNO</div><div>BRUNO</div>'

Etapa 3: Aplica em todos os <li>

document.querySelectorAll('li').forEach(li => li.innerHTML = groupChars(li.innerText) );

Customizações Possíveis

Ajustar velocidade da animação:

/* Mais rápido */ transition: calc(var(--time) * 0.05s) ease-in-out; /* Mais lento */ transition: calc(var(--time) * 0.2s) ease-in-out;

Mudar direção:

/* Para cima (padrão) */ translate: 0 -100%; /* Para baixo */ translate: 0 100%; /* Para direita */ translate: 100% 0;

Adicionar bounce:

transition: calc(var(--time) * 0.1s) cubic-bezier(0.68, -0.55, 0.265, 1.55);

Resultado Final

Com apenas 3 linhas de JavaScript, criamos um efeito profissional e totalmente escalável!

HTML simples:

<li>BRUNO</li>

Vira automaticamente:

<li> <div> <span style="--time:1">B</span> <span style="--time:2">R</span> <span style="--time:3">U</span> <span style="--time:4">N</span> <span style="--time:5">O</span> </div> <div> <span style="--time:1">B</span> <span style="--time:2">R</span> <span style="--time:3">U</span> <span style="--time:4">N</span> <span style="--time:5">O</span> </div> </li>