Com que frequência você acessa 404 páginas? Geralmente, eles não têm estilo e permanecem padrão. Recentemente, encontrei
test.do.am, que personagem interativo atrai a atenção e anima a página de erro.
Provavelmente, havia apenas uma foto de gato, então eles pensaram que o movimento dos olhos e o desenvolvedor implementaram a idéia.

Agora, o usuário visita a página e verifica o efeito. É um recurso pequeno e agradável, que captura, e o usuário o discute com colegas ou amigos e até repete o recurso. Poderia ser assim tão fácil, se não:
- O ponto central não está sendo renovado quando o usuário redimensiona a janela. Abra a janela do navegador com uma pequena janela de visualização de largura e redimensione para tela cheia, o gato não olha para o cursor.
- O ponto central é colocado no olho esquerdo, não no centro binocular do círculo.
- Quando o usuário passa o cursor entre os olhos, as maçãs dos olhos não se juntam e não se concentram. Os olhos estão olhando para o infinito, é por isso que o gato não olha para o usuário, olha através dele.
- Os movimentos dos olhos são imediatos, eles precisam ser suaves.
- Os movimentos das maçãs acontecem por causa da alteração da margem esquerda / margem superior. Está incorreto, encontre a explicação abaixo.
- Os olhos não se movem se o cursor estiver no rodapé.
O que eu sugiroPara começar, vamos implementar o movimento impecável dos olhos.
1. Prepare a marcação
<div class="cat"> <div class="cat__eye _left"></div> <div class="cat__eye _right"></div> </div>
2. Obtenha links para os elementos dos olhos
const cat = document.querySelector('.cat'); const eyes = cat.querySelectorAll('.cat__eye'); const eye_left = eyes[0]; const eye_right = eyes[1];
3. Registre o ouvinte de eventos do mousemove e obtenha as coordenadas do cursor:
let mouseX; let mouseY; window.addEventListener('mousemove', e => { mouseX = e.clientX; mouseY = e.clientY; })
Eu adiciono o ouvinte mousemove no objeto da janela, não no corpo do documento, porque preciso usar toda a tela para obter as coordenadas do mouse.
4. Movimento
Como vou suavizar os movimentos, não posso gerenciá-los no manipulador de remoção de rato.
Adicione o método de atualização que será buscado pelo requestAnimationFrame que é sincronizado com a renovação do navegador. Normalmente, as renovações acontecem 60 vezes por segundo; portanto, vemos 60 fotos por segundo a cada 16,6 ms.
Se o desenvolvedor supor que o navegador do usuário não pode suportar requestAnimationFrame, o desenvolvedor poderá usar o fallback setTimeout ou o
polyfill pronto
window.requestAnimationFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })();
Para renovar ou buscar a atualização de forma estável a tempo, registro a variável iniciada
let started = false; let mouseX; let mouseY; window.addEventListener('mousemove', e => { mouseX = e.clientX; mouseY = e.clientY; if(!started){ started = true; update(); } }) function update(){
Dessa forma, eu constantemente buscava o método de atualização e as coordenadas do cursor. Então eu preciso obter valores dos movimentos das maçãs dentro dos olhos.
Eu tento mover os dois olhos como elemento único
let dx = mouseX - eyesCenterX; let dy = mouseY - eyesCenterY; let angle = Math.atan2(dy, dx); let distance = Math.sqrt(dx * dx + dy * dy); distance = distance > EYES_RADIUS ? EYES_RADIUS : distance; let x = Math.cos(angle) * distance; let y = Math.sin(angle) * distance; eye_left.style.transform = 'translate(' + x + 'px,' + y + 'px)'; eye_right.style.transform = 'translate(' + x + 'px,' + y + 'px)';
Bem simples: encontre dx e dy, que são a diferença de coordenadas entre o centro dos olhos e o mouse, encontre o ângulo do centro para o cursor, usando os métodos Math.cos e Math.sin para obter o valor do movimento para horizontal e vertical. Use o
operador ternário e limite a área de movimento dos olhos.
O valor Y é fornecido primeiro para o método Math.atan2, depois o valor x. Como resultado, o usuário percebe falta de naturalidade dos movimentos dos olhos e não focaliza.
Faça com que cada um se mova e observe sem referência um ao outro.
Interessante, mas pior que o resultado anterior, os olhos se movem para cima e para baixo de forma independente. Então, eu usei a primeira demo como mecânica básica de movimento e fiz com que as maçãs dos olhos se juntassem quando o cursor estivesse sobre o centro do personagem.
Não vou descrever o código inteiro, encontre um resultado:
Por tentativa e erro, combinei os parâmetros necessários para o movimento e o foco dos olhos. Então agora eu preciso de suavização.
SuavizaçãoVincular a
biblioteca TweenMax e codificar algo como isto?
TweenMax.to( eye, 0.15, {x: x, y: y});
Vincular toda a biblioteca para tarefas simples não faz sentido; portanto, eu faço a suavização do zero.
Coloque o caso de que há apenas um elemento de olho na página e sua área de deslocamento não é limitada. Para suavizar os valores das coordenadas do mouse, eu uso esta mecânica:
const SMOOTHING = 10; x += (needX - x) / SMOOTHING; y += (needY - y) / SMOOTHING; eye.style.transform = 'translate3d(' + x + 'px,' + y + 'px,0)';
Eu uso o translate3d para separar os olhos para outro fluxo de renderização e acelerá-los.
O truque é que cada 16,6ms (60 fotos por segundo) variável x e y tendem aos valores necessários. Cada renovação fecha o valor ao necessário por 1/10 da diferença.
let x = 0; let needX = 100; let SMOOTHING = 2; function update(){ x += (needX - x) / SMOOTHING; console.log(x); }
A cada 16,6 ms de renovação, obtemos uma suavização simples e os próximos valores de x (aprox.):
50 75 87.5 93.75 96.875 98.4375 99.21875 99.609375 100
Mais alguns truques não óbvios:
- Inicie este exame para otimizar a carga de trabalho
if(x != needX || y != needY){ eye.style.transform = 'translate3d(' + x + 'px,' + y + 'px,0)'; }
Mas você precisa equiparar x a needX quando chegarem tão perto quanto as posições dos olhos forem quase as mesmas
if(Math.abs(x - needX) < 0.25){ x = needX; } if(Math.abs(y - needY) < 0.25){ y = needY; }
Caso contrário, os valores xey atingirão needX e needY por muito tempo; não haverá diferenças visuais, mas cada alteração na tela afetará o estilo dos olhos. Btw você pode mexer com ele mesmo.
let x = 0; let needX = 100; let smoothing = 2; function update(){ x += (needX - x) / smoothing; if( Math.abs(x - needX) > 0.25 ){
- Se a mecânica acima estiver clara, você poderá criar efeitos mais complexos, por exemplo, primavera. A suavização mais simples e a aproximação do cursor são assim:
x += (mouseX - x) / smoothing; y += (mouseY - y) / smoothing;
Adicione suavização à diferença entre os valores das coordenadas necessárias e atuais.
Às vezes, a limitação de aproximação faz sentido. Há um exemplo acima, em que o valor muda de 0 a 100; portanto, no 1º valor da iteração atinge “50”, é um número bastante grande para 1 etapa. Essa mecânica lembra o
paradoxo de Aquiles e a tartaruga
PiscandoEsconda e mostre maçãs dos olhos a cada 2-3 segundos. O método mais trivial é “display: none;”, “transform: scaleY (N)” com valor dinâmico da escala y é um pouco mais complexo.
Crie 2 consts
const BLINK_COUNTER_LIMIT = 180; - número de renovações antes do início do piscar,
const BLINKED_COUNTER_LIMIT = 6; - número de renovações durante uma piscada.
E 2 variáveis, cujos valores mudarão a cada renovação.
let blinkCounter = 0; let blinkedCounter = 0;
Código de piscamento
let blinkTransform = ''; blinkCounter++; if(blinkCounter > BLINK_COUNTER_LIMIT){ blinkedCounter++ if(blinkedCounter > BLINKED_COUNTER_LIMIT){ blinkCounter = 0; } else { blinkTransform = ' scaleY(' + (blinkedCounter / BLINKED_COUNTER_LIMIT) + ')'; } } else { blinkedCounter = 0; }
BlinkTransform é uma variável de traçado que possui valor vazio entre o piscamento e os seguintes durante o piscamento
' scaleY(0.17)' ' scaleY(0.33)' ' scaleY(0.50)' ' scaleY(0.67)' ' scaleY(0.83)' ' scaleY(1.00)'
Todos os cálculos fornecem transformação de piscada variável, cujo valor deve ser adicionado à transformação de posição do código css dos olhos. Assim, uma string vazia é adicionada em caso de 3s de inatividade e não afeta a escala dos olhos, o valor de css é adicionado durante o piscar.
eye_left.style.transform = 'translate(' + xLeft + 'px,' + y + 'px)' + blinkTransform; eye_right.style.transform = 'translate(' + xRight + 'px,' + y + 'px)' + blinkTransform;
Lição da históriaTodos os dias encontramos coisas que parecem simples e óbvias e nem entendemos que essa simplicidade externa oculte uma quantidade colossal de perguntas e melhorias. Na minha opinião, o diabo está nos detalhes que formam todo o resultado final. Muhammad Ali, o melhor boxeador do século 20, levantou o calcanhar do pé traseiro no momento do soco direto. Essa manobra aumentou a distância efetiva do golpe e deu a ele mais chances de vencer. Sempre funcionou.
PS: Eu não tenho nenhuma influência no site e espero que seus proprietários não se ofendam com meus comentários. Por conveniência, chamei de maçã do olho = olho no código.