Eu acabei de criar um objeto Javascript que plota gráficos em linha utilizando Canvas. Funciona em "todos" os browsers, exceto IE abaixo da versão 9.
http://www.aplicacaoweb.com/aw/admin/teste/graficos/grafico_linha_demo.phpAbaixo segue o código fonte completo do exemplo. Criando uma página e o colando, terão o exemplo inteiro em funcionamento. Mais abaixo ainda, comento o uso do objeto e apresento um outro exemplo mais elaborado.
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title></title><style> body{ text-align:center; } #grafico{ margin:auto; border:1px solid #000; background-color:#F5F5F5; border:1px solid #CCC; }</style></head><body> <canvas id="grafico" width="600" height="450"></canvas></body><script> function id(idElemento){ return document.getElementById(idElemento); }; function mescla_objetos(velho,novo){ for(chave in novo){ velho[chave] = novo[chave]; } }; function GraficoLinha(canvas,opcoes_usuario){ var g, altura,largura,altura_util,largura_util,deltaX,deltaY; function constroi(){ opcoes = { 'cores':['red','blue','green','orange'], 'margem_esquerda':10, 'divisoes':0, 'espessura_linha':3 }; mescla_objetos(opcoes,opcoes_usuario); g = canvas.getContext('2d'); largura = canvas.offsetWidth; altura = canvas.offsetHeight; largura_util = largura - opcoes.margem_esquerda - 10; altura_util = altura - 20; desenha_eixos(); }; function desenha_eixos(){ g.strokeStyle = 'black'; g.lineWidth = 1; g.beginPath(); g.moveTo(opcoes.margem_esquerda,altura - 5); g.lineTo(opcoes.margem_esquerda,10); g.moveTo(opcoes.margem_esquerda - 5,altura - 10); g.lineTo(largura - 10,altura - 10); g.stroke(); g.beginPath(); g.moveTo(opcoes.margem_esquerda,10); g.lineTo(opcoes.margem_esquerda + 3,17); g.lineTo(opcoes.margem_esquerda - 3,17); g.fill(); g.beginPath(); g.moveTo(largura - 10,altura - 10); g.lineTo(largura - 17,altura - 13); g.lineTo(largura - 17,altura - 7); g.fill(); }; function zera(){ g.clearRect(0,0,largura,altura); desenha_eixos(); }; function setDados(vdados){ dados = vdados; zera(); plota(); }; function plota(){ var x,y, deltaX,deltaY; g.save(); g.translate(opcoes.margem_esquerda,altura - 10); var maior = 0; var menor = Math.pow(2,53); for(var i=0;i<dados.length;i++){ for(var j=0;j<dados[i].dados.length;j++){ if(dados[i].dados[j] < menor){ menor = dados[i].dados[j]; } if(dados[i].dados[j] > maior){ maior = dados[i].dados[j]; } } } deltaY = maior - menor; var intervalo = deltaY/opcoes.divisoes; g.strokeStyle = 'rgba(0,0,0,0.25)'; if(opcoes.divisoes){ g.fillText(Math.round(menor),5-opcoes.margem_esquerda,3); for(var i=0;i<opcoes.divisoes;i++){ g.beginPath(); y = Math.round((i+1)*intervalo*altura_util/deltaY); g.fillText(Math.round((i+1)*intervalo + menor),5-opcoes.margem_esquerda,-y+3); g.lineTo(0,-y); g.lineTo(largura_util,-y); g.stroke(); } } g.lineWidth = opcoes.espessura_linha; g.lineCap = 'round'; g.lineJoin = 'round'; for(var i=0;i<dados.length;i++){ deltaX = dados[i].dados.length; g.strokeStyle = opcoes.cores[i%opcoes.cores.length]; g.beginPath(); for(j=0;j<dados[i].dados.length;j++){ x = Math.round(j*largura_util/(deltaX-1)); y = Math.round((dados[i].dados[j] - menor)*altura_util/deltaY); x = isNaN(x)?0:x; y = isNaN(y)?(altura_util/2):y; g.lineTo(x,-y); } g.stroke(); } g.restore(); }; constroi(); this.zera = zera; this.setDados = setDados; }; var opt = { 'cores':['red','blue','green','orange'], 'margem_esquerda': 35, 'divisoes': 10, 'espessura_linha': 5 }; var g = new GraficoLinha(id('grafico'),opt); var dados = [ {'legenda':'linha1','dados':[100,500,200,600,400]}, {'legenda':'linha2','dados':[500,600,800,150,200]}, {'legenda':'linha3','dados':[100,217,618,900,755]} ]; g.setDados(dados);</script></html>A largura e altura do gráfico devem ser especificadas com os atributos width e height na própria tag canvas, e não no CSS.
O parâmetro de opções que é passado na instanciação do gráfico pode ser omitido, pois o objeto do gráfico trabalha com opções default.
A entrada "divisoes" do parâmetro de opções é um número natural que divide em escalas o eixo das ordenadas. Se omitido, não há escalas.
Os dados que são passados devem ser reais positivos. O gráfico faz arredondamentos.
Toda vez que o método setDados é chamado, o objeto faz a plotagem novamente.
Segue um exemplo mais elaborado que lança mão desse objeto para renderizar dinamicamente as linhas de acordo com valores passados pelo usuário na tela.
http://www.aplicacaoweb.com/aw/admin/teste/graficos/grafico_linha.phpVocês podem adicionar, alterar e remover linhas e seus respectivos
valores à vontade. Os valores do exemplo devem estar compreendidos entre
0 e 9999 e separados por vírgulas. Clicando no botão "Plotar" ou teclando ENTER, o sistema refaz a renderização, inclusive com as escalas no eixos das ordenadas.
O objeto está pronto pra, mas sinto que o objeto pode ser melhorado, como a plotagem de legendas e até mesmo de escalas no eixo das abscissas. Compartilho com vocês. Espero que o evoluam e façam bom proveito.