HTML5+JS
Os efeitos são criados usando JavaScript puro em um elemento canvas do HTML5. Nesta seção, vamos cobrir o básico: desenhar formas, desenhar com iteração e adicionar movimento.
Desenhando Formas
Seção intitulada “Desenhando Formas”Para este tutorial, vou usar um template genérico de lightscript:
<head> <meta description="Template"/> <meta publisher="WhirlwindFX" /></head>
<body style="margin: 0; padding: 0;"> <canvas id="exCanvas" width="320" height="200"></canvas></body>
<script> // Get the canvas element from the DOM var c = document.getElementById("exCanvas"); var ctx = c.getContext("2d");
var width = 320; var height = 200; var hue = 0;
function update() { // Code goes here window.requestAnimationFrame(update); }
window.requestAnimationFrame(update);</script>Seja usando uma linha ou várias, o processo é o mesmo.
- ctx.beginPath() inicia sua forma
- ctx.moveTo(x, y) define o primeiro ponto
- ctx.lineTo(x, y) define o próximo ponto e uma linha de volta ao primeiro. Use múltiplos lineTo’s para desenhar formas maiores.
- Defina ctx.strokeStyle e/ou ctx.fillStyle (se a forma for fechada)
- Use ctx.stroke() para desenhar as linhas e ctx.fill() para preencher formas fechadas. É importante notar que até este ponto, tudo é hipotético — nada é desenhado até que esses comandos sejam executados.
Aqui estão três exemplos usando linhas:
function update() { // One Line ctx.beginPath(); ctx.moveTo(30, 30); ctx.lineTo(30, 170); ctx.strokeStyle = "blue"; ctx.stroke();
// L ctx.beginPath(); ctx.moveTo(80, 30); ctx.lineTo(80, 100); ctx.lineTo(120, 100); ctx.strokeStyle = "blue"; ctx.stroke();
// Filled Triangle ctx.beginPath(); ctx.moveTo(130, 30); ctx.lineTo(130, 100); ctx.lineTo(170, 100); ctx.lineTo(130, 30); ctx.fillStyle = "red"; ctx.strokeStyle = "blue"; ctx.fill(); ctx.stroke();
window.requestAnimationFrame(update);}
É importante observar a ordem de desenho aqui: o triângulo preenchido é desenhado por último, então ele cobriria as outras duas formas se estivessem na mesma posição. Além disso, o contorno no triângulo é desenhado após o preenchimento. Se invertêssemos esses passos, a metade interior do contorno seria coberta pelo preenchimento vermelho.
Retângulos
Seção intitulada “Retângulos”Retângulos são simples e úteis, especialmente para a resolução média de um teclado RGB. O processo é parecido com o de desenhar linhas — basta substituir os comandos moveTo e lineTo por rect(x, y, width, height).
function update() { // Basic Square ctx.beginPath(); ctx.rect(30, 30, 75, 75); ctx.strokeStyle = "blue"; ctx.stroke();
// Filled Rectangle ctx.beginPath(); ctx.rect(125, 30, 125, 100); ctx.fillStyle = "red"; ctx.strokeStyle = "blue"; ctx.fill(); ctx.stroke();
window.requestAnimationFrame(update); }
Arcos são desenhados rotacionando um ponto externo em torno de um ponto central e podem ser usados para criar círculos completos. O comando de arco é ctx.arc(x, y, radius, start angle, end angle), onde ambas as medidas de ângulo são fornecidas em radianos. Diferentemente dos retângulos, um arco é desenhado com a origem (x, y) no centro do arco.
function update() { // Half-circle ctx.beginPath(); ctx.arc(70, 100, 30, 0, Math.PI); ctx.strokeStyle = "blue"; ctx.stroke();
// 3/4 circle ctx.beginPath(); ctx.arc(140, 100, 30, 0, Math.PI * (3/2)); ctx.strokeStyle = "blue"; ctx.stroke();
// Full circle ctx.beginPath(); ctx.arc(210, 100, 30, 0, Math.PI * 2); ctx.strokeStyle = "blue"; ctx.stroke(); ctx.fillStyle = "red"; ctx.fill();
window.requestAnimationFrame(update); }
Como você pode ver, os arcos são desenhados no sentido horário por padrão. Isso pode ser revertido com um ângulo final menor que o ângulo inicial, ou adicionando “true” como argumento final ao método ctx.arc().
Desenhando Com Iteração
Seção intitulada “Desenhando Com Iteração”Desenhar três formas à mão pode facilmente tomar vinte linhas de código, então se você precisar desenhar cem formas, precisamos de uma abordagem mais eficiente. Loops em JavaScript são uma forma simples de repetir tarefas e podem ser aplicados ao desenho também.
Aqui está um exemplo usando um for-loop para desenhar uma fileira de quadrados em xadrez:
function update() {
for(let i = 0; i < 8; i++){
// 1. Create rectangle path and draw the stroke for each. ctx.beginPath(); ctx.rect(i * 20, 20, 20, 20); ctx.strokeStyle = "black"; ctx.stroke();
// 2. If 'i' is even, the fillStyle is black. Otherwise it's white. if(i % 2 == 0){ ctx.fillStyle = "black"; } else { ctx.fillStyle = "white"; } // 3. Fill the shape after setting the fillStyle. ctx.fill(); }
window.requestAnimationFrame(update); }
Aqui está um exemplo de while-loop para desenhar uma grade xadrez. Vou alterar levemente a luminosidade dos quadrados pretos e o matiz dos quadrados brancos em cada iteração usando template literals.
function update() {
var i = 0;
while(i < 16){
// 1. X and Y calculations with 'row' and 'column' // This operation rounds the result down to the nearest integer. For i = (0-3) the row will be 0, for i = (4-7) the row will be 1, and so on var iRow = Math.floor(i / 4); // This operation finds the remainder after division, limiting column to the (0-3) range. var iCol = i % 4; // Multiply by the square width to find this square's x-origin var ix = iCol * 20; // Multiply by the square height to find this square's y-origin var iy = iRow * 20;
// 2. Path and stroke ctx.beginPath(); ctx.rect(ix, iy, 20, 20); ctx.strokeStyle = "black"; ctx.stroke();
// 3. If the row is even, every other square is filled black. If odd, switch the black squares. // Each iteration adds a small amount of the lightness component to the black, and hue to the white. if(iRow % 2 == 0){ if(i % 2 == 0){ // 'Black' ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)` } else { // 'White' ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`; } } else { if(i % 2 == 0){ // 'White' ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`; } else { // 'Black' ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)` } }
ctx.fill(); i++; }
window.requestAnimationFrame(update); }
Adicionando Movimento
Seção intitulada “Adicionando Movimento”As animações no canvas são limitadas apenas pela sua imaginação e pelo seu conhecimento matemático. Se você se sentir inseguro com trigonometria ou geometria, recomendo fazer uma pesquisa básica para levar suas habilidades ao próximo nível. Nos próximos exemplos, vou pegar um círculo estático e vincular cada variável possível a um movimento oscilatório simples.
O padrão ouro para oscilação (movimento regular de vai e vem) é calcular o sin() ou cosseno() de um timer. Qualquer uma das operações retornará um valor entre -1 e 1 para QUALQUER valor passado, mesmo que esteja apenas contando. A única diferença entre as duas é que elas estão levemente fora de sincronia — quando cos é 0, sin é -1 ou 1, e vice-versa. Podemos usar isso a nosso favor nesta animação —
function update() {
// Find current time in milliseconds, then divide to slow down the animation speed let time = Date.now() / 100;
// Draw a background so the old shapes don't stick around ctx.fillStyle = "white"; ctx.fillRect(0, 0, 320,200);
ctx.beginPath();
//USE ONE EXAMPLE AT A TIME // 1st example - side to side, y is constant ctx.arc(15 * Math.cos(time) + 160, 100, 30, 0, Math.PI * 2); // 2nd example - up and down, x is constant ctx.arc(160, 15 * Math.sin(time) + 100, 30, 0, Math.PI * 2); // 3rd example - circle, x and y are variable ctx.arc(15 * Math.cos(time) + 160, 15 * Math.sin(time) + 100, 30, 0, Math.PI * 2);
ctx.fillStyle = "black"; ctx.fill();
window.requestAnimationFrame(update); }De Lado a Lado
Seção intitulada “De Lado a Lado”
Para Cima e Para Baixo
Seção intitulada “Para Cima e Para Baixo”
Círculo Completo
Seção intitulada “Círculo Completo”
Temos mais alguns atributos para alterar ao longo do tempo — raio do círculo, preenchimento e contorno. Vou deixar os ângulos do arco de lado por enquanto para manter a forma simples, pois o resto será bem chamativo.
function update() {
let time = Date.now() / 100;
ctx.fillStyle = "white"; ctx.fillRect(0, 0, 320,200);
ctx.beginPath(); ctx.arc(15 * Math.cos(time) + 160, 15 * Math.sin(time) + 100, 30 + 15 * Math.sin(time), 0, Math.PI * 2);
ctx.fillStyle = `hsl(${180 + 180 * Math.cos(time)}, 100%, 50%)`; ctx.fill();
ctx.strokeStyle = `hsl(${180 + 180 * Math.sin(time)}, 100%, 50%)`; ctx.stroke();
window.requestAnimationFrame(update); }
Todos os atributos de todas as formas que criamos até agora podem ser alterados ao longo do tempo, e cada animação que faço é uma combinação das habilidades acima. Para mais detalhes sobre como criar animações reutilizáveis e eficientes, confira nossa página de Callbacks!