Pular para o conteúdo principal

DO IT parte 3: JS

· Leitura de 5 minutos

Na terceira parte, implemento a interação com JS.


Seções do JS

Separei o arquivo JS em seis seções:

  1. State: objeto que guarda as posições do cursor
  2. Elements: elementos HTML que serão manipulados
  3. Functions: funções usadas para manipular os elementos HTML e lidar com as posições do cursor
  4. Rendering: função que renderiza o texto tridimensional
  5. Handlers: funções que lidam com eventos do window
  6. Events: define funções de callback para os eventos do window

State

Objeto usado para guardar a posição atual e a posição alvo do cursor. A posição alvo é a posição real do cursor, enquanto a posição atual é a posição com inércia. Isso cria uma movimentação suave ao mover o cursor. A inércia é calculada pela função getPositionWithInertia.

index.js
/* ========================================   State======================================== */const state = {  current: { x: 0, y: 0 },  target: { x: 0, y: 0 },};

Elements

O elemento container tem a propriedade transform manipulada, enquanto o elemento text tem a propriedade text-shadow manipulada.

index.js
/* ========================================   Elements======================================== */const container = document.querySelector(".container");const text = document.querySelector(".text");

Functions

getTextShadowValue

A função getTextShadowValue gera o valor para a propriedade text-shadow do elemento div.text, o valor é baseado na posição do cursor.

A função recebe os parâmetros xPercentage e yPercentage, onde xPercentage varia de -100% (esquerda) a 100% (direita), e yPercentage varia de -100% (fundo) a 100% (topo).

A porcentagem diz respeito à posição do cursor. Por exemplo, se o cursor está no centro do viewport, ambas as porcentagens são 0%. Se o cursor está no topo esquerdo do viewport, xPercentage é -100% e yPercentage é 100%.

A função cria 50 sombras, onde a última sombra se posiciona no máximo até 2vw (2 unidades de largura do viewport).

getTransformValue

A função getTransformValue gera o valor para a propriedade transform do elemento div.container, o valor é baseado na posição do cursor.

Assim como a função getTextShadowValue, a função getTransformValue recebe os parâmetros xPercentage e yPercentage.

A função cria uma rotação de no máximo 20 graus.

getPositionWithInertia

A função getPositionWithInertia gera o valor para state.current.x e state.current.y, recebendo os parâmetros current e target.

A função calcula a distância entre os dois pontos. Se a distância for muito pequena, a posição alvo é retornada. Do contrário, 10% da distância é somada à posição atual, criando uma movimentação suave.

updateTarget

A função updateTarget mapeia a posição do cursor para state.target.x e state.target.y. A função é usada no evento mousemove do window.

index.js
/* ========================================   Functions======================================== */function getTextShadowValue(xPercentage, yPercentage) {  const shadowAmount = 50;  const maxVw = 2;  let result = "";  for (let index = 0; index < shadowAmount; index++) {    const isLast = index === shadowAmount - 1;    const percentage = (index + 1) / shadowAmount;    const xValue = maxVw * percentage * (xPercentage * -1);    const yValue = maxVw * percentage * yPercentage;    result +=      `${xValue}vw ` +      `${yValue}vw ` +      `var(--foreground-color)${isLast ? "" : ","}`;  }  return result;}function getTransformValue(xPercentage, yPercentage) {  const maxDegrees = 20;  const rotateXValue = yPercentage * maxDegrees;  const rotateYValue = xPercentage * maxDegrees;  return `rotateX(${rotateXValue}deg) rotateY(${rotateYValue}deg)`;}function getPositionWithInertia(current, target) {  const distance = target - current;  if (Math.abs(distance) < 0.01) {    return target;  }  return current + distance * 0.1;}function updateTarget(event) {  state.target.x = event.x;  state.target.y = event.y;}

Rendering

A função render é executada em todo quadro de animação usando requestAnimationFrame, que sincroniza a renderização com a taxa de atualização da tela. Sendo os destaques:

  • Linha 6: a função faz uma nova renderização apenas quando a posição atual do cursor é diferente da posição alvo
  • Linhas 10, 11, 22 e 23: usa as funções getPositionWithInertia, getTransformValue e getTextShadowValue para gerar os novos valores das propriedades dos elementos div.container e div.text
  • Linhas 26 e 27: atualiza a posição atual do cursor
  • Linha 31: solicita um quadro de animação chamando a si mesma, criando um loop infinito de renderização
index.js
/* ========================================   Rendering======================================== */function render() {  if (    state.current.x !== state.target.x ||    state.current.y !== state.target.y  ) {    const x = getPositionWithInertia(state.current.x, state.target.x);    const y = getPositionWithInertia(state.current.y, state.target.y);    const halfWidth = window.innerWidth * 0.5;    const halfHeight = window.innerHeight * 0.5;    // left is negative, right is posivite    const xPercentage = (x - halfWidth) / halfWidth;    // bottom is negative, top is posivite    const yPercentage = ((y - halfHeight) / halfHeight) * -1;    // update dom    container.style.transform = getTransformValue(xPercentage, yPercentage);    text.style.textShadow = getTextShadowValue(xPercentage, yPercentage);    // update state    state.current.x = x;    state.current.y = y;  }  // render loop  requestAnimationFrame(render);}

Atualização apenas da propriedade transform



Atualização apenas da propriedade text-shadow



Atualização das propriedades transform e text-shadow juntas

Handlers e Events

Nesta seção os callbacks para os eventos mousemove e load do window são definidos. No evento mousemove, a posição alvo do cursor é atualizada, e no evento load, a renderização é iniciada.

index.js
/* ========================================   Handlers======================================== */function handleMouseMove(event) {  updateTarget(event);}function handleLoad(event) {  render();}/* ========================================   Events======================================== */window.addEventListener("mousemove", handleMouseMove);window.addEventListener("load", handleLoad);

Inércia: comparação

A inércia traz suavidade ao movimento.

Sem inércia



Com inércia

Conclusão

Tendo o JS definido, dei o projeto como concluído.

Na conclusão, escrevo meus pensamentos finais: Conclusão DO IT: texto 3D com CSS e JS.

Leitura recomendada