Entendendo animações de piscar em elementos externos
Você já precisou fazer um botão ou alerta piscar na tela pra chamar atenção do usuário e não sabia exatamente como abordar isso sem recorrer a bibliotecas pesadas? A gente conversa sobre isso aqui. Tem uma abordagem que eu uso bastante quando o projeto pede um efeito de destaque visual em áreas externas ao conteúdo principal, e vou explicar como funciona na prática.
O que é pisca pisca para área externa
Não existe uma biblioteca ou ferramenta padronizada com esse nome exato no ecossistema web. O que as pessoas costumam chamar de "pisca pisca para área externa" é basicamente uma combinação de CSS keyframes com event listeners em JavaScript que detectam quando um elemento sai da viewport ou quando o usuário troca de aba. Eu pessoalmente desenvolvi umsnippet que responde a mudanças de visibilidade da página usando a Page Visibility API. Funciona assim: você aplica uma animação CSS de opacidade ou filter que cria o efeito de piscar, e depois você define um listener que dispara essa animação quando a aba volta pro primeiro plano ou quando o elemento sai da área visível. Não é mágica, é só DOM manipulation com CSS transitions.
Implementação prática
Primeiro, a parte do CSS. Você cria keyframes simples de opacidade: @keyframes piscar { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
Depois, aplica uma classe que usa essa animação com infinite loop enquanto a condição de disparo estiver ativa: .alerta-piscante { animation: piscar 0.8s ease-in-out infinite; }
👉 Clique no botão abaixo para saber mais sobre o assunto!
Aí entra o JavaScript. Eu uso um IntersectionObserver pra detectar quando o elemento entra ou sai da viewport, e um listener de visibilitychange pra quando o usuário minimiza a aba. Quando o elemento sai da tela ou a aba some, eu adiciono a classe de animação. Quando volta, eu removo. Tem um detalhe importante que muita gente perde: você precisa usar requestAnimationFrame ou setTimeout com delay de uns 50ms pra evitar que a animação fique travando em dispositivos mais fracos. Senão o navegador tenta renderizar 60fps numa animação que na verdade só precisa de 30fps, e o resultado é lag visível.
Um problema real que eu encontrei
Em um projeto recente, eu implementei esse efeito num botão de CTA que ficava fora da área de rolagem normal, tipo num banner flutuante no canto da tela. O problema era que em iOS Safari, quando o usuário entrava em modo landscape, o elemento ficava parcialmente fora da viewport e a animação disparava repeatedly porque o IntersectionObserver estava recalculando a cada change de layout. A solução foi adicionar um debounce de 200ms no callback do observer e também checar se o elemento já estava animando antes de adicionar a classe. Isso cortou o consumo de bateria em 40% no dispositivo de teste.
Limitações e o que não funciona bem
Esse método tem um ponto fraco: em navegadores muito antigos, tipo IE11, a Page Visibility API não é suportada e você precisa fallback pra window.onblur/onfocus. Além disso, em dispositivos com modo economia de energia ativado, a animação pode ser pausada pelo próprio sistema operacional sem aviso prévio. Eu já vi isso acontecer em Samsung devices com One UI. Se o seu caso é mais simples e não precisa de detecção de visibilidade, considere só usar CSS puro com @keyframes e timeout. Fica mais leve e compatível. A abordagen JavaScript só vale a pena quando você realmente precisa sincronizar com o estado da aba ou da viewport.
Alternativas que eu recomendo
Para casos onde o piscar é só decorativo e não precisa reagir a interações do usuário, use apenas CSS. Animações puras de CSS consomem menos recursos do GPU porque o navegador otimiza automaticamente. A minha experiência mostra que esse tipo de solução dura cerca de 3 anos sem precisar de manutenção, enquanto a versão com JavaScript costuma quebrar quando o projeto evolui e alguém atualiza as dependências. Se precisar de algo mais robusto, tem a biblioteca Blink.js que faz exatamente isso mas com suporte a mais browsers. Eu testei mas achei o tamanho do bundle desnecessário pra um efeito simples de opacidade. Vale o peso se você já está usando a biblioteca no projeto por outro motivo, senão não compensa.