Estava eu aqui no meu atual trabalho, com toda a minha parte feita, esperando os chefes me passarem mais trabalho (continuo assim), até que resolvi brincar um pouco. Como estou revoltado por aqui não ter nenhum compilador para linguagem nenhuma, excetuando o j2sdk que por algum motivo está com problemas na Virtual Machine, então o jeito foi brincar com DHTML.
Como adoro animações gráficas e meus recentes estudos têm sido em cima da área de desenvolvimento de jogos, e como qualquer biblioteca de animação gráfica tem sua base em funções que desenham na tela, resolvi começar uma biblioteca assim em DHTML.
Comecei a escrever uma simples classe que tem duas funções básicas que, com elas, dá para se fazer qualquer desenho: função que faz um ponto e uma função que faz uma reta. A partir da necessidade, criei outra função para escrever na tela, e outra para fazer uma linha tracejada, para brincar com os exemplos que eu fazia. Aqui eu vou mostrar um exemplo de relógio, que não vai usar a linha tracejada, mas eu deixei ela ainda assim no código. A classe é uma classe que eu chamei de graficos. Vamos a ela:
function graficos(janela) { this.wnd=janela; this.htm=""; this.texto=function(txt,x,y,fonte,cor) {this.htm+='<div style="position:absolute;left:'+x+';top:'+y+';overflow:hidden;font:'+fonte+';color:'+cor+'">'+txt+'</div>'} this.ponto=function(x,y,w,h,cor) {this.htm+='<div style="position:absolute;left:'+x+';top:'+y+';width:'+w+';height:'+h+';overflow:hidden;background-color:'+cor+'"></div>'} this.reta=function(x1,y1,x2,y2,cor) { var dx=x2-x1, dy=y2-y1; if(Math.abs(dx)>Math.abs(dy)) for(var i=x1,j=y1;i<=x2;i++,j+=dy/dx) this.ponto(i,j,1,1,cor); else for(var i=x1,j=y1;j<=y2;j++,i+=dx/dy) this.ponto(i,j,1,1,cor); if(Math.abs(dx)>Math.abs(dy)) for(var i=x1,j=y1;i>=x2;i--,j+=dy/dx*-1) this.ponto(i,j,1,1,cor); else for(var i=x1,j=y1;j>=y2;j--,i+=dx/dy*-1) this.ponto(i,j,1,1,cor); } this.clear=function() {this.htm=''} this.draw=function() {this.wnd.innerHTML=this.htm} }
Esta é a classe. Minha idéia inicial não era fazer com que vocês entendessem todo o funcionamento desta classe. Para isto eu teria que ficar aqui escrevendo por duas horas sobre geometria analítica pra explicar a função que desenha uma reta. É óbvio que se vocês quiserem aprender a fazer uma reta, terei o maior prazer em explicá-los, mas não será o objetivo deste tópico. Qualquer coisa, abram outro ou perguntem aqui.
Vamos aos detalhes da classe. Ela possui duas variáveis de propriedade: wnd e htm. A primeira propriedade é onde guardamos uma referência para o objeto dentro da página onde iremos desenhar dentro dele. No meu exemplo, usarei um <div> com borda. Mas como eu já expliquei num tópico anterior, qualquer container poderia fazer este papel (tabela, span, iframe, etc). A segunda propriedade, htm, é o código HTML que fará todo o desenho. Se chamarmos o método clear() desta classe, limpamos a variável htm. E se chamarmos o método draw() desta classe, fazemos com que o código HTML dentro do container seja a variável htm.
Para fins de fácil explicação, chamarei nosso alvo de desenho de JANELA (a propriedade wnd da classe). Portanto, se quisermos desenhar um ponto de 1 pixel de largura por um pixel de altura na nossa JANELA, basta chamar o método com a posição X e Y do ponto dentro da JANELA, a largura e a altura 1 e a cor desejada. O que o método ponto() faz? Simplesmente acrescenta à propriedade htm um DIV de dimensões WxH (largura x altura, que no meu exemplo de ponto será 1x1) sem nada dentro desse DIV, e a cor de fundo deste DIV é a cor escolhida. Para que isto funcione como desejado, existem alguns detalhes a serem seguidos: no Internet Explorer, o DIV não aparece na tela se não colocar a propriedade OVERFLOW como HIDDEN. Para quem não conhece, OVERFLOW é se vai ou não ter barra de rolagem. Como nosso DIV será muito pequeno e como a função de um DIV é guardar conteúdo, por padrão esse pequeno DIV entraria com uma barra de rolagem invisível para nós, mas que simplesmente não deixa a cor de fundo aparecer.
O método texto() desta classe é um método simples, que também adiciona um DIV com o texto específico.
Um detalhe desta classe é que só dá para fazer o desenho realmente aparecer na JANELA quando chamarmos o método DRAW. Isto é para fins de eficiência de processamento. Para se desenhar uma reta, por exemplo, sucessivos pontos são feitos na JANELA. Se toda vez que um ponto fosse adicionado eu enviaria o código HTML para a JANELA, muito processamento seria consumido. Então ao desenhar um ponto (chamar o método ponto()), eu simplesmente adiciono o código à variável htm. Só no final do desenho é que eu chamo o método draw().
Vamos exemplificar um uso desta classe:
<html><head><title>Exemplo de desenho</title> <script language=JavaScript> function graficos(janela) { this.wnd=janela; this.htm=""; this.texto=function(txt,x,y,fonte,cor) {this.htm+='<div style="position:absolute;left:'+x+';top:'+y+';overflow:hidden;font:'+fonte+';color:'+cor+'">'+txt+'</div>'} this.ponto=function(x,y,w,h,cor) {this.htm+='<div style="position:absolute;left:'+x+';top:'+y+';width:'+w+';height:'+h+';overflow:hidden;background-color:'+cor+'"></div>'} this.reta=function(x1,y1,x2,y2,cor) { var dx=x2-x1, dy=y2-y1; if(Math.abs(dx)>Math.abs(dy)) for(var i=x1,j=y1;i<=x2;i++,j+=dy/dx) this.ponto(i,j,1,1,cor); else for(var i=x1,j=y1;j<=y2;j++,i+=dx/dy) this.ponto(i,j,1,1,cor); if(Math.abs(dx)>Math.abs(dy)) for(var i=x1,j=y1;i>=x2;i--,j+=dy/dx*-1) this.ponto(i,j,1,1,cor); else for(var i=x1,j=y1;j>=y2;j--,i+=dx/dy*-1) this.ponto(i,j,1,1,cor); } this.clear=function() {this.htm=''} this.draw=function() {this.wnd.innerHTML=this.htm} } function desenha(onde) { var g=new graficos(onde); g.ponto(10,10,1,1,'black'); g.draw(); } </script></head> <body onLoad=desenha(this.alvo)> <div id=alvo style="position:relative;border:1 solid black;width:500;height:400"></div> </body></html>
Emocionante, né? Fizemos um ponto! Hehehehe... Era só pra mostrar como se faz uso desta classe. Agora vamos a um exemplo um pouco melhorzinho. Vou desenhar um relógio analógico que é atualizado de segundo em segundo.
<html><head><title>Teste</title> <script language=JavaScript> function graf(janela) { this.wnd=janela; this.htm=""; this.texto=function(txt,x,y,fonte,cor) {this.htm+='<div style="position:absolute;left:'+x+';top:'+y+';overflow:hidden;font:'+fonte+';color:'+cor+'">'+txt+'</div>'} this.ponto=function(x,y,w,h,cor) {this.htm+='<div style="position:absolute;left:'+x+';top:'+y+';width:'+w+';height:'+h+';overflow:hidden;background-color:'+cor+'"></div>'} this.reta=function(x1,y1,x2,y2,cor) { var dx=x2-x1, dy=y2-y1; if(Math.abs(dx)>Math.abs(dy)) for(var i=x1,j=y1;i<=x2;i++,j+=dy/dx) this.ponto(i,j,1,1,cor); else for(var i=x1,j=y1;j<=y2;j++,i+=dx/dy) this.ponto(i,j,1,1,cor); if(Math.abs(dx)>Math.abs(dy)) for(var i=x1,j=y1;i>=x2;i--,j+=dy/dx*-1) this.ponto(i,j,1,1,cor); else for(var i=x1,j=y1;j>=y2;j--,i+=dx/dy*-1) this.ponto(i,j,1,1,cor); } this.clear=function() {this.htm=''} this.draw=function() {this.wnd.innerHTML=this.htm} } var g; function roda(x) {g=new graf(x);clock()} function clock() { var t=new Date(); var s=t.getSeconds(); var m=t.getMinutes(); var h=t.getHours(); g.clear(); for(var i=0,j=1;i<2*Math.PI;i+=Math.PI/6,j++) g.texto(j,187-50*Math.cos(i+(4/6)*Math.PI),169-50*Math.sin(i+(4/6)*Math.PI),'11 arial','black'); for(var i=0;i<2*Math.PI;i+=0.01) g.ponto(190+60*Math.cos(i),175-60*Math.sin(i),1,1,'black'); g.reta(190,175,190-40*Math.cos(Math.PI/30*s+Math.PI/2),175-40*Math.sin(Math.PI/30*s+Math.PI/2),'red'); g.reta(190,175,190-35*Math.cos(Math.PI/30*m+Math.PI/2),175-35*Math.sin(Math.PI/30*m+Math.PI/2),'black'); g.reta(190,175,190-25*Math.cos(Math.PI/6*h+Math.PI/2),175-40*Math.sin(Math.PI/6*h+Math.PI/2),'black'); g.texto(h+':'+m+':'+s,10,10,'black'); g.draw(); setTimeout("clock()", 1000); } </script></head> <body onLoad=roda(this.quadro)> <div id=quadro style="border:1 solid black;width:400;height:350;position:relative"></div> </body></html>
Vale dizer que este exemplo envolve MUITO processamento por segundo. Aqui nesta máquina que eu estou no trabalho, que é um P4 2.66Ghz, roda numa boa. Mas se em sua máquina ficar lento ou travando o navegador, retire só a parte que cria a circunferência em torno do relógio. Basta arrancar estas linhas:
for(var i=0;i<2*Math.PI;i+=0.01)
g.ponto(190+60*Math.cos(i),175-60*Math.sin(i),1,1,'black');
Isto retira 728 instruções de processamento por segundo. Pouca coisa, né?
Estou editando agora, e resolvi aprimorar um pouco o processamento e o visual do meu relógio. Dêem uma olhada:
<html><head><title>Teste</title> <script language=JavaScript> function graf(janela) { this.wnd=janela; this.htm=""; this.texto=function(txt,x,y,fonte,cor) {this.htm+='<div style="position:absolute;left:'+x+';top:'+y+';overflow:hidden;font:'+fonte+';color:'+cor+'">'+txt+'</div>'} this.ponto=function(x,y,w,h,cor) {this.htm+='<div style="position:absolute;left:'+x+';top:'+y+';width:'+w+';height:'+h+';overflow:hidden;background-color:'+cor+'"></div>'} this.reta=function(x1,y1,x2,y2,cor) { var dx=x2-x1, dy=y2-y1; if(Math.abs(dx)>Math.abs(dy)) for(var i=x1,j=y1;i<=x2;i++,j+=dy/dx) this.ponto(i,j,1,1,cor); else for(var i=x1,j=y1;j<=y2;j++,i+=dx/dy) this.ponto(i,j,1,1,cor); if(Math.abs(dx)>Math.abs(dy)) for(var i=x1,j=y1;i>=x2;i--,j+=dy/dx*-1) this.ponto(i,j,1,1,cor); else for(var i=x1,j=y1;j>=y2;j--,i+=dx/dy*-1) this.ponto(i,j,1,1,cor); } this.clear=function() {this.htm=''} this.draw=function() {this.wnd.innerHTML=this.htm} } var g; function roda(x) {g=new graf(x);clock()} function clock() { var t=new Date(); var s=t.getSeconds(); var m=t.getMinutes(); var h=t.getHours(); g.clear(); for(var i=0,j=1;i<2*Math.PI;i+=Math.PI/6,j++) { g.texto(j,187-50*Math.cos(i+(4/6)*Math.PI),169-50*Math.sin(i+(4/6)*Math.PI),'11 arial','black'); } for(var i=0,j=0;i<2*Math.PI;i+=Math.PI/30,j++) { var l=(j%5==0)?2:1; g.ponto(190+60*Math.cos(i),175-60*Math.sin(i),l,l,'black'); } g.reta(190,175,190-40*Math.cos(Math.PI/30*s+Math.PI/2),175-40*Math.sin(Math.PI/30*s+Math.PI/2),'red'); g.reta(190,175,190-35*Math.cos(Math.PI/30*m+Math.PI/2),175-35*Math.sin(Math.PI/30*m+Math.PI/2),'black'); g.reta(190,175,190-25*Math.cos(Math.PI/6*h+Math.PI/2),175-40*Math.sin(Math.PI/6*h+Math.PI/2),'black'); g.texto(h+':'+m+':'+s,10,10,'black'); g.draw(); setTimeout("clock()", 1000); } </script></head> <body onLoad=roda(this.quadro)> <div id=quadro style="border:1 solid black;width:400;height:350;position:relative"></div> </body></html>
Espero que tenham gostado!
Aqui é o início da criação de um jogo em DHTML. A ferramenta básica nós já temos. Como já falei anteriormente, dá para se fazer quase qualquer coisa com DHTML, excetuando rotação de objetos, que é um recurso que nos impossibilita de fazer muitas coisas. Claro que ainda existem outros contras, mas meu intuito aqui é estimulá-los a aprender esta magnífica ferramenta para que você possam praticamente dominar assuntos relacionados à web.
Qualquer dúvida, perguntem!
Um abraço,
Thales Medeiros.
Edição feita por: Thales Medeiros, 17/12/2004, 16:25.