Desenvolvimento

26 jan, 2015

Desenhando partículas usando HTML5 Canvas

Publicidade

artigo03

Canvas para mim vem sendo a feature mais divertida do HTML5. A quantidade de coisas legais que podem ser criadas é absurdamente gigante. Porém, muitas pessoas acham que é algo difícil de aprender. Mas a verdade é que não é.

Evidentemente, ter uma boa base de geometria é muito importante. Mas se você não sabe muito, você pode criar coisas, porém mais simples.

Em seu arquivo HTML crie uma estrutura simples e adicione uma tag canvas com alguma classe aleatória. Neste artigo, o nome da classe será chamado de “particles”. Antes do fechamento da tag body, faça a chamada do arquivo Javascript, que irá ser nomeado como “particles.js”.

<canvas class="particles"></canvas>
<script src="particles.js"></script>
</body>

Agora, em particles.js, vamos começar a magia do canvas! Vou explicar parte por parte do código para um melhor entendimento – esse código está disponível no GitHub (neste link).

Na primeira parte, colocamos uma função para corresponder ao evento onload de window e nela selecionamos o body e o canvas, além de aplicar alguns estilos aos elementos. Note que não existe qualquer arquivo CSS. Optei por definir os estilos dentro do Javascript, mas você pode fazer da maneira que quiser. Também definimos que a função update (que iremos ver mais tarde) irá rodar a partir de um determinado intervalo.

window.onload = function() {
  var body = document.querySelector(‘body’);
  body.style.background = ‘#2C2C44';
  canvas = document.getElementById(‘particles’),
  ctx = canvas.getContext(‘2d’);
  body.style.margin = ‘0px’;
  canvas.style.margin = ‘0px’;
  canvas.style.padding = ‘0px’;
  canvas.width = canvas_width;
  canvas.height = canvas_height;
  draw = setInterval(update, speed);
}

Depois disso definimos algumas variáveis, tais como a velocidade dos eventos e tamanho do canvas. Mas que fique claro que não é bom usar variáveis globais, porém este uso dentro desse experimento é justificado por fins didáticos.

Neste projeto não há uso de requestAnimationFrame, mas recomendo dar uma boa olhada.

Com essa funcionalidade o navegador pode otimizar animações simultâneas em um único fluxo e remontar todo ciclo, levando a uma maior fidelidade de animação. Por exemplo, serve muito bem em  animações sincronizadas com transições CSS ou SVG SMIL.

Além disso, animações baseadas em JS, onde está executando o loop de animação em uma guia que não é visível, o navegador não vai mantê-lo funcionando, o que significa menos uso de CPU, GPU e da memória, levando a muito mais tempo de vida da bateria.

Uma excelente fonte de estudo para requestAnimationFrame: (neste link)

// Settings
var speed = 35,
    canvas_width = window.innerWidth,
    canvas_height = window.innerHeight;

Depois há a definição de outras variáveis globais para definir a instância do canvas, o número de vezes que as partículas foram criadas, um limite para partículas, uma lista de partículas que foram criadas e as cores usada.

Repetindo: dificilmente é legal usar variáveis globais, o custo computacional normalmente é bastante alto e a aplicação fica menos organizada. Um dos poucos casos onde aplicar escopo global em variáveis é vantajoso é quando os dados são constantes.

var canvas,
    ctx,
    times = 0,
    limit = 100,
    draw,
    particles = [],
    colors = [‘#F0FD36', ‘#F49FF1', ‘#F53EAC’, ‘#76FBFA’];

Se estamos criando algo que precisa de aleatoriedade na posição, tamanho e cor da partícula, por que não usar uma função para entregar esses dados? Esta não é a melhor solução, porém é bastante prático e fácil de entender.

var getRand = function(type) {
    if (type === ‘size’)
        return (Math.floor(Math.random() * 8) * 10)
    if (type === ‘color’)
        return Math.floor(Math.random() * colors.length)
    if (type === ‘pos’)
        return [
            (Math.floor(Math.random() * 200) * 10),
            (Math.floor(Math.random() * 80) * 10)
        ]
    return false
};

Ok, agora vamos criar uma função genérica para criar partículas usando como base os argumentos recebidos.

var drawParticle = function(x, y, size, color, opacity){
    ctx.beginPath();
    ctx.globalAlpha = opacity;
    ctx.arc(x, y, size, 0, 2 * Math.PI);
    ctx.fillStyle = color;
    ctx.fill();
    ctx.strokeStyle = color;
    ctx.stroke();
}

Lembra daquela função update? Aquela que foi posta para executar no setInterval dentro da função carregada no evento onload de window? Então, é nela que acontece a magia do desenho das partículas, além de controlar o limite das partículas geradas.

Perceba que para cada partícula projetada, também é salvo um objeto com informações individuais sobre a partícula na lista de partículas.

function update(args) {
    var color = colors[getRand(‘color’)],
        pos = getRand(‘pos’),
        size = getRand(‘size’),
        opacity = 1;
    drawParticle(pos[0], pos[1], size, color, opacity)
    times++;
    particles.push([pos[0], pos[1], color, opacity, size]);
    if (times >= limit) {
        clearInterval(draw);
        draw = setInterval(clean, speed);
    }
}

Até agora, o experimento só cria as partículas na tela, mas quando é alcançado o limite não acontece mais nada.

Existe uma função nomeada de clean, que é executada quando o limite de partículas é alcançado dentro da execução do método update. Nesta função ela realiza a leitura de cada partícula e atualiza sua opacidade por um valor menor ao anterior, tudo isso rodando por um período de tempo anteriormente definido, dando um efeito visual de fadeOut das partículas.

function clean() {
    ctx.clearRect(0, 0, canvas_width, canvas_height);
    particles.forEach(function(p) {
        /*
            p[0] = x,
            p[1] = y,
            p[2] = color
            p[3] = globalAlpha,
            p[4] = size
        */
        p[3] = p[3] — 0.06;
        drawParticle(p[0], p[1], p[4], p[2], p[3])
        if (p[p.length — 1] && p[3] <= 0.0) {
            ctx.clearRect(0, 0, canvas_width, canvas_height);
            clearInterval(draw);
            times = 0;
            particles = []
            draw = setInterval(update, speed);
        }
    });
}

Agora, sim, pode rodar o experimento em seu navegador. Lá você verá um simples canvas (você pode ver rodando aqui também). Este código usado precisa de refatoração e se você quiser pode enviar um Pull Request lá no GitHub.

A ideia deste artigo é mostrar como é simples criar e brincar com o canvas.

Se você já alguma uma ideia? Dê a ela uma chance.