Category Archives: Sem categoria

Javascript orientado a objetos – II

No  artigo anterior vimos como criar classes em JS, incluindo propriedades e métodos públicos, privilegiados e privados. Este artigo explica a herança em Javascript.

Sumário

  • É possível estabelecer uma relação de herança entre classes com NomeCLasseFilha.prototype = new ClassePai();.
  • É necessário fazer reset da propriedade constructor usando NomeClasseFilha.prototype.constructor=NomeClasseFilha.
  • Podem invocar-se métodos das classes ancestrais que a classe filha tenha modificado usando o método Function.call().
  • O Javascript não tem suporte para métodos protegidos.

Exemplo

Segue-se um exemplo de herança entre duas classes:

function Mamifero(nome){
	this.nome=nome;
	this.descendencia=[];
}
Mamifero.prototype.temUmFilho=function(){
	var novoFilho=new Mamifero("Filho "+this.nome);
	this.descendencia.push(novoFilho);
	return novoFilho;
}
Mamifero.prototype.toString=function(){
	return '[Mamifero "'+this.nome+'"]';
}


Gato.prototype = new Mamifero();       // É aqui que a herança acontece
Gato.prototype.constructor=Gato;       // Caso contrário as instâncias de Gato usariam o construtor de Mamifero
function Gato(nome){
	this.nome=nome;
}
Gato.prototype.toString=function(){
	return '[Gato "'+this.nome+'"]';
}


var umAnimal = new Mamifero('Sr. Biggles');
var bicho = new Gato('Felix');
alert('umAnimal is '+umAnimal);        // resulta em 'umAnimal é [Mamifero "Sr. Biggles"]'
alert('bicho is '+bicho);              // resulta em 'bicho é [Gato "Felix"]'

bicho.temUmFilho();                    // chama um método herdado de Mamifero
alert(bicho.descendencia.length);      // mostra que o gato agora tem um filho
alert(bicho.descendencia[0]);          // resulta em '[Mamifero "Filho Felix"]'

Utilização da propriedade .constructor

Atente-se na última linha do exemplo anterior. O filho de um Gato deve ser um Gato, certo? Embora o método temUmFilho() funcione, esse método cria especificamente um new Mamifero. Embora pudéssemos criar um novo método temUmFilho() para a subclasse Gato como this.descendencia.push(new Gato("Filho "+this.nome)), seria preferível que a classe ancestral criasse um objeto do tipo correto.

Qualquer instância ou objeto em JS tem uma propriedade chamada constructor que aponta para a sua classe pai. Por exemplo, umAnimal.constructor==Mamifero é verdade. Com este conhecimento, podemos rescrever o método temUmFilho() assim:

Mamifero.prototype.temUmFilho=function(){
	var novoFilho=new this.constructor("Filho "+this.nome);
	this.descendencia.push(novoFilho);
	return novoFilho;
}
...
bicho.temUmFilho();                    // O mesmo que antes: chama o método herdado de Mamifero
alert(bicho.descendencia[0]);          // Agora resulta em '[Gato "Filho Felix"]'

Invocar ‘super’ métodos

Vamos estender o exemplo para quando os gatinhos filhos são criados, eles ‘miam’ logo assim que nascem. Para isso, vai ser necessário escrever um método próprio Gato.prototype.temUmFilho(), que seja capaz de invocar o método original Mamifero.prototype.temUmFilho():

Gato.prototype.temUmFilho=function(){
	Mamifero.prototype.temUmFilho.call(this);
	alert("miau!");
}

O código acima pode parecer bizarro. O Javascript não tem a prorpiedade ‘super’, que aponte para a classe pai. Ao invés, usa-se o método call() de uma Function do objeto, que permite invocar uma função usando um objeto diferente como contexto para essa função. Se for necessário passar parâmetros para essa função, vão após o ‘this’. Para mais informações sobre o método Function.call(), ver MSDN docs for call().

Criar uma propriedade ‘super’

Em vez de ter que saber que Gato herda de Mamifero, e ter que escrever Mamifero.prototype de cada vez que quisermos invocar um método ancestral, não seria mais interessante ter uma propriedade de gato para a sua superclasse? Os mais familiarizados com linguagens orientadas a objetos seriam tentados a chamar a essa propriedade ‘super’, mas o JS reserva esta palavra para uso futuro. A palavra ‘parent’, embora seja usada nalguns itens do DOM, pode ser usada livremente em JS, portanto vamos usar parent neste exemplo:

Gato.prototype = new Mamifero();
Gato.prototype.constructor=Gato;
Gato.prototype.parent = Mamifero.prototype;
...
Gato.prototype.temUmFilho=function(){
	var osFilhos = this.parent.temUmFilho.call(this);
	alert("miau!");
	return osFilhos;
}

Falsas classes virtuais puras

Algumas linguagens de programação orientadas a objetos têm o conceito de classe virtual pura, que é uma classe que não pode ser instanciada, mas apenas herdada. Por exemplo, podemos ter a classe CoisaViva da qual a classe Mamifero herda, mas não queremos que seja possível instanciar CoisaViva sem especificar o tipo de coisa. É possível fazer isto em JS criando a classe virtual a partir de um objeto ao invés de uma função.

O exemplo seguinte mostra como isto pode ser usado para simular uma superclasse virtual pura:

CoisaViva = {
	nascer : function(){
		this.vivo=true;
	}
}
...
Mamifero.prototype = CoisaViva;
Mamifero.prototype.parent = CoisaViva;   //Nota: não 'CoisaViva.prototype'
Mamifero.prototype.temUmFilho=function(){
	this.parent.nascer.call(this);
	var novoFilho=new this.constructor("Filho "+this.nome);
	this.descendencia.push(novoFilho);
	return novoFilho;
}

Com o código acima,, a instrução seguinte var espirito = new CoisaViva() daria erro, pois CoisaViva não é uma função, e por isso não pode ser usada como um construtor.

Herança conveniente

Ao invés de escrever 3 linhas de cada vez que queremos que uma classe herde de outra, é mais conveniente esteder o objeto Function para fazer isso por nós:

Function.prototype.herdaDe = function( objetoOuClassePai ){
	if ( objetoOuClassePai.constructor == Function )
	{
		//Herança normal
		this.prototype = new objetoOuClassePai;
		this.prototype.constructor = this;
		this.prototype.parent = objetoOuClassePai.prototype;
	}
	else
	{
		//Herança pura virtual
		this.prototype = objetoOuClassePai;
		this.prototype.constructor = this;
		this.prototype.parent = objetoOuClassePai;
	}
	return this;
}
//
//
CoisaViva = {
	beBorn : function(){
		this.vivo = true;
	}
}
//
//
function Mamifero(nome){
	this.nome=nome;
	this.descendencia=[];
}
Mamifero.herdaDe( CoisaViva );
Mamifero.prototype.temUmFilho=function(){
	this.parent.nascer.call(this);
	var novoFilho = new this.constructor( "Filho " + this.nome );
	this.descendencia.push(novoFilho);
	return novoFilho;
}
//
//
function Gato( nome ){
	this.nome=nome;
}
Gato.herdaDe( Mamifero );
Gato.prototype.temUmFilho=function(){
	var osGatos = this.parent.temUmFilho.call(this);
	alert("miau!");
	return osGatos;
}
Gato.prototype.toString=function(){
	return '[Gato "'+this.nome+'"]';
}
//
//
var felix = new Gato( "Felix" );
var gatos = felix.temUmFilho( ); // mew!
alert( kitten );                 // [Gato "Filho Felix"]

Este método deve ser chamado imediatamente após o construtor, e antes de estender o protótipo para o objeto.

Métodos protegidos?

Algumas linguagens de programação orientadas a objetos têm o conceito de métodos protegidos — métodos que existem numa classe pai ou ancestral e que só podem ser invocados por descendentes do objeto, mas não por objetos externos. O JS não suporta estas funcionalidades. Quem precisar disto, terá que escrever a sua própria framework, garantindo que cada classe tem um ‘parent’ ou uma propriedade semelhante, e sobe a árvore à procura de ancestrais, verificando se o objeto invocador é do mesmo tipo. Fazível, mas não agradável.

Traduzido de OOP in JS – 2

Javascript orientado a objetos – I

JS orientado a objetos, Parte 1 : variáveis e métodos públicos e privados

Neste artigo mostramos como criar variáveis e métodos públicos e privados em classes de Javascript.

Sumário

  • variáveis privadas são declaradas com a palavra reservada ‘var’ dentro do objeto, e só podem ser acedidas por funções privadas e métodos privilegiados.
  • funções privadas são declaradas em linha dentro do construtor do objeto (ou alternativamente podem ser declaradas via var nomeFuncao=function(){...}) e só podem ser chamadas por métodos privilegiados (incluindo o construtor do objeto).
  • métodos privilegiados são declarados com this.nomeMetodo=function(){...} e podem ser invocados por código externo ao objeto.
  • propriedades públicas são declaradas com this.nomeVariavel e podem ser lidas e escritas de fora do objeto.
  • métodos públicos são definidos por NomeClasse.prototype.nomeMetodo = function(){...} e podem ser invocados de fora do objeto.
  • propriedades do protótipo são definidas por NomeClasse.prototype.nomePropriedade = valor
  • propriedades estáticas são definidas por NomeClasse.nomePropriedade = valor

Exemplo

Neste exemplo, o nome e cor do cabelo da pessoa são definidos no início e não podem ser alterados. Uma pessoa é criada no ano 1 e com uma idade máxima escondida. A pessoa tem um peso que é modificado pelo que come (triplicando o seu peso) ou pelo exercício (diminuindo para metade). De cada vez que a pessoa come ou exercita, a sua idade aumenta uma ano. O objeto pessoa tem uma propriedade ‘roupa’ que qualquer um pode modificar, assim como um fatorLixo que qualquer um pode modificar manualmente, mas que cresce sempre que a pessoa come ou exercita, e é reduzido pelo uso do método chuveiro().

Código exemplo

function Pessoa(n,cabelo){
	this.constructor.populacao++;

	// ************************************************************************
	// Variáveis e funções privadas
	// só me´todos privilegiados podem ver/editar/invocar
	// ***********************************************************************
	var vivo=true, idade=1;
	var maxIdade=70+Math.round(Math.random()*15)+Math.round(Math.random()*15);
	function maisVelho(){ return vivo = (++idade <= maxIdade) }

	var meuNome=n?n:"Maria João";
	var peso=1;


	// ************************************************************************
	// Métodos privilegiados
	// Podem ser invocados publicamente e aceder a itens privados
	// não podem ser alterados; podem ser substituídos por versões públicas
	// ************************************************************************
	this.toString=this.getNome=function(){ return meuNome }

	this.comer=function(){
		if (maisVelho()){
			this.fatorLixo++;
			return peso*=3;
		} else alert(meuNome+" não pode comer, morreu!");
	}
	this.exercicio=function(){
		if (maisVelho()){
			this.fatorLixo++;
			return peso/=2;
		} else alert(meuNome+" não pode comer, morreu!");
	}
	this.pes=function(){ return peso; }
	this.getCor=function(){ return cor; }
	this.getIdade=function(){ return idade; }
	this.muitoTempo=function(){ idade+=50; this.fatorLixo=10; }


	// ************************************************************************
	// Propriedades públicas -- Qualquer um pode ler/escrever
	// ************************************************************************
	this.roupa="nada/nú";
	this.fatorLixo=0;
}


// ************************************************************************
// Métodos públicos -- Qualquer um pode ler/escrever
// ************************************************************************
Person.prototype.serFixe = function(){ this.roupa="cáqui e camisa preta" }
Person.prototype.chuveiro = function(){ this.fatorLixo=2 }
Person.prototype.mostraPernas = function(){ alert(this+" tem "+this.pernas+" pernas") }
Person.prototype.amputar = function(){ this.pernas-- }


// ************************************************************************
// Propriedades do protótipo -- Qualquer um pode ler/Escrever (mas podem ser reescritas)
// ************************************************************************
Person.prototype.pernas=2;


// ************************************************************************
// Propriedades estáticas -- Qualquer um pode ler/Escrever
// ************************************************************************
Person.populacao = 0;



// Aqui vai o código que usa a classe Pessoa
function GereVidaDoAlex(){
	var gk=new Pessoa("Alex","castanho");       //Nova instância da classe Pessoa
	var lk=new Pessoa("Maria","ruiva");         //Nova instância da classe Pessoa
	alert("Neste momento há "+Pessoa.populacao+" pessoas");

	gk.mostraPernas(); lk.mostraPernas();                 //Ambos partilham a variável comum 'Pessoa.prototype.pernas' quando olham para 'this.legs'

	gk.cabelo = "castanho";                               //afeta uma variável pública, mas não reescreve a variável 'cor'.
	alert("A cor do cabelo de "+gk+" é "+gk.getCor());    //Devolve 'castanho' da variável privada 'cor' criada no início..
	gk.comer(); gk.comer(); gk.comer();                   //o peso é 3...depois 9...depois 27
	alert(gk+" pesa "+gk.peso()+" quilos e tem um fator lixo de "+gk.fatorLixo);

	gk.exercicio();                                // o peso é agora 13.5
	gk.serFixe();                                  //a roupa foi atualizada para a moda atual
	gk.roupa="Roupa de cafetão";                   //A roupa é uma variável pública que pode ser atualizada para qualquer valor
	gk.chuveiro();
	alert("A atual tecnologia de chuveiro obteve "+gk+" para um fator lixo de "+gk.fator Lixo);

	gk.muitoTempo();                              //Passam 50 anos
	Person.prototype.chuveiro=function(){         //A tecnologia de chuveiro melhorou para todos
		this.fatorLixo=0;
	}
	gk.serFixe=function(){                        //O Alex tem novas ideias sobre moda
		this.roupa="Roupa de lata";
	};

	gk.serFixe(); gk.chuveiro();
	alert("O elegante "+gk+" com "
		+gk.getIdade()+" anos, veste agora "
		+gk.roupa+" com fator lixo "
		+gk.fatorLixo);

	gk.amputar();                                 //Usa a propriedade do protótipo e cria uma propriedade pública
	gk.mostraPernas(); lk.mostraPernas();         //A Lisa ainda tem a propriedade do protótipo

	gk.muitoTempo();                              //Passm 50 anos...O Alex tem agora 100 anos
	gk.comer();                                   //Queixa-se sobre idade extrema, morte e dificuldade em comer.
}

Notas

  • maxIdade é uma variável privada, sem método de acesso privilegiado; ou seja, não há forma pública de alterá-la ou consultá-la.
  • cabelo é uma variável privada definida como argumento para o construtor. Variáveis passadas para o construtor estão disponíveis no objeto como variáveis privadas.
  • O método serFixe() 'Roupa de Lata' foi aplicado apenas ao objeto gk, e não a toda a classe Pessoa. Para outras pessoas, o método serFixe() usa a roupa original ‘cáqui e camisa preta’ que o Alex abandonou mais tarde.
  • Notar a chamada implícita ao métido gk.toString() quando se usa a concatenação de strings. É isto que permite que o código alert(gk+' é fixe.') coloque o nome ‘Alex’ lá, e é equivalente a alert(gk.toString()+' é fixe.'). Qualquer objeto de qualquer tipo em JS tem um método .toString(), mas pode ser substituído por um nosso.
  • Não é possível (em princípio) definir métodos públicos de uma classe dentro do seu construtor.  É necessário usar a propriedade prototype externamente, como nos métodos serFixe() e chuveiro() acima.
  • Como tentei mostrar com a propriedade Pessoa.prototype.pernas e a função amputate(), propriedades do protótiplo são partilhadas por todas as instâncias ou objetos. A propriedade lk.pernas tem o valor 2. Contudo, tentar alterar este valor usando gk.pernas=1 ou (no objeto Pessoa) this.pernas=1 acaba por criar uma nova propriedade pública específica para essa instância. (É por isso que invocar gk.amputar() só remove uma perna ao Alex, mas não a Lisa.) Para alterar uma propriedade do protótipo, devemos usar Pessoa.prototype.pernas=1 ou algo como this.constructor.prototype.pernas=1. (digo “algo como” porque this.constructor não está disponível dentro de funções privadas do objeto, uma vez que this se refere ao objeto todo.)
  • Sempre que uma função anónima é declarada em linha com
    foo = function(p1,p2){ código }
    o construtor new Function() NÃO é equivalente, por exemplo, a
    foo = new Function('p1','p2','código');
    uma vez que o último corre no âmbito global – ao invés de herdar o contexto da função construtor – não permitindo assim que ele aceda às variáveis privadas.
  • Como indicado nos comentários acima, o ato de afetar gk.cabelo com um valor qualquer NÃO substitui a variável privada cabelo. Embora seja uma ideia parva, é possível ter ambas as variáveis, pública e privada, com o mesmo nome. Por exemplo, o método  gritat() na classe seguinte, tem valores diferentes para foo e this.foo:
    function ClasseParva(){
      var foo = "interno";
      this.foo = "externo";
      this.gritar=function(){ alert("O foo interno é "+foo+"\nO foo externo é "+this.foo) }
    }
  • Funções privadas e métodos privilegiados, tal como variáveis privadas e propriedades públicas, são instanciados com cada novo objeto criado. Por isso, de cada vez que se cria uma pessoa com new Pessoa() são criadas novas cópias de maisVelho(), toString(), getNome(), comer(), exercício(), peso(), getCor(), getIdade(), and muitoTempo(). Para cada Pessoa, de cada vez. Isto contrasta com os métodos públicos (existe apenas uma cópia de serFixe() e chuveiro() independentemente do número de objetos de Pessoa que sejam criados) e por motivos de consumo de memória e desempenho é preferível abandonar algum grau de proteção dos objetos a favor do uso de métodos públicos. Note-se que, para isso, é necessário tornar públicas as variáveis privadas (uma vez que sem métodos de acesso privilegiado não haveria maneira de usá-las) para que os métodos públicos sejam capazes de ter acesso a elas; o que também permite que código externos veja e destrua essas variáveis. A otimização de memória e desempenho, recorrendo ao uso exclusivo de propriedades públicas, tem consequências que podem tornar o código menos robusto. Por exemplo, no código acima, as variáveis idade e maxIdade são privadas; idade só pode ser acedida externamente através de getIdade() (não pode ser modificada) e maxIdade não pode ser lida nem modificada externamente. Torná-las públicas iria permitir que qualquer código executasse gk.maxIdade=1; gk.idade=200; o que não faz sentido (não deve ser possível manipular a idade ou a esperança de vida de alguém diretamente), e como consequência, a variável vivo não poderia ser atualizada, deixando o objeto Pessoa num estado incoerente.
Traduzido de OOP in JS

Derivada e tangente

A reta tangente a uma função, num ponto, pode ser calculada, de forma aproximada, a partir da reta que passa em (x,f(x)) e (x2,f(x2)), onde x2=x+dx, sendo dx um valor muito pequeno.

No exemplo abaixo, o utilizador pode inserir a expressão da função (com sintaxe de Javascript), assim como os valores de x e dx.

O declive da reta tangente é calculado como m=(f(x2)-f(x))/(x2-x), e a ordenada da origem é obtida pela expressão b=f(x)-m*x.

O gráfico é configurável: é possível alterar a escala de x (xEscala), a escala de y (yEscala), a frequência de traços em cada eixo (xMarcas e yMarcas), assim como a posição da origem do referencial (xr e yr).

Visualização:

Código html

<!DOCTYPE html>
<html>
<head>
<title>Gráfico - reta tangente</title>
<meta charset="iso-8859-1">
<script 
  src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
</script>


<script type="text/javascript" src="codigo.js"></script>
<script type="text/javascript" src="Grafico.js"></script>
<link type="text/css" href="estilos.css" rel="stylesheet" />
</head>
<body onload="init();">
<h3>Gráfico - reta tangente</h3>
<canvas width="400" height="300" id="cvs"></canvas>
<form>
  <div>
    xEscala
    <meter id="xEsc" min="1" max="100" value=""></meter>
    <input type="text" id="vxEsc" size="3" value="" onblur="cInput('xEsc');" />
  </div>
  <div>
    yEscala
    <meter id="yEsc" min="1" max="100" value=""></meter>
    <input type="text" id="vyEsc" size="3" value="" onblur="cInput('yEsc');" />
  </div>
  <div>
    xMarcas
    <meter id="xMrc" min="1" max="50" value=""></meter>
    <input type="text" id="vxMrc" size="3" value="" onblur="cInput('xMrc');" />
  </div>
  <div>
    yMarcas
    <meter id="yMrc" min="1" max="50" value=""></meter>
    <input type="text" id="vyMrc" size="3" value="" onblur="cInput('yMrc');" />
  </div>
  <div>
    xr
    <meter id="xRef" min="0" max="400" value=""></meter>
    <input type="text" id="vxRef" size="3" value="" onblur="cInput('xRef');" />
  </div>
  <div>
    yr
    <meter id="yRef" min="0" max="300" value=""></meter>
    <input type="text" id="vyRef" size="3" value="" onblur="cInput('yRef');" />
  </div>
  <div>
    Função:
    <input type="text" id="func" size="30" value="4*Math.log(x)" />
  </div>
  <div>
    x:
    <input type="text" id="xtan" size="10" value="5" />
  </div>
  <div>
    dx:
    <input type="text" id="xdelta" size="10" value="0.2" />
  </div>
  <input type="button" value="Redesenha" onclick="redesenha();" />
</form>
</body>
</html>

Ficheiro codigo.js

var g;

function init() {
	g = new Grafico();
	g.inicia();

	document.getElementById("xEsc").value = g.xEscala;
	document.getElementById("vxEsc").value = g.xEscala;
	document.getElementById("yEsc").value = g.yEscala;
	document.getElementById("vyEsc").value = g.yEscala;

	document.getElementById("xMrc").value = g.xMarcas;
	document.getElementById("vxMrc").value = g.xMarcas;
	document.getElementById("yMrc").value = g.yMarcas;
	document.getElementById("vyMrc").value = g.yMarcas;

	document.getElementById("xRef").value = g.xr;
	document.getElementById("vxRef").value = g.xr;
	document.getElementById("yRef").value = g.yr;
	document.getElementById("vyRef").value = g.yr;

	$("#xEsc").click(cMeter);
	$("#yEsc").click(cMeter);
	$("#xMrc").click(cMeter);
	$("#yMrc").click(cMeter);
	$("#xRef").click(cMeter);
	$("#yRef").click(cMeter);
}

function redesenha() {
	g.xEscala = eval(document.getElementById("vxEsc").value);
	g.yEscala = eval(document.getElementById("vyEsc").value);
	g.xMarcas = eval(document.getElementById("vxMrc").value);
	g.yMarcas = eval(document.getElementById("vyMrc").value);
	g.xr = eval(document.getElementById("vxRef").value);
	g.yr = eval(document.getElementById("vyRef").value);
	g.inicia();
}

function cMeter(e) {
	var id = $(this).attr("id");
	var met = document.getElementById(id);
	var max = met.getAttribute("max");
	var min = met.getAttribute("min");

	var offx = e.clientX - met.getBoundingClientRect().left;
	opac = offx / $('#'+id).width();
	var pct = Math.round((max-min) * opac);
	$('#'+id).val(pct);
	$('#v'+id).val(pct);
}

function cInput(id) {
	var valor = $('#v'+id).val();
	$('#'+id).val(valor);
}

Ficheiro Grafico.js


/* 
Classe Grafico

*/

function Grafico() {

	// Coordenadas da origem do referencial visível
	this.xr = 50;
	this.yr = 260;
	// Escalas dos eixos
	this.xEscala = 15;
	this.yEscala = 15;
	// Espaçamento entre marcas
	this.xMarcas = 5;
	this.yMarcas = 2;

	var canvas = document.getElementById("cvs");
	var larg = canvas.getAttribute("width");
	var alt = canvas.getAttribute("height");
	var ctx = canvas.getContext("2d");

	// Criar imagens de 1x1 pixels
	var ptPreto = ctx.createImageData(1,1);
	ptPreto.data[0] = 0;	// Componente vermelha
	ptPreto.data[1] = 0;	// Componente verde
	ptPreto.data[2] = 0;	// Componente azul
	ptPreto.data[3] = 255;	// Componente alfa
	var ptVermelho = ctx.createImageData(1,1);
	ptVermelho.data[0] = 255;// Componente vermelha
	ptVermelho.data[1] = 0;	// Componente verde
	ptVermelho.data[2] = 0;	// Componente azul
	ptVermelho.data[3] = 255;	// Componente alfa
	var ptVerde = ctx.createImageData(1,1);
	ptVerde.data[0] = 0;	// Componente vermelha
	ptVerde.data[1] = 128;	// Componente verde
	ptVerde.data[2] = 0;	// Componente azul
	ptVerde.data[3] = 255;	// Componente alfa
	var ptAzul = ctx.createImageData(1,1);
	ptAzul.data[0] = 0;	// Componente vermelha
	ptAzul.data[1] = 0;	// Componente verde
	ptAzul.data[2] = 255;	// Componente azul
	ptAzul.data[3] = 255;	// Componente alfa

	this.inicia = inicia;
	function inicia() {
		ctx.fillStyle = "white";
		ctx.beginPath();
		ctx.rect(0,0,larg,alt);
		ctx.fill();

		ctx.fillStyle = "white";
		ctx.beginPath();
		ctx.rect(0,0,larg,alt);
		ctx.fill();

		ctx.strokeStyle = "black";
		ctx.beginPath();
		ctx.rect(0,0,larg,alt);
		ctx.stroke();

		// Colocar os dois eixos xx, yy
		poeLinha(0,this.yr,larg,this.yr,"black");
		poeLinha(this.xr,0,this.xr,alt,"black");

		// Preparação para escrever o texto na horizontal
		ctx.fillStyle = "black";
		ctx.font = "12px 'Courier'";
		ctx.textAlign = "center";
		ctx.textBaseline = "top";
		// Colocar a escala nos eixos
		// Eixo x positivo
		for(var n=0; n*this.xEscala+this.xr<larg; n+=this.xMarcas) {
			poeLinha(n*this.xEscala+this.xr,this.yr-5,n*this.xEscala+this.xr,this.yr+5,"black");
			ctx.fillText(n, n*this.xEscala+this.xr, this.yr+10);
		}
		// Eixo x negativo
		for(var n=0; n*this.xEscala+this.xr>0; n-=this.xMarcas) {
			poeLinha(n*this.xEscala+this.xr,this.yr-5,n*this.xEscala+this.xr,this.yr+5,"black");
			ctx.fillText(n, n*this.xEscala+this.xr, this.yr+10);
		}
		// Preparação para escrever o texto na horizontal
		ctx.textAlign = "right";
		ctx.textBaseline = "middle";
		// Eixo y negativo
		for(var n=0; n*this.yEscala+this.yr<alt; n+=this.yMarcas) {
			poeLinha(this.xr-5,n*this.yEscala+this.yr,this.xr+5,n*this.yEscala+this.yr,"black");
			ctx.fillText(-n, this.xr-10, n*this.yEscala+this.yr);
		}
		// Eixo y positivo
		for(var n=0; n*this.yEscala+this.yr>0; n-=this.yMarcas) {
			poeLinha(this.xr-5,n*this.yEscala+this.yr,this.xr+5,n*this.yEscala+this.yr,"black");
			ctx.fillText(-n, this.xr-10, n*this.yEscala+this.yr);
		}

		// Desenhar a função
		var y;
		for(var i=0; i<larg; i++) {
			x = i/this.xEscala;
			y = f(x);
			if(!isFinite(y)) continue;
			this.poePonto(ptPreto, x, y);
		}

		// Calcular o declive
		var xt = eval( document.getElementById("xtan").value );
		var dx = eval( document.getElementById("xdelta").value );
		if(xt==undefined || dx==undefined) return;
		yt = f(xt);
		yt2 = f(xt+dx);
		var m = (yt2-yt)/dx;
		// Calcular ordenada na origem
		var b = yt - m * xt;

		// Desenhar tangente
		for(var i=0; i<larg; i++) {
			x = i/this.xEscala;
			y = m * x + b;
			if(!isFinite(y)) continue;
			this.poePonto(ptPreto, x, y);
		}

		
	}

	function f(x) {
		var ff = document.getElementById("func").value;
		if(ff=="") return 0;

		var y = eval(ff);
		return y;
	}


	this.poePonto = poePonto;
	function poePonto(pt, x, y) {
		var xe = x * this.xEscala + this.xr;
		var ye = -y * this.yEscala + this.yr;
		ctx.putImageData(pt, xe, ye);
	}

	this.poeLinha = poeLinha;
	function poeLinha(x1,y1,x2,y2,cor) {
		ctx.strokeStyle = cor;
		ctx.beginPath();
		ctx.moveTo(x1,y1);
		ctx.lineTo(x2,y2);
		ctx.stroke();
	}
}

Ficheiro estilos.css

meter {
	width: 200px;
}

Integral

O integral de uma função, entre dois valores limite de x, pode ser calculado, de forma aproximada, pela soma da áres de um conjunto finito de retângulos verticais de largura dx e altura f(x), com o canto superior esquerdo posicionado em cima da curva da função, e o canto inferior esquerdo em cima do eixo dos xx.

No exemplo abaixo, o utilizador pode inserir a expressão da função (com sintaxe de Javascript), assim como os limites xMax e xMin, e ainda o valor de dx.

O gráfico é configurável: é possível alterar a escala de x (xEscala), a escala de y (yEscala), a frequência de traços em cada eixo (xMarcas e yMarcas), assim como a posição da origem do referencial (xr e yr).

Visualização:

Código html

<!DOCTYPE html>
<html>
<head>
<title> - Integral</title>
<meta charset="utf8">
<script 
  src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
</script>


<script type="text/javascript" src="codigo.js"></script>
<script type="text/javascript" src="Grafico.js"></script>
<link type="text/css" href="estilos.css" rel="stylesheet" />
</head>
<body onload="init();">
<h3>Gráfico - Integral</h3>
<canvas width="400" height="300" id="cvs"></canvas>
<form>
  <div>
    xEscala
    <meter id="xEsc" min="1" max="100" value=""></meter>
    <input type="text" id="vxEsc" size="3" value="" onblur="cInput('xEsc');" />
  </div>
  <div>
    yEscala
    <meter id="yEsc" min="1" max="100" value=""></meter>
    <input type="text" id="vyEsc" size="3" value="" onblur="cInput('yEsc');" />
  </div>
  <div>
    xMarcas
    <meter id="xMrc" min="1" max="50" value=""></meter>
    <input type="text" id="vxMrc" size="3" value="" onblur="cInput('xMrc');" />
  </div>
  <div>
    yMarcas
    <meter id="yMrc" min="1" max="50" value=""></meter>
    <input type="text" id="vyMrc" size="3" value="" onblur="cInput('yMrc');" />
  </div>
  <div>
    xr
    <meter id="xRef" min="0" max="400" value=""></meter>
    <input type="text" id="vxRef" size="3" value="" onblur="cInput('xRef');" />
  </div>
  <div>
    yr
    <meter id="yRef" min="0" max="300" value=""></meter>
    <input type="text" id="vyRef" size="3" value="" onblur="cInput('yRef');" />
  </div>
  <div>
    Função:
    <input type="text" id="func" size="30" value="7*Math.log(x)" />
  </div>
  <div>
    xMin:
    <input type="text" id="xmin" size="10" value="10" />
  </div>
  <div>
    xMax:
    <input type="text" id="xmax" size="10" value="25" />
  </div>
  <div>
    dx:
    <input type="text" id="xdelta" size="10" value="1" />
  </div>
  <input type="button" value="Redesenha" onclick="redesenha();" />
  <div>
    Área: <span id="vArea"></span>
  </div>
</form>
</body>
</html>

Ficheiro codigo.js

var g;

function init() {
	g = new Grafico();
	g.inicia();

	document.getElementById("xEsc").value = g.xEscala;
	document.getElementById("vxEsc").value = g.xEscala;
	document.getElementById("yEsc").value = g.yEscala;
	document.getElementById("vyEsc").value = g.yEscala;

	document.getElementById("xMrc").value = g.xMarcas;
	document.getElementById("vxMrc").value = g.xMarcas;
	document.getElementById("yMrc").value = g.yMarcas;
	document.getElementById("vyMrc").value = g.yMarcas;

	document.getElementById("xRef").value = g.xr;
	document.getElementById("vxRef").value = g.xr;
	document.getElementById("yRef").value = g.yr;
	document.getElementById("vyRef").value = g.yr;

	$("#xEsc").click(cMeter);
	$("#yEsc").click(cMeter);
	$("#xMrc").click(cMeter);
	$("#yMrc").click(cMeter);
	$("#xRef").click(cMeter);
	$("#yRef").click(cMeter);
}

function redesenha() {
	g.xEscala = eval(document.getElementById("vxEsc").value);
	g.yEscala = eval(document.getElementById("vyEsc").value);
	g.xMarcas = eval(document.getElementById("vxMrc").value);
	g.yMarcas = eval(document.getElementById("vyMrc").value);
	g.xr = eval(document.getElementById("vxRef").value);
	g.yr = eval(document.getElementById("vyRef").value);
	g.inicia();
}

function cMeter(e) {
	var id = $(this).attr("id");
	var met = document.getElementById(id);
	var max = met.getAttribute("max");
	var min = met.getAttribute("min");

	var offx = event.clientX - met.getBoundingClientRect().left;
	opac = offx / $('#'+id).width();
	var pct = Math.round((max-min) * opac);
	$('#'+id).val(pct);
	$('#v'+id).val(pct);
}

function cInput(id) {
	var valor = $('#v'+id).val();
	$('#'+id).val(valor);
}

Ficheiro Grafico.js


/* 
Classe Grafico

*/

function Grafico() {

	// Coordenadas da origem do referencial visível
	this.xr = 50;
	this.yr = 260;
	// Escalas dos eixos
	this.xEscala = 10;
	this.yEscala = 8;
	// Espaçamento entre marcas
	this.xMarcas = 5;
	this.yMarcas = 4;

	var canvas = document.getElementById("cvs");
	var larg = canvas.getAttribute("width");
	var alt = canvas.getAttribute("height");
	var ctx = canvas.getContext("2d");

	// Criar imagens de 1x1 pixels
	var ptPreto = ctx.createImageData(1,1);
	ptPreto.data[0] = 0;	// Componente vermelha
	ptPreto.data[1] = 0;	// Componente verde
	ptPreto.data[2] = 0;	// Componente azul
	ptPreto.data[3] = 255;	// Componente alfa
	var ptVermelho = ctx.createImageData(1,1);
	ptVermelho.data[0] = 255;// Componente vermelha
	ptVermelho.data[1] = 0;	// Componente verde
	ptVermelho.data[2] = 0;	// Componente azul
	ptVermelho.data[3] = 255;	// Componente alfa
	var ptVerde = ctx.createImageData(1,1);
	ptVerde.data[0] = 0;	// Componente vermelha
	ptVerde.data[1] = 128;	// Componente verde
	ptVerde.data[2] = 0;	// Componente azul
	ptVerde.data[3] = 255;	// Componente alfa
	var ptAzul = ctx.createImageData(1,1);
	ptAzul.data[0] = 0;	// Componente vermelha
	ptAzul.data[1] = 0;	// Componente verde
	ptAzul.data[2] = 255;	// Componente azul
	ptAzul.data[3] = 255;	// Componente alfa

	this.inicia = inicia;
	function inicia() {
		ctx.fillStyle = "white";
		ctx.beginPath();
		ctx.rect(0,0,larg,alt);
		ctx.fill();

		ctx.fillStyle = "white";
		ctx.beginPath();
		ctx.rect(0,0,larg,alt);
		ctx.fill();

		ctx.strokeStyle = "black";
		ctx.beginPath();
		ctx.rect(0,0,larg,alt);
		ctx.stroke();

		// Colocar os dois eixos xx, yy
		poeLinha(0,this.yr,larg,this.yr,"black");
		poeLinha(this.xr,0,this.xr,alt,"black");

		// Preparação para escrever o texto na horizontal
		ctx.fillStyle = "black";
		ctx.font = "12px 'Courier'";
		ctx.textAlign = "center";
		ctx.textBaseline = "top";
		// Colocar a escala nos eixos
		// Eixo x positivo
		for(var n=0; n*this.xEscala+this.xr<larg; n+=this.xMarcas) {
			poeLinha(n*this.xEscala+this.xr,this.yr-5,n*this.xEscala+this.xr,this.yr+5,"black");
			ctx.fillText(n, n*this.xEscala+this.xr, this.yr+10);
		}
		// Eixo x negativo
		for(var n=0; n*this.xEscala+this.xr>0; n-=this.xMarcas) {
			poeLinha(n*this.xEscala+this.xr,this.yr-5,n*this.xEscala+this.xr,this.yr+5,"black");
			ctx.fillText(n, n*this.xEscala+this.xr, this.yr+10);
		}
		// Preparação para escrever o texto na horizontal
		ctx.textAlign = "right";
		ctx.textBaseline = "middle";
		// Eixo y negativo
		for(var n=0; n*this.yEscala+this.yr<alt; n+=this.yMarcas) {
			poeLinha(this.xr-5,n*this.yEscala+this.yr,this.xr+5,n*this.yEscala+this.yr,"black");
			ctx.fillText(-n, this.xr-10, n*this.yEscala+this.yr);
		}
		// Eixo y positivo
		for(var n=0; n*this.yEscala+this.yr>0; n-=this.yMarcas) {
			poeLinha(this.xr-5,n*this.yEscala+this.yr,this.xr+5,n*this.yEscala+this.yr,"black");
			ctx.fillText(-n, this.xr-10, n*this.yEscala+this.yr);
		}

		// Desenhar a função
		var y;
		for(var i=0; i<larg; i++) {
			x = i/this.xEscala;
			y = f(x);
			if(!isFinite(y)) continue;
			this.poePonto(ptPreto, x, y);
		}

		// Colocar os retângulos
		var x1 = eval( document.getElementById("xmin").value );
		var x2 = eval( document.getElementById("xmax").value );
		var dx = eval( document.getElementById("xdelta").value );
		var area = 0;
		for(x=x1; x<x2; x+=dx) {
			if(x+dx>x2) dx = x2-x;
			y = f(x);
			area += dx*y;
			// Desenhar retângulo
			var xe = x * this.xEscala + this.xr;
			var ye = -y * this.yEscala + this.yr;
			var lg = dx * this.xEscala;
			var at = y * this.yEscala;

			ctx.fillStyle = "red";
			ctx.beginPath();
			ctx.rect(xe,ye,lg,at);
			ctx.fill();

			ctx.strokeStyle = "black";
			ctx.beginPath();
			ctx.rect(xe,ye,lg,at);
			ctx.stroke();
		}
		document.getElementById("vArea").innerHTML = area;
		
	}

	function f(x) {
		var ff = document.getElementById("func").value;
		if(ff=="") return 0;

		var y = eval(ff);
		return y;
	}


	this.poePonto = poePonto;
	function poePonto(pt, x, y) {
		var xe = x * this.xEscala + this.xr;
		var ye = -y * this.yEscala + this.yr;
		ctx.putImageData(pt, xe, ye);
	}

	this.poeLinha = poeLinha;
	function poeLinha(x1,y1,x2,y2,cor) {
		ctx.strokeStyle = cor;
		ctx.beginPath();
		ctx.moveTo(x1,y1);
		ctx.lineTo(x2,y2);
		ctx.stroke();
	}
}

Ficheiro estilos.css

meter {
	width: 200px;
}

Sol, Terra e Lua

O movimento do sistema Sol-Terra-Lua apresentado abaixo é uma simplificação circular do movimento elítico real destes astros. Neste caso, temos um movimento circular simples da Terra em torno do Sol, e um movimento circular composto da Lua em torno da Terra. A animação não foi feita à escala.

Em primeiro lugar é calculado o movimento da Terra, ou seja, são calculadas as coordenadas x e y do movimento circular da Terra, às quais são adicionadas as coordenadas fixas da posição do Sol.

Depois é calculado o movimento da Lua: são calculadas as coordenadas x e y do movimento circular da Lua, às quais são adicionadas as coordenadas da posição da Terra.

Visualização:

<!DOCTYPE html>
<html>
<head>
<title>Gráfico</title>
<meta charset="iso-8859-1">
<script type="text/javascript">

var r1 = 130;	// raio da terra
var p1 = 10;	// perído da terra em seg.
var r2 = 40;	// raio da lua
var p2 = 3;	// perído da lua em seg.

var ang1=0, ang2=0;	// ângulos da terra e da lua no momento atual
var delta1, delta2;	// incrementos nos ângulos da terra e da lua em
			// cada chamada da função anima()

var x0 = 300;	// coordenadas do sol - fixas
var y0 = 200;

var ctx;

var larg, alt;

function init() {
	var c = document.getElementById("cvs");
	ctx = c.getContext("2d");
	larg = eval( c.getAttribute("width") );
	alt = eval( c.getAttribute("height") );
	var periodoRelogio = 40;
	setInterval("anima()", periodoRelogio);
	delta1 = (360*periodoRelogio)/(1000*p1);
	delta2 = (360*periodoRelogio)/(1000*p2);
}

function anima() {
	// Terra
	ang1 += delta1;
	x1 = r1 * Math.cos(ang1*Math.PI/180) + x0;
	y1 = r1 * Math.sin(ang1*Math.PI/180) + y0;
	// Lua
	ang2 += delta2;
	x2 = r2 * Math.cos(ang2*Math.PI/180) + x1;
	y2 = r2 * Math.sin(ang2*Math.PI/180) + y1;
	// Desenhar
	ctx.fillStyle = "black";
	ctx.beginPath();
	ctx.rect(0,0,larg,alt);
	ctx.fill();

	ctx.fillStyle = "#FFFF20";
	ctx.beginPath();
	ctx.arc(x0,y0,20,0,2*Math.PI);
	ctx.fill();
		
	ctx.fillStyle = "#0000FF";
	ctx.beginPath();
	ctx.arc(x1,y1,10,0,2*Math.PI);
	ctx.fill();
		
	ctx.fillStyle = "white";
	ctx.beginPath();
	ctx.arc(x2,y2,5,0,2*Math.PI);
	ctx.fill();
		
}

</script>


</head>
<body onload="init();">
<canvas width="600" height="400" id="cvs"></canvas>
</body>
</html>

Relógio

Um relógio apenas com ponteiro dos minutos e segundos, com movimentos discretos. Cada segundo ou minuto corresponde a 6º. A animação tem um período de refrescamento de 1000ms.

As coordenadas do ponteiro dos segundos são atualizadas em cada disparo do relógio (função setInterval). O ponteiro do minutos só é atualizado quando o ponteiro dos segundos tem um ângulo múltiplo de 360º (ang2%360==0).

Visualização:

<!DOCTYPE html>
<html>
<head>
<title>Gráfico</title>
<meta charset="iso-8859-1">
<script type="text/javascript">

var r1 = 150;	// extremidade dos minutos
var p1 = 3600;	// período dos minutos
var r2 = 180;	// extremidade dos segundos
var p2 = 60;	// período dos segundos.

var ang1=0, ang2=0;	// ângulos dos ponteiros no momento atual
var delta1=6, delta2=6;	// incrementos nos ângulos dos ponteiros em
			// cada chamada da função anima()

var x0 = 300;	// coordenadas do centro do relógio - fixas
var y0 = 200;

var ctx;

function init() {
	var c = document.getElementById("cvs");
	ctx = c.getContext("2d");
	larg = eval( c.getAttribute("width") );
	alt = eval( c.getAttribute("height") );
	var periodoRelogio = 1000;
	setInterval("anima()", periodoRelogio);
}

function anima() {
	// Segundos
	ang2 += delta2;
	x2 = r2 * Math.cos((ang2-90)*Math.PI/180) + x0;
	y2 = r2 * Math.sin((ang2-90)*Math.PI/180) + y0;
	// Minutos
	if(ang2 % 360 == 0) ang1 += delta1;
	x1 = r1 * Math.cos((ang1-90)*Math.PI/180) + x0;
	y1 = r1 * Math.sin((ang1-90)*Math.PI/180) + y0;
	// Desenhar
	ctx.fillStyle = "black";
	ctx.beginPath();
	ctx.rect(0,0,larg,alt);
	ctx.fill();

	ctx.fillStyle = "#FFFF20";
	ctx.beginPath();
	ctx.arc(x0,y0,10,0,2*Math.PI);
	ctx.fill();
		
	ctx.fillStyle = "#8080FF";
	ctx.beginPath();
	ctx.arc(x1,y1,5,0,2*Math.PI);
	ctx.fill();
	poeLinha(x0,y0,x1,y1,"#8080FF");
		
	ctx.fillStyle = "white";
	ctx.beginPath();
	ctx.arc(x2,y2,3,0,2*Math.PI);
	ctx.fill();
	poeLinha(x0,y0,x2,y2,"white");
		
}

function poeLinha(x1,y1,x2,y2,cor) {
	ctx.strokeStyle = cor;
	ctx.beginPath();
	ctx.moveTo(x1,y1);
	ctx.lineTo(x2,y2);
	ctx.stroke();
}


</script>


</head>
<body onload="init();">
<canvas width="600" height="400" id="cvs"></canvas>
</body>
</html>

Pêndulo

Um pêndulo, sem atrito, com um ângulo inicial ang0 e uma corda de comprimento corda. As equações do movimento foram desenvolvidas apenas com o objetivo de determinar as coordenadas x e y do pêndulo, que seguem uma função sinusoidal esquematizada no rascunho seguinte.

pendulo

O movimento do pêndulo é discretizado com um tempo de amostragem fixo de 40ms. A variável auxiliar Z define os momentos da amostragem. O valor de cos(Z) indica a posição x do pêndulo. O ângulo atual do pêndulo, ang, é dado por asin(x/corda). O y do pêndulo é dado por corda*cos(ang).

Visualização:

<!DOCTYPE html>
<html>
<head>
<title>Gráfico</title>
<meta charset="iso-8859-1">
<script type="text/javascript">

var corda = 300;// comprimento da corda
var ang0=20;	// ângulo inicial

var xc = 300;	// Ponto onde a corda está presa
var yc = 35;

var x;	// coordenadas do pêndulo
var y;
var ang;

var d = corda*Math.sin(ang0*Math.PI/180);	// x máximo

// nº de posições intermédias por onde passa o pêndulo entre os extremos
var numPos = 30;
var incAng = Math.PI/numPos;	// incremento angular (para angZ) de cada vez que o relógio dispara
var angZ = 0;

var ctx;

var larg, alt;

function init() {
	var c = document.getElementById("cvs");
	ctx = c.getContext("2d");
	larg = eval( c.getAttribute("width") );
	alt = eval( c.getAttribute("height") );
	var relogio = 40;
	ang = ang0;
	x = corda * Math.sin(ang*Math.PI/180) + xc;
	y = corda * Math.cos(ang*Math.PI/180) + yc;
	setInterval("anima()", relogio);
}

function anima() {
	angZ += incAng;
	var z = d * Math.cos(angZ);
	x = z;
	ang = Math.asin(x/corda);
	y = corda * Math.cos(ang);

	x += xc;
	y += yc;

	// Desenhar
	ctx.fillStyle = "black";
	ctx.beginPath();
	ctx.rect(0,0,larg,alt);
	ctx.fill();

	ctx.lineWidth = 3;
	poeLinha(xc,yc,x,y,"#8080FF");
		
	ctx.fillStyle = "#FFFF20";
	ctx.beginPath();
	ctx.arc(x,y,16,0,2*Math.PI);
	ctx.fill();

}

function poeLinha(x1,y1,x2,y2,cor) {
	ctx.strokeStyle = cor;
	ctx.beginPath();
	ctx.moveTo(x1,y1);
	ctx.lineTo(x2,y2);
	ctx.stroke();
}


</script>


</head>
<body onload="init();">
<canvas width="600" height="400" id="cvs"></canvas>
</body>
</html>

Raio de luz

Um raio de luz parte do canto inferior esquerdo do ecrã, com um ângulo de 45º. Quando colide com o círculo, o círculo muda de cor. Assim que o raio atinge o limite do canvas, o processo é reiniciado.

O período de atualização do canvas é de 20ms. A função eval é usada para transformar a largura e altura do canvas em valores numéricos, caso contrário o programa vê-as como texto e as adições sobre a variável y são concatenações de texto.

Visualização:

<!DOCTYPE html>
<html>
<head>
<title>Gráfico</title>
<meta charset="iso-8859-1">
<script type="text/javascript">

var xc = 350;	// centro do círculo
var yc = 150;
var r = 80;	// raio do círculo
var ang = 45;	// ângulo do raio de luz em graus

// Incrementos para o raio de luz
var incx = Math.cos(ang*Math.PI/180);
var incy = -Math.sin(ang*Math.PI/180);

var x, y;	// coordenadas do ponto cintilante

var ctx;

var larg, alt;

function init() {
	var c = document.getElementById("cvs");
	ctx = c.getContext("2d");
	ctx.lineWidth = 3;
	larg = eval( c.getAttribute("width") );
	alt = eval( c.getAttribute("height") );
	x = 0;
	y = alt;
	var periodoRelogio = 20;
	setInterval("anima()", periodoRelogio);
}

function anima() {
	// Desenhar
	ctx.fillStyle = "black";
	ctx.beginPath();
	ctx.rect(0,0,larg,alt);
	ctx.fill();

	// Se a distância de (x,y) a (xc,yc) for inferior a r, então
	// muda de cor

	var d = Math.sqrt(Math.pow(x-xc,2)+Math.pow(y-yc,2));
	if(d<r)
		ctx.fillStyle = "#FF8040";
	else
		ctx.fillStyle = "#FF0000";

	ctx.beginPath();
	ctx.arc(xc,yc,r,0,2*Math.PI);
	ctx.fill();
	

	x += incx;
	y += incy;

	//ctx.fillStyle = "#FFFF20";
	//ctx.beginPath();
	//ctx.arc(x,y,5,0,2*Math.PI);
	//ctx.fill();
	poeLinha(0,alt,x,y,"#FFFF20");

	// Quando sai do canvas, volta ao início
	if(y<0 || x>larg) {
		x = 0;
		y = alt;
	}

}


function poeLinha(x1,y1,x2,y2,cor) {
	ctx.strokeStyle = cor;
	ctx.beginPath();
	ctx.moveTo(x1,y1);
	ctx.lineTo(x2,y2);
	ctx.stroke();
}

</script>


</head>
<body onload="init();">
<canvas width="600" height="400" id="cvs"></canvas>
</body>
</html>