Monthly Archives: Janeiro 2016

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>

Pong

Uma bola que sai do canto superior esquerdo com um determinado ângulo e velocidade e, ao batar numa tabela, reflete o movimento com o mesmo ângulo, no sentido oposto.

É um exemplo simples de uma animação criada com a função setInterval. Mais à frente usaremos outra forma de criar animações.

A direção inicial é dada pelos valores de incx e incy. O ecrã é atualizado a cada 40ms. Em cada atualização do ecrã, a posição da bola é atualizada com os incrementos incx e incy. Estes incrementos são atualizados sempre que a bola bate nas extremidades do canvas.

Note-se que o canvas tem que ser totalmente rescrito em cada frame.

Visualização:

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

var incx = 3 + 6 * Math.random();	// direção- coordenada x
var incy = 3 + 6 * Math.random();	// direção- coordenada y

// Garante velocidade mínima
var vMin = 8;
var vel = Math.sqrt(incx*incx+incy*incy);
if(vel<vMin) {
	incx *= vMin/vel;	
	incy *= vMin/vel;
}
// Nota: Para garantir velocidade mínima exata de vMin, era necessário corrigir as fórmulas anteriores com base na direção da velocidade.
// É preferível não tornar isto muito mais complexo, para não dificultar a compreensão.

incx = Math.floor(incx);
incy = Math.floor(incy);

var raio=5;	// raio do ponto cintilante
var x=raio, y=raio; 	// coordenadas do ponto cintilante

var ctx;

var larg, alt;

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

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

	x += incx;
	y += incy;

	//window.alert(x+','+y);

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

	if(x+2*raio>larg) incx = -incx;
	if(x-raio<0) incx = -incx;
	if(y+2*raio>alt) incy = -incy;
	if(y-raio<0) incy = -incy;
}

</script>


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

Programar no Canvas

O HTML5 introduziu um novo objeto – o canvas – onde é possível programar em javascript, como uma tela, da mesma forma que se programa sobre painéis em Java.

Há uma série de funções predefinidas para desenhar círculos, retângulos, linhas e escrever textos, com a cor, espessura e fonte que quisermos.

No exemplo abaixo, criamos um canvas com 600px de largura e 400px de altura, e desenhamos dois retângulos, uma circunferência e um círculo, assim como 6 linhas que unem estes 4 objetos.

Visualização:

Código HTML

<!DOCTYPE html>
<html>
<head>
<title>Canvas e Javascript</title>
<meta charset="iso-8859-1">
<script type="text/javascript" src="codigo.js"></script>
</head>
<body onload="inicia();">
<h3>Canvas e Javascript</h3>
<canvas width="640" height="480" id="tela"></canvas>
</body>
</html>

Código javascript


function inicia() {
	var canvas = document.getElementById("tela");
	var larg = canvas.getAttribute("width");
	var alt = canvas.getAttribute("height");

	// Aceder ao contexto gráfico do canvas
	var ctx = canvas.getContext("2d");

	// Retângulo do fundo
	ctx.fillStyle = "#60FFFF";
	ctx.beginPath();
	ctx.rect(0,0,larg,alt);
	ctx.fill();

	// Retângulo cima, esquerda
	ctx.fillStyle = "red";
	ctx.beginPath();
	ctx.rect(80,70,70,50);
	ctx.fill();

	// Retângulo cima, direita
	ctx.fillStyle = "blue";
	ctx.beginPath();
	ctx.rect(450,70,70,50);
	ctx.fill();

	// Círculo baixo esquerda (circunferência)
	ctx.lineWidth = 5;
	ctx.strokeStyle = "#008000";
	ctx.beginPath();
	ctx.arc(115,305,35,0,2*Math.PI);
	ctx.stroke();

	// Círculo baixo direita
	ctx.lineWidth = 5;
	ctx.fillStyle = "#FFFF20";
	ctx.beginPath();
	ctx.arc(485,305,35,0,2*Math.PI);
	ctx.fill();

	// Linha entre os retângulos
	poeLinha(ctx,115,95,485,95,"black");
	poeLinha(ctx,115,305,485,305,"white");
	poeLinha(ctx,115,95,115,305,"khaki");
	poeLinha(ctx,485,95,485,305,"purple");
	poeLinha(ctx,115,95,485,305,"magenta");
	poeLinha(ctx,115,305,485,95,"#123456");
}

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

A função ctx.arc permite desenhar um arco com a amplitude (em radianos) pretendida. Para desenhar um círculo, precisamos de desenhar de 0 a 2*PI (dois últimos parâmetros). Os dois primeiros parâmetros são as coordenadas do centro, e o terceiro parâmetro é o raio.

ctx.arc(485,305,35,0,2*Math.PI);

Os parâmetros da função ctx.rect são mais intuitivos: abcissa e ordenada do canto superior esquerdo, largura e altura do retângulo.

ctx.rect(450,70,70,50);

As funções do tipo stroke desenham apenas a linha, enquanto que as funções do tipo fill preenchem a figura.

Resultado final:
canvas1