Transformando Dados em Efeitos de Partículas com Canvas

Este artigo explora como utilizar a API Canvas do HTML5 para converter dados de imagem em efeitos visuais baseados em partículas, cobrindo desde a leitura de pixels até a renderização animada.

Conceitos Fundamentais

Cada pixel em uma imagem é representado por quatro valores: Vermelho, Verde, Azul e Alfa (RGBA). Para acessar os dados de uma região no Canvas, usa-se o método getImageData(), que retorna um array onde cada grupo de quatro elementos corresponde a um pixel. A posição de um pixel na linha i e coluna j pode ser calculada com (i * largura + j) * 4. Em vez de renderizar os pixels diretamente, podemos deslocar suas posições para criar animações dispersas.

Exemplo 1: Efeito de Montagem de Imagem

Este exemplo carrega uma imagem, extrai seus pixels e os desenha com variações aleatórias de posição para simular um efeito de montagem.


<html lang="pt-BR">
<head>
  <meta charset="UTF-8">
  <title>Efeito de Montagem em Canvas</title>
</head>
<body>
  <canvas id="canvasMontagem" width="600" height="400"></canvas>
  <script>
    const tela = document.getElementById('canvasMontagem');
    const contexto = tela.getContext('2d');
    const imagem = new Image();
    imagem.src = 'caminho/para/imagem.jpg';
    imagem.crossOrigin = 'anonymous';
    let pontos = [];
    imagem.onload = function() {
      contexto.drawImage(imagem, 200, 100, 200, 200);
      const dadosPixels = contexto.getImageData(200, 100, 200, 200);
      extrairPontos(dadosPixels);
      desenharPontos();
    };

    function extrairPontos(dados) {
      const valores = dados.data;
      const largura = dados.width;
      const altura = dados.height;
      for (let y = 0; y < altura; y++) {
        for (let x = 0; x < largura; x++) {
          const indice = (y * largura + x) * 4;
          if (valores[indice + 3] > 0) {
            const ponto = {
              posX: 200 + x + (Math.random() - 0.5) * 30,
              posY: 100 + y + (Math.random() - 0.5) * 30,
              cor: `rgba(${valores[indice]}, ${valores[indice+1]}, ${valores[indice+2]}, ${valores[indice+3] / 255})`
            };
            pontos.push(ponto);
          }
        }
      }
    }

    function desenharPontos() {
      contexto.clearRect(0, 0, 600, 400);
      pontos.forEach(function(ponto) {
        contexto.fillStyle = ponto.cor;
        contexto.fillRect(ponto.posX, ponto.posY, 2, 2);
      });
    }
  </script>
</body>
</html>

Exemplo 2: Efeito de Relógio com Partículas

Este exemplo renderiza a hora atual como texto no Canvas, converte o texto em partículas e anima-as continuamente para criar um efeito dinâmico.


<html lang="pt-BR">
<head>
  <meta charset="UTF-8">
  <title>Relógio de Partículas</title>
  <style>
    body { margin: 0; background: #111; display: flex; justify-content: center; align-items: center; height: 100vh; }
    canvas { border: 1px solid #333; }
  </style>
</head>
<body>
  <canvas id="canvasRelogio" width="800" height="200"></canvas>
  <script>
    const canvas = document.getElementById('canvasRelogio');
    const ctx = canvas.getContext('2d');
    const NUM_PARTICULAS = 3000;
    const VELOCIDADE = 0.08;
    let particulas = [];
    let textoAtual = obterHoraFormatada();

    function obterHoraFormatada() {
      const agora = new Date();
      const horas = String(agora.getHours()).padStart(2, '0');
      const minutos = String(agora.getMinutes()).padStart(2, '0');
      const segundos = String(agora.getSeconds()).padStart(2, '0');
      return `${horas}:${minutos}:${segundos}`;
    }

    class Particula {
      constructor() {
        this.x = Math.random() * canvas.width;
        this.y = Math.random() * canvas.height;
        this.destinoX = 0;
        this.destinoY = 0;
        this.tamanho = Math.random() * 2 + 1;
        this.opacidade = 0;
        this.ativa = false;
      }
      atualizar() {
        if (this.ativa) {
          this.opacidade = Math.min(this.opacidade + 0.02, 1);
          const dx = this.destinoX - this.x;
          const dy = this.destinoY - this.y;
          const distancia = Math.sqrt(dx * dx + dy * dy);
          if (distancia > 1) {
            this.x += dx * VELOCIDADE;
            this.y += dy * VELOCIDADE;
          }
        } else {
          this.opacidade = Math.max(this.opacidade - 0.01, 0);
          this.x += (Math.random() - 0.5) * 2;
          this.y += (Math.random() - 0.5) * 2;
        }
      }
      desenhar() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.tamanho, 0, Math.PI * 2);
        ctx.fillStyle = `rgba(100, 200, 255, ${this.opacidade})`;
        ctx.fill();
      }
    }

    function inicializarParticulas() {
      particulas = [];
      for (let i = 0; i < NUM_PARTICULAS; i++) {
        particulas.push(new Particula());
      }
    }

    function extrairPixelsDoTexto() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.font = 'bold 100px Arial';
      ctx.fillStyle = 'white';
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(textoAtual, canvas.width / 2, canvas.height / 2);
      const dados = ctx.getImageData(0, 0, canvas.width, canvas.height);
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      const pixelsAtivos = [];
      for (let y = 0; y < dados.height; y += 2) {
        for (let x = 0; x < dados.width; x += 2) {
          const idx = (y * dados.width + x) * 4;
          if (dados.data[idx + 3] > 128) {
            pixelsAtivos.push({ x: x, y: y });
          }
        }
      }
      return pixelsAtivos;
    }

    function animar() {
      const pixels = extrairPixelsDoTexto();
      const numPixels = pixels.length;
      const numParticulasAtivas = Math.min(numPixels, particulas.length);
      for (let i = 0; i < numParticulasAtivas; i++) {
        const particula = particulas[i];
        particula.destinoX = pixels[i].x;
        particula.destinoY = pixels[i].y;
        particula.ativa = true;
      }
      for (let i = numParticulasAtivas; i < particulas.length; i++) {
        particulas[i].ativa = false;
      }
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      particulas.forEach(function(p) {
        p.atualizar();
        p.desenhar();
      });
      requestAnimationFrame(animar);
    }

    inicializarParticulas();
    setInterval(function() {
      textoAtual = obterHoraFormatada();
    }, 1000);
    animar();
  </script>
</body>
</html>

Para criar efeitos de partículas, é crucial utilizar getImageData() para obter dados de pixels e redesenhá-los com deslocamentos ou transformações. A limpeza do Canvas com clearRect() antes de cada quadro permite atualizações suaves, enquanto o método requestAnimationFrame() garante animações eficientes.

Tags: Canvas particulas javascript html5 Animação

Publicado em 7-8 23:50