Introdução

Fala pessoal, venho informar que é um prazer estar com vocês novamente, e que esse é o segundo post da série "Bem-vindo ao canvas", pra quem não viu o primeiro é só vir aqui. Lembrando que a intenção dessa série em 3 posts é nos introduzir aos princípios do canvas, e no final fazer um efeito de particulas muito maneiro.

Nesse post iremos falar sobre as principais transformações matriciais(translação, rotação e escala). No final novamente irá surgir algum desenho interesssante(eu acho), sem mais, vamos ao que interessa.

Se você não faz idéia do que é o canvas, no primeiro post tem uma explicação bem resumida dessa ótima ferramenta do HTML5.

Antes de tudo

Precismos da nossa tag <canvas></canvas> e inicializar nosso contexto

index.html

<style>
   *{padding:0;margin:0;}
   // esse position é só ter uma barra de rolagem
   // overflow: hidden no body tb é blz XD
   #mycanvas{position:fixed;}
</style>
...
<canvas id="mycanvas" width="150" height="150"></canvas>
...
	
index.js

window.onload = draw;

function draw(){
   // Obtendo nosso nó de referencia
   var canvas = document.getElementById("mycanvas");
   // ajustar as dimensões para ocupar a página inteira
   canvas.width = window.innerWidth;
   canvas.height = window.innerHeight;
   
   // inicializando o contexto em 2 dimensões
   var context = canvas.getContext("2d");
}
	

Continuamos usando as formas

Infelizmente, aprender algo novo não cancela algo antigo, pelo menos não nesse caso, aqui precisaremos usar os "caminhos"(paths), para demonstrar o que iremos fazer.

Como o desenho desse post ficará um pouco mais complexo, vamos abraçar toda a nossa peça em um objeto.

Mãos a obra, vamos fazer nosso desenho.

index.js

window.onload = draw;

function draw(){
   var canvas = document.getElementById("mycanvas");
   canvas.width = window.innerWidth;
   canvas.height = window.innerHeight;

   // retiramos o contexto dessa função
   // e transferimos para o objeto sky
   sky.paint(canvas);
}

var sky = {
   canvas:null,
   ctx:null,
   paint:function(canvas){
      // setar as configurações iniciais
      this.canvas = canvas;
      // atribuindo o contexto a uma variavel
      // no nosso objeto
      this.ctx = this.canvas.getContext("2d");

      // vamos simplificar nosso código e usar
      // funções especificas para cada parte
      // do nosso desenho
      this.drawSky();
   },
   drawSky:function(){
      // função responsável por desenhar nosso céu
      // (oh really?)
      this.ctx.fillStyle = "#0c1644"; // fillStyle para escolher a cor
                                      // de preenchimento do nosso fundo
      
      // aqui o céu é desenhado para ocupar
      // o palco inteiro
      this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
   }
}
   

Até ai show, nada de novo a não ser o fillStyle, que não é nada tão complicado de entender, ele é semelhante ao strokeStyle, mas vai cuidar da cor do preenchimento das nossas formas.

Transformações

Consegui pensar em um exemplo muito legal, nós iremos em uma função, pra desenhar uma unica forma utilizar 3 das 4 transformações possíveis, rotação, translação e escala.

Infelizmente não da pra pular a parte teórica chatona, não para mim, então pra não confundir tudo, irei primeiro falar sobre cada transformação separadamente, e por fim vamos ao exemplo utilizando as 3

Translação

A transformação de translação, é realizada quando você quer mudar a posição de um objeto, demonstrado acima.

No nosso caso do canvas, nós não conseguimos criar objetos inteiros e move-los de forma sólida para outro ponto, dessa maneira, a transformação de translação no canvas serve para mudar a origem do nosso palco, demonstrado abaixo, e a partir dali iremos desenhar a forma que planejavamos translocar.

A função responsavel por isso no canvas é a .translate(x, y).

Escala

Como demonstrado na figura acima, a transformação de escala, é nada mais do que aumentar algum eixo proporcionalmente ao valor passado, lembrando que a escala inicial/padrão é sempre (1, 1), sendo assim na imagem vemos que a figura foi diminuida pela metade(ou 1/2 = 0.5), se quisesse dobrar o tamanho do desenho ele teria usado (2, 2).

A função que iremos utilizar será .scale(x, y).

Fato interessante: nós podemos espelhar nosso canvas utilizando uma transformação negativa (-1, 1), (1, -1) ou (-1, -1)

espelhado.js

...
   // caso apliquemos
   context.scale(1, -1);
   // nosso sistema de coordenadas ficará
   // com a origem em baixo esquerda, como
   // um tradicional plano cartersiano :D
...

Rotação

Rotação a última das transformações mais utilizadas, ela também pode ser considerada a mais simples, responsável apenas por rotacionar um objeto no sentido horário.

Novamente, no canvas não controlamos as formas, só controlamos o palco, sendo assim o que acontece é que o nosso palco será rotacionado no sentido horário como demonstrado abaixo.

Utilizaremos .rotate(angulo) para aplicar essa transformação.

muito importante

Todas as transformações que fazemos no nosso contexto ficam salvas, então sempre é necessão chamar um .save() e .restore(), antes e depois de aplicar as transformações.

indicado_fazer.js

...
   context.save(); // salvo o estado atual
   // faz as transformações
   context.restore(); // retorna ao estado salvo
...

Exemplo

Visto isso, vamos à continuação do nosso desenho.

index.js

window.onload = draw;

function draw(){ ... } // o código é o mesmo de lá de cima
                       // só omiti ele para não ficar
                       // ocupando mt espaço na área de code

var sky = {
   canvas:null,
   ctx:null,
   paint:function(canvas){
      this.canvas = canvas;
      this.ctx = this.canvas.getContext("2d");

      this.drawSky();

      // manda desenhar a estrela do nosso céu
      this.drawStar();
   },
   drawSky:function(){ ... },
   drawStar:function(){
      // primeira coisa sempre é salvar o estado atual
      this.ctx.save();

      // nossa primeira transformação, mudamos a origem
      // para 100px distante em x e y
      this.ctx.translate(100, 100);

      // definindo o tamanho da nossa estrela
      // aqui definimos qual será a escala da nossa estrela 
      // de 0 até 1.5
      var escala = Math.random()*1.5;
      // nossa segunda transformação, alteramos o tamanho
      // da escala para o valor da variavel calculada acima
      this.ctx.scale(escala, escala);

      // aqui definimos a distancia das pontas das nossas estrelas
      var raioMax = 20;
      // e aqui a distancia minima das nossas pontas
      var raioMin = 8;

      // iniciamos nosso caminho
      this.ctx.beginPath();

      // aplicamos uma rotação aleatória para a
      // estrela ficar um pouco inclinada
      this.ctx.rotate(Math.random());

      // movemos nossa ponta da caneta para a ponta mais distance
      this.ctx.moveTo(raioMax, 0);
      
      // e aqui vem o for do desenho que fica fazendo uma linha
      // para ponta distante e a interna
      for(var i = 0; i < 10; i++){
         // a cada loop nós adicionamos pi/5 a rotação anterior
         // isso ocorre porque não chamamos o método .restore()
         // se tivessemos chamado ele toda vez fariamos uma
         // linha na mesma direção.
         this.ctx.rotate(Math.PI/5);
      
         // condicional simples para verificar se é para desenhar
         // para a ponta externa ou para a interna
         if(i%2 == 0){
            this.ctx.lineTo(raioMin, 0)
         }else{
            this.ctx.lineTo(raioMax, 0)
         }
      }
      
      // fechar o caminho, MUITO IMPORTANTE
      this.ctx.closePath();
      
      // escolher a cor do preenchimento da nossa estrela
      this.ctx.fillStyle = "white";
      // preencher
      this.ctx.fill();
      
      // e retornar ao estado anterior, assim não atrapalharemos
      // nenhum desenho feito por outra função
      this.ctx.restore();
   }
}

Deixando interessante

Na introdução do post eu prometi que surgiria alguma coisa legal, até o momento só tem uma estrela no meio de um troço azul.

Pra deixar interessante vamos direto ao código, bora encher esse céu de estrelas.

index.js

window.onload = draw;

function draw(){ ... } 

var sky = {
   quantStars:70, // aqui setamos quantas estrelas queremos
                  // no nosso céu
   canvas:null,
   ctx:null,
   paint:function(canvas){
      this.canvas = canvas;
      this.ctx = this.canvas.getContext("2d");

      this.drawSky();

      this.populateSky(); // vamos popular o céu com estrelas !
   },
   drawSky:function(){ ... },
   populateSky:function(){
      // um loop simples que cuida de criar as
      // nossas estrelas
      for(var i=0; i < this.quantStars; i++;){
         // a estrela vai ser gerada em algum lugar aleatório 
         // sem ultrapassar o tamanho da tela
         this.drawStar(Math.random()*this.canvas.width,
                       Math.random()*this.canvas.height);
      }
   },
   drawStar:function(posX, posY){ // só fizemos essa alteração aqui
      this.ctx.save();
      
      // pra poder criar a estrela no local que passarmos como parametro
      this.ctx.translate(posX, posY);

      var escala = Math.random()*1.5;
      this.ctx.scale(escala, escala);

      var raioMax = 20;
      var raioMin = 8;

      this.ctx.beginPath();
      this.ctx.rotate(Math.random());
      this.ctx.moveTo(raioMax, 0);
      for(var i = 0; i < 10; i++){
         this.ctx.rotate(Math.PI/5);
         if(i%2 == 0){
            this.ctx.lineTo(raioMin, 0)
         }else{
            this.ctx.lineTo(raioMax, 0)
         }
      }
      
      this.ctx.closePath();

      this.ctx.fillStyle = "white";
      this.ctx.fill();
      
      this.ctx.restore();
   }
}

Seguidos todos os passos corretamente, você provavelmente teve um resultado idêntico a esse !(só ta mudando toda vez que você recarrega a página :p)

See the Pen Céu estrelado by Matheus Mesquita (@mathmesquita) on CodePen.

Conclusão

Wow, esse post acabou conseguindo ser maior que o outro!! :O Mas é isso aí galera, conforme vai aumentando a complexidade os posts vão ficando maiores, esse talvez foi mais divertido que o outro pois teve um resultado que ja começa a traçar uma linha para o que queremos de particulas(se você mudar a estrela pra um circulo você acabou de gerar as particulas).

A série continua semana que vem(chegando ao seu final :[ ), mas relaxe que ja estou planejando mais coisas para brincar com o canvas !! Quem sabe uma série de canvas avançado ? ou fazendo um jogo na unha ? Enfim !!! muitas idéias e por hoje paramos aqui.

Qualquer dúvida, sugestão, elogio, xingamento convite para uma conversa, estarei nos comentários !! Espero que tenham gostado e até a próxima. õ7

Último post ja está disponível !