Você está aqui: Home ‣ Dive Into HTML5 ‣
❧
TML 5 define o elemento <canvas> como “uma tela bitmap de resolução dependente que pode ser usada para renderizar gráficos, jogos, ou outras imagens em tempo real.” A tag canvas é um retângulo na sua página, onde você pode usar JavaScript para desenhar o que você quiser.
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
---|---|---|---|---|---|---|
7.0+* | 3.0+ | 3.0+ | 3.0+ | 10.0+ | 1.0+ | 1.0+ |
* Internet Explorer 7 e 8 necessitam da biblioteca
explorercanvas. Internet Explorer 9 suporta
<canvas> nativamente.
|
Então como o canvas se parece? Com nada, sério. O elemento
<canvas>
não tem conteúdo nem borda.
↜ Canvas Invisível
Sua sintaxe se assemelha a isso:
<canvas width="300" height="225"></canvas>
Vamos adicionar uma borda pontilhada, então você pode ver com o que estamos lidando.
↜ Canvas com borda
Você pode ter mais de um elemento <canvas>
na mesma
página. Cada canvas será mostrado no DOM, e cada um mantém
seu próprio estado. Se você der para cada canvas um atributo
id
, você pode acessá-lo como qualquer outro elemento.
Vamos expandir nosso exemplo de canvas para incluir o atributo
id
:
<canvas id="a" width="300" height="225"></canvas>
Agora você pode encontrar facilmente este
<canvas>
dentro do DOM.
var a_canvas = document.getElementById("a");
❧
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
---|---|---|---|---|---|---|
7.0+* | 3.0+ | 3.0+ | 3.0+ | 10.0+ | 1.0+ | 1.0+ |
* Internet Explorer 7 e 8 necessitam da biblioteca
explorercanvas. Internet Explorer 9 suporta
<canvas> nativamente.
|
Todo canvas começa em branco. Que chato! Vamos desenhar alguma coisa.
⇜ Clique para desenhar nesse canvas
O evento onclick
dispara essa função:
function draw_b() {
var b_canvas = document.getElementById("b");
var b_context = b_canvas.getContext("2d");
b_context.fillRect(50, 25, 150, 100);
}
A 1ª linha dessa função não é nada de especial; apenas encontra
o elemento <canvas>
no DOM.
E então temos isso ⇝
function draw_b() {
var b_canvas = document.getElementById("b");
var b_context = b_canvas.getContext("2d");
b_context.fillRect(50, 25, 150, 100);
}
Todo canvas possui um contexto de desenho, que é onde toda
diversão acontece. Uma vez tendo encontrado o elemento
<canvas>
no DOM (ao usar
document.getElementById()
ou qualquer método que você
queira), você chama seu método getContext()
. Você
deve passar a string "2d"
para o método
getContext()
.
☞P: Existe um canvas 3-D?
R: Ainda não. Fornecedores têm experimentado suas próprias APIs para canvas tridimensionais, mas nenhuma se tornou padrão ainda. De acordo com a especificação da HTML5, “Uma futura versão dessa especificação irá provavelmente definir um contexto 3d.”
Então você tem o elemento <canvas>
e seu contexto de
desenho. O contexto de desenho é onde todos os métodos e propriedades do
desenho são definidos. Há um bando de propriedades e métodos dedicados ao
desenho de retângulos:
fillStyle
pode ser uma cor, padrão ou degradê
do CSS. (Mais sobre degradês em breve.) O padrão para o
fillStyle
é preto sólido, mas você pode definir o que
quiser. Cada contexto de desenho guarda suas próprias propriedades
enquanto a página estiver aberta, a não ser que você faça alguma coisa
para resetar isso.
fillRect(x, y, largura, altura)
desenha um retângulo
preenchido com o fillStyle atual.
strokeStyle
é como fillStyle
—
pode ser uma cor, um padrão ou um degradê.
strokeRect(x, y, largura, altura)
desenha um retângulo com
o strokeStyle atual. strokeRect
não preenche o meio, apenas
desenha as bordas.
clearRect(x, y, largura, altura)
limpa os pixels no
retângulo especificado.
☞P: Eu posso “resetar” um canvas?
R: Sim. Definindo a altura e a largura de um elemento<canvas>
irá apagar seu conteúdo e resetar todas as propriedades do seu contexto de desenho para os valores padrão. Você sequer precisa alterar a largura; você pode simplesmente definir ele para seu valor atual, como em:var b_canvas = document.getElementById("b"); b_canvas.width = b_canvas.width;
Voltando para amostra de código vista no exemplo anterior…
Desenhe um retângulo ⇝
var b_canvas = document.getElementById("b");
var b_context = b_canvas.getContext("2d");
b_context.fillRect(50, 25, 150, 100);
Chamando o método fillRect()
desenha-se um retângulo e o
preenche com o estilo de preenchimento atual, no qual é preto até você
alterar isso. Um retângulo é limitado por seu canto superior esquerdo
(50, 25), sua largura (150), e sua altura (100). Para ter uma visão
melhor de como isso funciona, vamos ver o sistema de coordenadas desse
canvas.
❧
O canvas é uma grade bidimensional. A coordenada (0, 0) fica no canto superior esquerdo do canvas. Ao longo do eixo X, os valores aumentam em direção à borda direita da tela. Ao longo do eixo Y, os valores aumentam em direção à borda de baixo do canvas.
Diagrama de coordenadas do Canvas↷
O diagrama de coordenadas foi desenhado com o elemento
<canvas>
. Ele compreende:
Primeiro temos que definir o elemento <canvas>
. O
elemento <canvas>
define a largura
(width
), a altura (height
) e o identificador
(id
) para que possamos encontrá-lo mais tarde.
<canvas id="c" width="500" height="375"></canvas>
Depois nós precisamos encontrar o elemento <canvas>
no
DOM e buscar seu contexto de desenho.
var c_canvas = document.getElementById("c");
var context = c_canvas.getContext("2d");
Agora podemos começar a desenhar as linhas.
❧
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
---|---|---|---|---|---|---|
7.0+* | 3.0+ | 3.0+ | 3.0+ | 10.0+ | 1.0+ | 1.0+ |
* Internet Explorer 7 e 8 necessitam da biblioteca
explorercanvas. Internet Explorer 9 suporta caminhos do
<canvas> nativamente.
|
Imagine que você está desenhando um quadro com tinta. Você não quer começar mergulhando e desenhando o quadro com a tinta, isso porque você pode cometer um erro. Ao invés disso, você rascunha as linhas e curvas com um lápis, e quando você estiver feliz com aquilo, irá traçar com tinta por cima do rascunho.
Cada canvas tem um caminho. Definir um caminho é como desenhar com um lápis. Você pode desenhar o que quiser, mas não irá fazer do produto final até que você pegue a pena e trace seu caminho com tinta.
Para desenhar linhas retas com lápis, use os dois métodos seguintes:
moveTo(x, y)
move o lápis para o ponto inicial
especificado.
lineTo(x, y)
desenha a linha para o ponto final
especificado.
Quanto mais você chamar moveTo()
e lineTo()
,
maior será o tamanho do caminho. Esses são métodos “lápis” — você pode
chamá-los o quanto quiser, mas você não irá ver nada no canvas até que
você invoque os métodos “tinta”.
Vamos começar desenhando nossa grade esbranquiçada.
for (var x = 0.5; x < 500; x += 10) {
context.moveTo(x, 0);
context.lineTo(x, 375);
}
⇜ Desenha as linhas verticais
for (var y = 0.5; y < 375; y += 10) {
context.moveTo(0, y);
context.lineTo(500, y);
}
⇜ Desenha as linhas horizontais
Esses foram todos métodos “lápis”. Nada foi desenhado no canvas ainda. Nós precisamos de um método “tinta” para tornar isso permanente.
context.strokeStyle = "#eee";
context.stroke();
stroke()
é um dos métodos “tinta”. Ele pega o caminho
complexo que você definiu com todos aqueles moveTo()
e
lineTo()
, e realmente desenha eles no canvas. O
strokeStyle
controla a cor das linhas. Esse é o resultado:
☞P: Por que você começou com x e y com
0.5
? Por que não0
?
R: Imagine cada pixel como um grande quadrado. As coordenadas inteiras (0, 1, 2…) são arestas desse quadrado. Se você desenhar uma linha com uma unidade de largura entre coordenadas inteiras, ele irá sobrepor lados opostos do quadrado de pixel, e a linha resultante será desenhada com dois pixels de largura. Para desenhar uma linha que tenha apenas um pixel de largura, você precisa mudar as coordenadas para 0.5 perpendicular à direção da linha.Por exemplo, se você tentar desenhar a linha de
(1, 0)
para(1, 3)
, o navegador irá desenhar a linha cobrindo 0.5 pixels da tela em ambos os ladosx=1
. A tela não consegue exibir meio pixel, então irá expandir a linha para cobrir um total de dois pixels:
Mas se você tentar desenhar uma linha de
(1.5, 0)
para(1.5, 3)
, o navegador irá desenhar a linha cobrindo 0.5 pixels da tela em ambos os ladosx=1.5
, o que resulta na verdade em uma linha de 1 pixel de largura:
Agradecimentos ao Jason Johnson por prover esses diagramas.
Agora vamos desenhar a flecha horizontal. Todas as linhas e curvas no caminho são desenhadas com a mesma cor (ou degradê — sim, nós vamos chegar nisso em breve). Nós queremos desenhar a flecha com uma tinta de cor diferente — preta ao invés de esbranquiçada — então vamos precisar de um novo caminho.
Um novo caminho ↷
context.beginPath();
context.moveTo(0, 40);
context.lineTo(240, 40);
context.moveTo(260, 40);
context.lineTo(500, 40);
context.moveTo(495, 35);
context.lineTo(500, 40);
context.lineTo(495, 45);
A flecha vertical é praticamente igual. Já que a flecha vertical utiliza a mesma cor que a flecha horizontal, nós não vamos precisar criar um novo caminho. As duas flechas farão parte de um mesmo caminho.
context.moveTo(60, 0);
context.lineTo(60, 153);
context.moveTo(60, 173);
context.lineTo(60, 375);
context.moveTo(65, 370);
context.lineTo(60, 375);
context.lineTo(55, 370);
↜ Não é um novo caminho
Eu disse que essas flechas serão pretas, mas o
strokeStyle
continua esbranquiçado. (O
fillStyle
e o strokeStyle
não são resetados
quando você começa um novo caminho.) Tudo bem, porque nós vamos apenas
rodar uma série de métodos “lápis”. Mas antes de desenhar de verdade, na
“tinta”, nós vamos precisar definir o strokeStyle
para preto.
Caso contrário, essas duas flechas ficarão esbranquiçadas, e nós
dificilmente vamos ser capazes de vê-las! As seguintes linhas mudam a cor
para preto e desenham as linhas no canvas:
context.strokeStyle = "#000";
context.stroke();
E o resultado:
❧
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
---|---|---|---|---|---|---|
7.0+* | 3.0+† | 3.0+ | 3.0+ | 10.0+ | 1.0+ | 1.0+ |
* Internet Explorer 7 e 8 necessitam da biblioteca
explorercanvas. Internet Explorer 9 suporta
<canvas> nativamente.
|
||||||
† Mozilla Firefox 3.0 necessita uma biblioteca compatível. |
Em adição ao desenho de linhas com canvas, você também pode desenhar texto com um canvas. Diferentemente do texto em torno de uma página web, não há box model. Isso significa que nenhuma das técnicas familiares de layout em CSS são válidas: sem floats, sem margins, sem padding, sem word wrapping. (Talvez você pense que isso é uma boa coisa!) Você pode definir alguns poucos atributos de fonte, depois pode pegar um ponto no canvas e começar a desenhar seu texto ali.
Os atributos de fonte a seguir estão disponíveis no contexto do desenho:
font
pode ser qualquer coisa que você colocaria na regra
font
do CSS. Incluindo font style, font
variant, font weight, font size, line height, e font family.
textAlign
controla o alinhamento do texto. É parecido (mas
não idêntico) a regra text-align
do CSS. Os
possíveis valores são start
, end
,
left
, right
, e center
.
textBaseline
controla onde o texto é desenhado relativo ao
ponto de início. Os possíveis valores são top
,
hanging
, middle
, alphabetic
,
ideographic
, ou bottom
.
textBaseline
é complicado, porque texto é complicado (Inglês
não é, mas você pode desenhar qualquer caracter Unicode que queira no
canvas, e Unicode é complicado). A especificação da HTML5
explica os diferentes textBaselines:
A parte superior do quadrado 'em' fica aproximadamente na parte superior dos glifos em uma fonte, a linha de base é pendurada no lugar onde alguns glifos como आ são âncoradas, o meio é metade do caminho entre a parte superior do quadrado 'em' e a base do quadrado 'em', a linha de base alfabética é onde os caracteres como Á, ÿ, f, e Ω são âncorados, a linha de base ideográfica é onde glifos como 私 e 達 são âncorados, e a base do quadrado 'em' fica aproximadamente na base do glifo em uma fonte. O topo e a base da caixa delimitadora podem ser longe das linhas de base, devido a glifos que se estendem muito além do quadrado.
Para alfabetos simples como o Inglês, você pode usar com segurança o
top
, middle
, ou bottom
para a
propriedade textBaseline
.
Vamos desenhar algum texto! O texto desenhado dentro do canvas herda o
tamanho da fonte e o estilo do próprio elemento
<canvas>
, você pode sobrescrever isso definindo a
propriedade font
para o contexto de desenho.
context.font = "bold 12px sans-serif";
context.fillText("x", 248, 43);
context.fillText("y", 58, 165);
↜ Altera o estilo da fonte
O método fillText()
desenha o texto de fato.
context.font = "bold 12px sans-serif";
context.fillText("x", 248, 43);
context.fillText("y", 58, 165);
⇜ Desenha o texto
☞P: Posso usar tamanhos de fontes relativos para desenhar texto em um canvas?
R: Sim. Como qualquer outro elemento HTML na sua página, o próprio elemento<canvas>
computa o tamanho da fonte baseado nas regras CSS da sua página. Se você definir a propriedadecontext.font
para um tamanho relativo de fonte como1.5em
ou150%
, seu navegador irá manipular isso para o tamanho computado no próprio elemento<canvas>
.
Para o texto no canto superior esquerdo, vamos dizer que eu queira colocar
o topo do texto no y=5
. Mas eu sou um cara preguiçoso — Eu
não quero medir a altura do texto e calcular a linha de base. Ao invés
disso, eu posso definir o textBaseline
para
top
e passar para o canto superior esquerdo a coordenada da
caixa delimitadora do texto.
context.textBaseline = "top";
context.fillText("( 0 , 0 )", 8, 5);
Agora para o texto no canto inferior direito. Vamos dizer que eu queira
que fique na coordenada (492,370)
— somente há alguns pixels
de distância do canto inferior direito do canvas — mas eu não quero medir
a altura e largura do texto. Posso definir a propriedade
textAlign
para right
e a
textBaseline
para bottom
, então chamar
fillText()
com as coordenadas do canto inferior direito da
caixa delimitadora do texto.
context.textAlign = "right";
context.textBaseline = "bottom";
context.fillText("( 500 , 375 )", 492, 370);
E o resultado:
Oops! Nós nos esquecemos dos pontos nos cantos. Nós iremos ver como desenhar círculos um pouco mais tarde. Por enquanto, vou trapacear um pouquinho e desenhar eles como retângulos.
context.fillRect(0, 0, 3, 3);
context.fillRect(497, 372, 3, 3);
⇜ Desenha dois “pontos”
E isso é tudo! Aqui está o produto final:
❧
IE | Firefox | Safari | Chrome | Opera | iPhone | Android | |
---|---|---|---|---|---|---|---|
degradês lineares | 7.0+* | 3.0+ | 3.0+ | 3.0+ | 10.0+ | 1.0+ | 1.0+ |
degradês radiais | 9.0+ | 3.0+ | 3.0+ | 3.0+ | 10.0+ | 1.0+ | 1.0+ |
* Internet Explorer 7 e 8 necessitam da biblioteca
explorercanvas. Internet Explorer 9 suporta <canvas> degradê
nativamente.
|
Mais cedo nesse capítulo, você aprendeu como desenhar um retângulo preenchido com uma cor sólida, depois uma linha com a borda de uma cor sólida. Mas formas e linhas não são limitadas a cores sólidas. Você pode usar qualquer mágica com degradês. Vamos ver um exemplo.
A marcação parece a mesma que qualquer outro canvas.
<canvas id="d" width="300" height="225"></canvas>
Primeiro, nós precisamos encontrar o elemento
<canvas>
e seu contexto de desenho.
var d_canvas = document.getElementById("d");
var context = d_canvas.getContext("2d");
Uma vez tendo o contexto de desenho, nós começamos a definir o degradê. O degradê é uma transição suave entre duas ou mais cores. O contexto de desenho do canvas suporta dois tipos de degradês:
createLinearGradient(x0, y0, x1, y1)
pinta através de uma
linha de (x0, y0) até (x1, y1).
createRadialGradient(x0, y0, r0, x1, y1, r1)
pinta através
de um cone entre dois círculos. Os primeiros três parâmetros representam
o início do círculo, com origem (x0, y0) e raio r0. Os últimos três
parâmetros representam o fim do círculo, com origem (x1, y1) e raio r1.
Vamos criar um degradê linear. Degradês podem ter qualquer tamanho, mas eu vou criar esse degradê com 300 pixels de largura, como o canvas.
Cria um objeto degradê↷
var my_gradient = context.createLinearGradient(0, 0, 300, 0);
Por conta dos valores y
(o 2º e 4º
parâmetros) são ambos 0, esse degradê irá sombrear uniformemente da
esquerda para direita.
Uma vez tendo o objeto degradê, nós podemos definir as cores do degradê. O degradê possui duas ou mais paradas de cor. Paradas de cor podem estar em qualquer lugar através do degradê. Para adicionar uma parada de cor, você precisa especificar sua posição através do degradê. As posições no degradê podem estar em qualquer lugar entre 0 e 1.
Vamos definir um degradê que irá sombrear do preto ao branco.
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");
Definir um degradê não desenha nada no canvas. É só um objeto armazenado
em algum lugar na memória. Para desenhar o degradê, você define seu
fillStyle
para o degradê e desenha a forma, como um retângulo
ou linha.
Fill style é um degradê ↷
context.fillStyle = my_gradient;
context.fillRect(0, 0, 300, 225);
E esse é o resultado:
Supondo que você queira um degradê que irá sombrear de cima para baixo.
Quando você criar um objeto degradê, deixe os valores
x
(1º e 3º parâmetros) constante, e
faça que os valores y
(2º e 4º
parâmetros) alcancem de 0 até a altura do canvas.
valores x são 0, valores y variam ↷
var my_gradient = context.createLinearGradient(0, 0, 0, 225);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");
context.fillStyle = my_gradient;
context.fillRect(0, 0, 300, 225);
E esse é o resultado:
Você também pode criar degradê na diagonal.
ambos valores x e y variam ↷
var my_gradient = context.createLinearGradient(0, 0, 300, 225);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");
context.fillStyle = my_gradient;
context.fillRect(0, 0, 300, 225);
E esse é o resultado:
❧
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
---|---|---|---|---|---|---|
7.0+* | 3.0+ | 3.0+ | 3.0+ | 10.0+ | 1.0+ | 1.0+ |
* Internet Explorer 7 e 8 necessitam da biblioteca
explorercanvas. Internet Explorer 9 suporta <canvas> imagens
nativamente.
|
Aqui está um gato:
⇜ Um elemento <img>
Aqui está o mesmo gato, só que desenhado em um canvas:
Um elemento <canvas> ⇝
O contexto de desenho do canvas define o método
drawImage()
para desenhar uma imagem no canvas. O método pode
ter três, cinco ou nove argumentos.
drawImage(image, dx, dy)
pega uma imagem e a desenha no
canvas. A coordenada (dx, dy)
será o canto superior
esquerdo da imagem. Coordenadas (0, 0)
devem desenhar a
imagem no canto superior esquerdo do canvas.
drawImage(image, dx, dy, dw, dh)
pega uma imagem, escala
para a largura de dw
e altura de dh
, e a
desenha no canvas nas coordenadas (dx, dy)
.
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
pega uma
imagem, ajusta ela para o retângulo (sx, sy, sw, sh)
,
escala para as dimensões (dw, dh)
, e a desenha no canvas
nas coordenadas (dx, dy)
.
A especificação da HTML5
explica os parâmetros de drawImage()
:
O retângulo de origem é o retângulo [no interior da imagem fonte] cujos cantos são os quatro pontos
(sx, sy)
,(sx+sw, sy)
,(sx+sw, sy+sh)
,(sx, sy+sh)
.O retângulo de destino é o retângulo [no interior do canvas] cujos cantos são os quatro pontos
(dx, dy)
,(dx+dw, dy)
,(dx+dw, dy+dh)
,(dx, dy+dh)
.
Para desenhar uma imagem no canvas, você precisa de uma imagem. A imagem
pode ser um elemento <img>
existente, ou pode ser
criada com o objeto Image()
do JavaScript. De qualquer
maneira, você precisa garantir que a imagem está completamente carregada
antes de desenhá-la no canvas.
Se você está usando um elemento <img>
existente, você
pode seguramente desenhá-la no canvas durante o evento
window.onload
.
↶ usando um elemento <img>
<img id="cat" src="images/cat.png" alt="sleeping cat" width="177" height="113">
<canvas id="e" width="177" height="113"></canvas>
<script>
window.onload = function() {
var canvas = document.getElementById("e");
var context = canvas.getContext("2d");
var cat = document.getElementById("cat");
context.drawImage(cat, 0, 0);
};
</script>
Se você está criando o objeto de imagem inteiramente no JavaScript, você
pode seguramente desenhar a imagem no canvas durante o evento
Image.onload
.
usando um objeto Image() ↷
<canvas id="e" width="177" height="113"></canvas>
<script>
var canvas = document.getElementById("e");
var context = canvas.getContext("2d");
var cat = new Image();
cat.src = "images/cat.png";
cat.onload = function() {
context.drawImage(cat, 0, 0);
};
</script>
O 3º e 4º parâmetros opcionais do método
drawImage()
controlam a escala da imagem. Essa é a mesma
imagem, escalada pela metade de sua largura e altura e desenhada
repetidamente em diferentes coordenadas no mesmo canvas.
Aqui está o script que produz o efeito “multicat”:
cat.onload = function() {
for (var x = 0, y = 0;
x < 500 && y < 375;
x += 50, y += 37) {
context.drawImage(cat, x, y, 88, 56);
}
};
⇜ Escala a imagem
Todo esse esforço levanta a seguinte questão: por que você deveria
desenhar uma imagem dentro de um canvas em primeiro lugar? O que faz com
que a complexidade extra de uma imagem em um canvas ofereça em cima de um
elemento <img>
e algumas regras CSS? Até
mesmo o efeito “multicat” pode ser replicado com 10 elementos
<img>
sobrepostos.
A resposta simples é, pelo mesmo motivo que você iria querer
desenhar um texto no canvas. O
diagrama de coordenadas do canvas inclui texto,
linhas, e formas; o texto no canvas é só uma parte de um grande trabalho.
Um diagrama mais complexo poderia facilmente utilizar o
drawImage()
para incluir ícones, sprites, e outros gráficos.
❧
Versões do Internet Explorer antes de 9.0 não suportam a
API de canvas. (IE9
suporta completamente a API
de canvas.) Entretanto, essas versões antigas do Internet Explorer
suportam sim uma tecnologia proprietária da Microsoft chamada
VML, na qual pode fazer muitas das coisas que o elemento
<canvas>
faz. E assim, nasceu o
excanvas.js
.
Explorercanvas
(excanvas.js
) é uma biblioteca JavaScript de código livre,
licenciada pela Apache que implementa a API de canvas no
Internet Explorer. Para utilizá-la, inclua o seguinte elemento
<script>
no topo da sua página.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Dive Into HTML5</title>
<!--[if lt IE 9]>
<script src="excanvas.js"></script>
<![endif]-->
</head>
<body>
...
</body>
</html>
O <!--[if lt IE 9]>
e
<![endif]-->
são
comentários condicionais. Internet Explorer interpreta eles como uma declaração if
:
“se o navegador é uma versão do Internet Explorer inferior da (mas não
inclusa) versão 9, então execute o bloco.” Cada outro navegador vai tratar
o bloco inteiro como um bloco de comentário HTML. O resultado
na prática é que o Internet Explorer 7 e 8 irão realizar o download do
script excanvas.js
e executá-los, mas outros navegadores irão
ignorar o script (não realizando o download dele, não executando ele, não
fazendo nada). Isso irá fazer com que sua página carregue mais rapidamente
para aqueles navegadores que implementem a API de canvas
nativamente.
Uma vez incluindo o excanvas.js
no
<head>
da sua página, você não precisa fazer nada mais
para acomodar o Internet Explorer. Só inclua os elementos
<canvas>
na sua marcação, ou crie eles dinâmicamente
com JavaScript. Siga as instruções desse capítulo para capturar o contexto
de desenho do elemento <canvas>
, e você poderá desenhar
formas, texto, e padrões.
Bem… não exatamente. Existem algumas limitações:
Há mais uma advertência sobre o uso de excanvas.js
, e é um
problema que passei enquanto criava o exemplo desse capítulo.
ExplorerCanvas inicializa sua própria interface de falso-canvas
automaticamente uma vez incluindo o script excanvas.js
na sua
página HTML. Mas isso não significa que o Internet Explorer
está pronto para usá-lo imediatamente. Em algumas situações, você pode
rodar sob uma condição onde a interface do falso-canvas está
quase, mas ainda não, pronta para ser usada. O principal sintoma
desse estado é que o Internet Explorer irá reclamar que o “objeto não suporta essa propriedade ou método” sempre que você tentar fazer qualquer coisa com o elemento
<canvas>
, como pegar seu contexto de desenho.
A solução mais simples para fazer é adiar todas as suas manipulações
relacionadas ao canvas até depois que evento onload
seja
disparado. Isto pode levar um tempo — se a sua página possui muitas
imagens ou vídeos, elas irão atrasar o onload
— mas irá dar
ao ExplorerCanvas tempo para trabalhar na sua mágica.
❧
Halma é um jogo de tabuleiro com séculos de idade. Muitas variações existem. Nesse exemplo, criei uma versão solitária de Halma com 9 peças em um tabuleiro 9 × 9. No início do jogo, as peças formam um quadrado 3 × 3 no canto interior esquerdo do tabuleiro. O objetivo do jogo é mover todas as peças até que formem um quadrado 3 × 3 no canto superior direito do tabuleiro, com o menor número de movimentos.
Existem dois tipos de movimentos legais no Halma:
Aqui está o jogo propriamente dito. Você também pode jogá-lo em uma página separada se você quiser cutucar seu developer tools.
Moves: 0
Como isso funciona? Estou feliz que você perguntou. Não irei mostrar todo código aqui. (Você pode vê-lo em diveintohtml5.com.br/examples/halma.js.) Na verdade irei pular a maioria do código de jogabilidade, mas quero ressaltar algumas partes do código que realmente mexem o desenho no canvas e a resposta aos cliques de mouse no elemento canvas.
Durante o carregamento da página, nós inicializamos o jogo ao definir as
dimensões no <canvas>
e guardamos as referências no seu
contexto de desenho.
gCanvasElement.width = kPixelWidth;
gCanvasElement.height = kPixelHeight;
gDrawingContext = gCanvasElement.getContext("2d");
Depois fazemos algo que você ainda não viu: nós adicionamos uma escuta de
evento ao elemento <canvas>
para ouvir por eventos de
clique.
gCanvasElement.addEventListener("click", halmaOnClick, false);
A função halmaOnClick()
é chamada quando o usuário clica em
qualquer lugar no canvas. Seu argumento é um objeto
MouseEvent
que contém as informações sobre onde o usuário
clicou.
function halmaOnClick(e) {
var cell = getCursorPosition(e);
// o resto é só lógica do jogo
for (var i = 0; i < gNumPieces; i++) {
if ((gPieces[i].row == cell.row) &&
(gPieces[i].column == cell.column)) {
clickOnPiece(i);
return;
}
}
clickOnEmptyCell(cell);
}
O próximo passo é pegar o objeto MouseEvent
e calcular em
qual quadrado no tabuleiro Halma acaba de ser clicado. O tabuleiro Halma
contempla todo o canvas, ou seja cada clique em qualquer lugar do
tabuleiro. Nós só precisamos descobrir onde. Isto é complicado, porque
eventos de mouse são implementados diferentemente em quase todo navegador.
function getCursorPosition(e) {
var x;
var y;
if (e.pageX != undefined && e.pageY != undefined) {
x = e.pageX;
y = e.pageY;
}
else {
x = e.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
Nesse ponto, nós temos as coordenadas x
e y
que
são relativas ao documento (isso é, a página HTML inteira).
Isso não é muito útil ainda. Nós queremos as coordenadas relativas ao
canvas.
x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;
Agora nós temos as coordenadas x
e y
que são
relativas ao canvas. Isso é, se
x
é 0 e y
é 0 nesse ponto, nós sabemos que o
usuário acabou de clicar no pixel do canto superior esquerdo do canvas.
Daqui em diante, nós podemos calcular em qual quadrado do Halma o usuário clicou, e então agir de acordo com isso.
var cell = new Cell(Math.floor(y/kPieceHeight),
Math.floor(x/kPieceWidth));
return cell;
}
Ufa! Eventos de mouse são difíceis. Mas você pode usar a mesma lógica (na verdade, o exato mesmo código) em todas as suas aplicações baseadas em canvas. Lembre-se: mouse click → coordenadas relativas ao documento → coordenadas relativas ao canvas → código específico de aplicação.
OK, vamos olhar para a rotina principal do desenho. Porque os gráficos são simples, escolhi limpar e redesenhar o tabuleiro em cada hora que houver qualquer mudança no jogo. Isso não é estritamente necessário. O contexto de desenho do canvas irá reter qualquer coisa que você tenha desenhado anteriormente nele, até mesmo se o usuário realizar scroll para fora do campo de visão ou mudar de uma aba para outra e depois voltar. Se você está desenvolvendo uma aplicação baseada em canvas com gráficos mais complicados (como um jogo de arcade), você pode otimizar a performance ao identificar quais regiões do canvas estão “sujas” e redesenhar apenas as regiões sujas. Mas isso está fora do escopo desse livro.
gDrawingContext.clearRect(0, 0, kPixelWidth, kPixelHeight);
A rotina de desenho do tabuleiro deve lhe parecer familiar. É similar como nós desenhamos o digrama de coordenadas do canvas anteriormmente nesse capítulo.
gDrawingContext.beginPath();
/* linhas verticais */
for (var x = 0; x <= kPixelWidth; x += kPieceWidth) {
gDrawingContext.moveTo(0.5 + x, 0);
gDrawingContext.lineTo(0.5 + x, kPixelHeight);
}
/* linhas horizontais */
for (var y = 0; y <= kPixelHeight; y += kPieceHeight) {
gDrawingContext.moveTo(0, 0.5 + y);
gDrawingContext.lineTo(kPixelWidth, 0.5 + y);
}
/* desenhe! */
gDrawingContext.strokeStyle = "#ccc";
gDrawingContext.stroke();
A brincadeira começa de verdade quando nós vamos desenhar cada peça
individual. Uma peça é um círculo, algo que ainda não desenhamos antes.
Além disso, se o usuário seleciona a peça em antecipação ao seu movimento,
nós queremos desenhar a peça em forma de um círculo preenchido. Aqui, o
argumento p
representa a peça, na qual possui as propriedades
de linha
e coluna
que denotam a posição atual da
peça no tabuleiro. Nós usamos algumas constantes do jogo para traduzir
(coluna, linha)
em coordenadas (x, y)
relativas
ao canvas, então desenhar o círculo, e (se a peça for selecionada)
preencher o círculo com uma cor sólida.
function drawPiece(p, selected) {
var column = p.column;
var row = p.row;
var x = (column * kPieceWidth) + (kPieceWidth/2);
var y = (row * kPieceHeight) + (kPieceHeight/2);
var radius = (kPieceWidth/2) - (kPieceWidth/10);
Esse é o fim da lógica específica do jogo. Agora nós temos as coordenadas
(x, y)
, relativas ao canvas, para o centro do círculo que
queremos desenhar. Não há um método circle()
na
API do canvas, mas há um método arc()
. E
realmente, o que é um círculo se não um arco que faz uma volta completa?
Você lembra de geometria básica? O método arc()
pega o ponto
central (x, y)
, o raio, o ângulo inicial e final (em
radianos), e a direção (false
para sentido horário,
true
para sentido anti-horário). Você pode usar o módulo
Math
que está dentro do JavaScript para calcular radianos.
gDrawingContext.beginPath();
gDrawingContext.arc(x, y, radius, 0, Math.PI * 2, false);
gDrawingContext.closePath();
Mas espera aí! Nada foi desenhado ainda. Como moveTo()
e
lineTo
, o método arc()
é um método
“lápis”. Para realmente desenhar o círculo, nós
precisamos definir o strokeStyle
e chamar o
stroke()
para traçar a “tinta.”
gDrawingContext.strokeStyle = "#000";
gDrawingContext.stroke();
E se a peça estiver selecionada? Nós podemos reutilizar o mesmo caminho que criamos para desenhar o contorno da peça, para preencher o círculo com uma cor sólida.
if (selected) {
gDrawingContext.fillStyle = "#000";
gDrawingContext.fill();
}
E isso é… bem, isso é tudo. O resto do programa é lógica específica do
jogo — distinguindo entre movimentos válidos e inválidos, registrando o
número de movimentos, detectando se o jogo está terminado. Com 9 círculos,
algumas linhas, e 1 onclick
handler, nós criamos um jogo
inteiramente em <canvas>
. Huzzah!
❧
canvas
— o básico, por Mihai Sucan
canvas
canvas
no rascunho da especificação do HTML5
canvas
❧
Isso foi “Vamos Chamá-lo (Superfície) De Desenho.” Consulte o Sumário, caso queira continuar com a leitura.
Em associação a Google Press, O’Reilly está distribuindo este livro em variados formatos, incluindo papel, ePub, Mobi, DRM-free e PDF. A edição paga é chamada “HTML5: Up & Running” e está disponível agora. Este capítulo está incluído na versão paga.
Se você gostou deste capítulo e quer mostrar sua apreciação, basta comprar o livro “HTML5: Up & Running” com esse link afiliado ou comprar a edição eletrônica diretamente da O’Reilly. Você vai ganhar um livro, e eu vou ganhar um trocado. Atualmente, não aceito doações diretas.
Copyright MMIX–MMXI Mark Pilgrim