Gráficos em Linha com Javascript e Canvas

694 views
Skip to first unread message

gaido

unread,
Dec 8, 2011, 9:07:48 PM12/8/11
to list...@googlegroups.com
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.php

Abaixo 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.php

Você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.

Spiderpoison

unread,
Dec 9, 2011, 4:40:18 AM12/9/11
to LISTA PHP
Interessante, eu fiz um com VML que roda só nos IE (não sei se no 9
rola). Ele tem grafico em pizza e barra também.

http://websystem.sourceforge.net

O script está no pacote da primeira versão, procure o graficos.js.
talvez voce consiga adaptar para suprir a necessidade dos navegadores
antigos.

abraços
Spiderpoison

Luis Fernando Gaido

unread,
Dec 11, 2011, 7:42:08 PM12/11/11
to list...@googlegroups.com
Valeu, Spiderpoison. Vou estudar.


2011/12/9 Spiderpoison <spider...@gmail.com>

--
============================================================
LEIAM SEMPRE AS REGRAS DA LISTA:
http://groups.google.com.br/group/listaphp/web/regras-da-lista-php
--
JQUERY MAGAZINE > http://www.jquerymagazine.com.br
--
PHP MAGAZINE > http://www.phpmagazine.com.br
--
LISTA NODE.JS > https://groups.google.com/group/lista-nodejs?hl=pt-br
--
AJAX-BRASIL > http://groups.google.com/group/ajax-brasil
--
PYTHON-GOOGLE > http://groups.google.com.br/group/python-google
--
DOTNET-BRASIL > http://groups.google.com.br/group/dotnet_br
============================================================



--
Luís Fernando Gaido
Analista de Sistemas
PHP + MySQL + Ajax

"Em teoria, não existe nenhuma diferença entre teoria e prática. Mas, na prática, existe."
Reply all
Reply to author
Forward
0 new messages