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
<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>
...
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.
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)
...
// 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.
...
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.
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.
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 !