In questa serie in due parti, combineremo l'elemento canvas versatile con la robusta libreria jQuery per creare un plug-in per grafici a barre. In questa seconda parte, lo convertiremo in un plug-in jQuery, quindi aggiungiamo alcuni effetti speciali e funzionalità aggiuntive.
Concludendo il Divertimento con la tela serie in due parti, oggi creeremo un plug-in per grafici a barre; non è un normale plug-in, sia chiaro. Mostreremo un po 'di jQuery love all'elemento canvas per creare un plugin molto robusto.
Nella prima parte, abbiamo cercato esclusivamente di implementare la logica del plug-in come script autonomo. Alla fine della prima parte, il nostro grafico a barre sembrava così.
In questa parte finale, lavoreremo per convertire il nostro codice e renderlo un vero plugin jQuery, aggiungendo alcune sottigliezze visive e infine includendo alcune funzionalità aggiuntive. In definitiva, il nostro risultato sarà simile a questo:
Tutto si è riscaldato? Lasciaci entrare!
Prima di iniziare a convertire il nostro codice in un plug-in, dobbiamo prima esaminare alcune formalità quando si tratta della creazione di plug-in.
Iniziamo scegliendo un nome per il plugin. ho scelto istogramma e rinominato il file JavaScript in jquery.barGraph.js. Alleghiamo ora tutto il codice dell'articolo precedente all'interno del seguente frammento.
$ .fn.barGraph = function (settings) // code here
impostazioni contiene tutti i parametri opzionali passati al plugin.
Nella creazione di plug-in jQuery, generalmente si considera una best practice da utilizzare jQuery invece di $ alias nel codice, al fine di ridurre al minimo i conflitti con altre librerie Javascript. Invece di affrontare tutti questi problemi, possiamo semplicemente usare alias personalizzati come menzionato nei documenti jQuery. Alleghiamo tutto il nostro codice plugin all'interno di questa funzione anonima autoeseguibile, come mostrato di seguito:
(function ($) $ .fn.barGraph = function (settings) // codice di implementazione del plugin qui) (jQuery);
In sostanza, incapsuliamo tutto il nostro codice all'interno di una funzione e passiamo a jQuery. Siamo liberi di usare l'alias $ tanto quanto vogliamo nel nostro codice ora, senza doverci preoccupare potenzialmente di essere in conflitto con altre librerie JavaScript.
Quando si progetta un plug-in, è consigliabile esporre un numero ragionevole di impostazioni all'utente, mentre si utilizzano opzioni predefinite sensate se gli utenti utilizzano il plug-in senza passare alcuna opzione. Con questo in mente, consentiremo all'utente di modificare ciascuna delle variabili di opzioni del grafico che ho menzionato in questo precedente articolo di questa serie. Fare così è facile; definiamo semplicemente ciascuna di queste variabili come proprietà di un oggetto e quindi le accediamo.
var defaults = barSpacing = 20, barWidth = 20, cvHeight = 220, numYlabels = 8, xOffset = 20, maxVal, gWidth = 550, gHeight = 200; ;
Abbiamo finalmente bisogno di unire le opzioni predefinite con le opzioni passate, dando la preferenza a quelle passate. Questa linea si prende cura di questo.
opzione var = $ .extend (default, impostazioni);
Ricordarsi di cambiare i nomi delle variabili laddove necessario. Come in -
return (param * barWidth) + ((param + 1) * barSpacing) + xOffset;
… cambia in:
return (param * option.barWidth) + ((param + 1) * option.barSpacing) + option.xOffset;
Questo è dove il plugin è martellato. La nostra vecchia implementazione poteva solo produrre un singolo grafico in una pagina e la possibilità di creare più grafici in una pagina è il motivo principale per cui stiamo creando un plug-in per questa funzionalità. Inoltre, dobbiamo assicurarci che l'utente non abbia bisogno di creare un elemento canvas per ogni grafico da creare. Con questo in mente, creeremo gli elementi del canvas in modo dinamico a seconda delle necessità. Procediamo. Esamineremo le versioni precedenti e aggiornate delle parti rilevanti del codice.
Prima di iniziare, vorrei sottolineare come verrà invocato il nostro plug-in.
$ ("# anni"). barGraph (barSpacing = 30, barWidth = 25, numYlabels = 12,);
Semplice come quella. anni è l'ID della tabella che contiene tutti i nostri valori. Passiamo alle opzioni se necessario.
Per iniziare, abbiamo prima bisogno di un riferimento alla fonte di dati per i grafici. Ora accediamo all'elemento source e otteniamo il suo ID. Aggiungi la seguente riga al grappolo di variabili del grafico che abbiamo dichiarato in precedenza.
var dataSource = $ (this) .attr ("id");
Definiamo una nuova variabile e assegniamo il valore dell'attributo ID dell'elemento passato. All'interno del nostro codice, Questo si riferisce all'elemento DOM attualmente selezionato. Nel nostro esempio, si riferisce alla tabella con un ID di anni.
Nell'implementazione precedente, l'ID per l'origine dati era codificato in hard. Ora lo sostituiamo con l'attributo ID che abbiamo estratto in precedenza. La versione precedente di grabValues la funzione è di seguito:
function grabValues () // Accede alla cella della tabella richiesta, estrae e aggiunge il suo valore alla matrice dei valori. $ ("# data tr td: nth-child (2)"). each (function () gValues.push ($ (this) .text ());); // Accede alla cella della tabella richiesta, estrae e aggiunge il suo valore alla matrice xLabels. $ ("# data trtd: nth-child (1)"). each (function () xLabels.push ($ (this) .text ()););
È aggiornato a questo:
function grabValues () // Accede alla cella della tabella richiesta, estrae e aggiunge il suo valore alla matrice dei valori. $ ("#" + dataSource + "trtd: nth-child (2)"). each (function () gValues.push ($ (this) .text ());); // Accede alla cella della tabella richiesta, estrae e aggiunge il suo valore alla matrice xLabels. $ ("#" + dataSource + "trtd: nth-child (1)"). each (function () xLabels.push ($ (this) .text ()););
function initCanvas () $ ("#" + dataSource) .after (""); // Prova ad accedere all'elemento canvas cv = $ (" # bargraph - "+ dataSource) .get (0); if (! Cv.getContext) return; // Cerca di ottenere un contesto 2D per tela e genera un errore se non è in grado di ctx = cv.getContext ('2d'); if (! ctx) return;
Creiamo un elemento canvas e lo inseriamo nel DOM dopo la tabella, che funge da origine dati. jQuery di dopo la funzione è davvero utile qui. Un attributo di classe di istogramma e un attributo ID nel formato BarGraph-DataSourceID viene anche applicato per consentire all'utente di ridisegnarli tutti come gruppo o individualmente secondo necessità.
Ci sono due modi in cui puoi effettivamente invocare questo plugin. Potresti creare ogni grafico separatamente passando solo una fonte di dati o potresti passare in un certo numero di fonti. In quest'ultimo caso, il nostro costrutto corrente incontrerà un errore e si chiuderà. Per rettificare questo, usiamo il ogni costruire per iterare sul set di elementi passati.
(function ($) $ .fn.barGraph = function (settings) // Variabili opzionali var defaults = // opzioni qui; // Unisci i parametri passati con l'opzione var di default = $ .extend (valori predefiniti, impostazioni // Passa attraverso ogni oggetto passato this.each (function () // Codice di implementazione qui); // Restituisce l'oggetto jQuery per consentire la concatenabilità. Restituisci questo;) (jQuery);
Incapsuliamo tutto il codice dopo aver ottenuto e unito le impostazioni all'interno di this.each costruire. Ci assicuriamo inoltre di restituire l'oggetto jQuery alla fine per abilitare la concatenabilità.
Con questo, il nostro refactoring è completo. Dovremmo essere in grado di invocare il nostro plug-in e creare tutti i grafici necessari.
Ora che la nostra conversione è completa, possiamo lavorare per renderla visivamente migliore. Faremo un certo numero di cose qui. Guarderemo ognuno di loro separatamente.
La versione precedente utilizzava un grigio blando per disegnare i grafici. Ora implementeremo un meccanismo di tematizzazione per le barre. Questo, di per sé, consiste in una serie di passaggi.
var defaults = // Altre impostazioni predefinite qui theme: "Ocean",;
Aggiungiamo a tema opzione ai valori predefiniti che consente all'utente di cambiare il tema in uno dei quattro preset disponibili.
function grabValues () // Switch codice precedente (option.theme) case 'Ocean': gTheme = thBlue; rompere; caso 'Foliage': gTheme = thGreen; rompere; caso 'Cherry Blossom': gTheme = thPink; rompere; case 'Spectrum': gTheme = thAssorted; rompere;
Un semplice interruttore costruisce guarda al option.theme impostazione e punti il gTheme variabile alla matrice di colori necessaria. Usiamo nomi descrittivi per i temi invece di quelli generici.
// Themes var thPink = ['#FFCCCC', '# FFCCCC', '# FFC0C0', '# FFB5B5', '# FFADAD', '# FFA4A4', '# FF9A9A', '# FF8989', '# FF6D6D ']; var thBlue = ['# ACE0FF', '# 9CDAFF', '# 90D6FF', '# 86D2FF', '# 7FCFFF', '# 79CDFF', '# 72CAFF', '# 6CC8FF', '# 57C0FF']; var thGreen = ['# D1FFA6', '# C6FF91', '# C0FF86', '# BCFF7D', '# B6FF72', '# B2FF6B', '# AAFE5D', '# A5FF51', '# 9FFF46']; var thAssorted = ['# FF93C2', '# FF93F6', '# E193FF', '# B893FF', '# 93A0FF', '# 93D7FF', '# 93F6FF', '# ABFF93', '# FF9B93'];
Quindi definiamo un numero di matrici, ciascuna con una serie di sfumature di un colore specifico. Iniziano con la tonalità più chiara e continuano ad aumentare. Effettueremo un ciclo su questi array in seguito. Aggiungere temi è semplice come aggiungere una matrice per il colore specifico di cui hai bisogno, e quindi modificare la precedente interruttore per riflettere i cambiamenti.
function getColour (param) return Math.ceil (Math.abs (((gValues.length / 2) -param)));
Questa è una funzione minuscola che ci consente di ottenere e applicare un effetto gradiente ai grafici. Essenzialmente, calcoliamo la differenza assoluta tra la metà del numero di valori da rendere e il parametro passato, che è l'indice della voce attualmente selezionata nell'array. In questo modo, siamo in grado di creare una sfumatura uniforme. Poiché abbiamo definito solo nove colori in ciascuna matrice di colori, siamo limitati a diciotto valori su un grafico. Estendere questo numero dovrebbe essere abbastanza banale.
function drawGraph () for (index = 0; indexQuesto è il punto in cui effettivamente trattiamo i grafici. Invece di impostare un valore statico per fillStyle proprietà, usiamo il getColour funzione per recuperare l'indice necessario dell'elemento nell'array del tema correntemente selezionato.
Opacità
In seguito, daremo all'utente la possibilità di controllare l'opacità delle barre disegnate. Impostazioni questa è una procedura in due fasi.
Senza trasparenza
Con un valore di 0.8Aggiungendolo alle Opzioni
var defaults = // Altre impostazioni predefinite qui barOpacity: 0.8,;Aggiungiamo a barOpacity opzione ai valori predefiniti, consentendo all'utente di modificare l'opacità dei grafici su un valore compreso tra 0 e 1, dove 0 è completamente trasparente e 1 è completamente opaco.
Impostazione del globalAlpha
function drawGraph () for (index = 0; indexIl globalAlpha proprietà controlla l'opacità o la trasparenza dell'elemento renderizzato. Impostiamo il valore di questa proprietà sul valore passato o sul valore predefinito per aggiungere un po 'di trasparenza. Come impostazione predefinita, utilizziamo un valore di 0,8 per renderlo trasparente.
Griglia
Una griglia può essere estremamente utile per elaborare i dati presentati in un grafico. Anche se inizialmente volevo una griglia adeguata, in seguito ho optato per una serie di linee orizzontali allineate con le etichette dell'asse Y e ho completamente gettato via le linee verticali, dato che si sono appena intromesse nei dati. Con quello fuori strada, andiamo a implementare un modo per renderlo.
Con la griglia disabilitata
Con la griglia abilitataCreare le linee usando i percorsi e il lineTo il metodo sembrava essere la soluzione più ovvia per disegnare i grafici, ma mi è capitato di imbattersi in un bug di rendering che rendeva questo approccio inadatto. Quindi sto attaccando con il fillRect metodo per creare anche queste linee. Ecco la funzione nella sua interezza.
function drawGrid () for (index = 0; indexQuesto è molto simile al disegno delle etichette dell'asse Y, eccetto che invece di rendere un'etichetta, disegniamo una linea orizzontale che copre la larghezza del grafico con una larghezza di 1 px. Il y la funzione ci aiuta nel posizionamento.
Aggiungendolo alle Opzioni
var defaults = // Altre impostazioni predefinite disableGrid: false,;Aggiungiamo a disableGrid opzione ai valori predefiniti, che consente all'utente di controllare se una griglia è visualizzata o meno. Di default, è reso.
// Funzione chiama se (! Option.disableGrid) drawGrid ();Controlliamo solo se l'utente vuole che la griglia sia resa e proceda di conseguenza.
Lineamenti
Ora che le barre sono tutte colorate, non ha accento su uno sfondo più chiaro. Per rettificare questo, abbiamo bisogno di un colpo da 1px. Ci sono due modi per farlo. Il primo, e più semplice, sarebbe semplicemente aggiungere un strokeRect metodo per il drawGraph metodo; oppure, potremmo usare il lineTo metodo per tracciare rapidamente i rettangoli. Ho scelto la prima via da prima lineTo metodo mi ha lanciato qualche strano bug di rendering.
Senza carezze
Con carezzeAggiungendolo alle opzioni
Per prima cosa lo aggiungiamo al default oggetto per dare all'utente il controllo se questo è applicato o meno.
var defaults = // Altri valori di default qui showOutline: true,;function drawGraph () // Codice precedente if (option.showOutline) ctx.fillStyle = "# 000"; ctx.strokeRect (x (indice), y (gValori [indice]), larghezza (), altezza (gValori [indice])); // Resto del codiceControlliamo se l'utente vuole rendere i contorni e, in caso affermativo, procediamo. Questo è quasi lo stesso del rendering delle barre effettive tranne quella invece di usare il fillRect metodo usiamo il strokeRect metodo.
Ombreggiatura
Nell'implementazione originale, non vi è alcuna differenziazione tra l'elemento canvas stesso e lo spazio di rendering effettivo delle barre. Rettificheremo questo ora.
Senza sfumature
Con ombreggiaturafunction shadeGraphArea () ctx.fillStyle = "# F2F2F2"; ctx.fillRect (option.xOffset, 0, gWidth-option.xOffset, gHeight);Questa è una funzione minuscola che ombreggia l'area richiesta. Copriamo l'elemento di tela meno l'area coperta dalle etichette di entrambi gli assi. I primi due parametri indicano le coordinate xey del punto iniziale e gli ultimi due indicano la larghezza e l'altezza richieste. Iniziando da option.offset, eliminiamo l'area coperta dalle etichette dell'asse Y e limitando l'altezza a gHeight, eliminiamo le etichette dell'asse X..
Aggiunta di funzionalità
Ora che il nostro grafico sembra abbastanza carino, possiamo concentrarci sull'aggiunta di alcune nuove funzionalità al nostro plugin. Li esamineremo separatamente.
Considera questo grafico dei famosi picchi 8K.
Quando il valore più alto è sufficientemente alto e la maggior parte dei valori scende entro il 10% del valore massimo, il grafico cessa di essere utile. Abbiamo due modi per rettificare questo.
ShowValue
Iniziamo con la soluzione più semplice. Rendendo il valore dei rispettivi grafici nella parte superiore, il problema è praticamente risolto in quanto i singoli valori possono essere facilmente differenziati. Ecco come è implementato.
var defaults = // Altri valori di default qui showValue: true,;Per prima cosa aggiungiamo una voce al default oggetto per consentire all'utente di accenderlo e spegnerlo a volontà.
// Funzione chiama if (option.showValue) drawValue ();Controlliamo se l'utente vuole che il valore sia mostrato e proceda di conseguenza.
function drawValue () for (index = 0; indexNoi iteriamo attraverso il gValues matrice e renderizza ogni valore individualmente. I calcoli che coinvolgono valAsString e Valx non sono altro che piccoli calcoli per aiutarci nelle indentazioni corrette, quindi non sembra fuori luogo.
Scala
Questo è il più difficile delle due soluzioni. In questo metodo, invece di avviare le etichette dell'asse Y su 0, iniziamo molto più vicino al valore minimo. Spiegherò mentre andiamo. Si noti che, nell'esempio sopra, la differenza tra i valori successivi rispetto al valore massimo è piuttosto insignificante e non mostra la sua efficacia. Altri set di dati dovrebbero facilitare l'analisi dei risultati.
Aggiungendolo alle opzioni
var defaults = // Altre impostazioni di default qui scale: false;Aggiornamento della funzione di scala
Dal momento che il scala la funzione è parte integrante del processo di rendering, è necessario aggiornarlo per consentire la funzionalità di ridimensionamento. Lo aggiorniamo in questo modo:
function scale (param) return ((option.scale)? Math.round (((param-minVal) / (maxVal-minVal)) * gHeight): Math.round ((param / maxVal) * gHeight));So che questo sembra un po 'complicato, ma sembra solo a causa dell'uso dell'operatore condizionale ternario. In sostanza, controlliamo il valore di option.scale e se dice falso, viene eseguito il codice più vecchio. Se è vero, invece di normalizzare il valore come funzione del valore massimo nell'array, ora lo normalizziamo in funzione della differenza tra i valori massimo e minimo. Il che ci porta a:
Aggiornamento del maxValues Funzione
Ora dobbiamo trovare sia il valore massimo che il valore minimo, rispetto al massimo che avevamo prima. La funzione è aggiornata a questo:
function minmaxValues (arr) maxVal = 0; per (i = 0; iparseInt (arr [i])) minVal = parseInt (arr [i]); maxVal * = 1.1; minVal = minVal - Math.round ((maxVal / 10)); Sono sicuro che potresti ottenere lo stesso risultato in un singolo ciclo senza usare tante linee di codice come me, ma mi sentivo particolarmente non creativo in quel momento, quindi portami con me. Con le formalità di calcolo fuori mano, emettiamo un aumento del 5% al MAXVAL variabile e al MINVAL variabile, sottraiamo un valore pari al 5% di MAXVAL di valore. Questo per garantire che le barre non tocchino sempre la parte superiore e che le differenze tra le etichette dell'asse Y siano uniformi.
Aggiornamento del drawYlabels Funzione
Con tutto il lavoro svolto, procediamo ora ad aggiornare la routine di rendering dell'etichetta dell'asse Y per riflettere il ridimensionamento.
function drawYlabels () ctx.save (); per (indice = 0; indiceAggiornamento piuttosto sostanzioso se me lo chiedi! Il nucleo della funzione rimane lo stesso. Verifichiamo solo se l'utente ha abilitato il ridimensionamento e si ramifica il codice secondo necessità. Se abilitato, alteriamo il modo in cui vengono assegnate le etichette Y per accertarci che aderiscano al nuovo algoritmo. Invece del valore massimo diviso in n numero di numeri equidistanti, calcoliamo ora la differenza tra il valore massimo e minimo, lo dividiamo in numeri uniformemente distanziati e lo sommiamo al valore minimo per costruire il nostro array di etichette dell'asse Y. Dopo questo, procediamo normalmente, rendendo ogni etichetta individualmente. Dato che abbiamo reso lo 0 più in basso manualmente, dobbiamo controllare se il ridimensionamento è abilitato e quindi rendere il valore minimo al suo posto. Non importa le piccole aggiunte numeriche a ciascun parametro passato; è solo per assicurarsi che ogni elemento del grafico si allinei come previsto.
Ridimensionamento dinamico
Nella nostra precedente implementazione, abbiamo codificato con difficoltà le dimensioni del grafico, che presenta una difficoltà significativa quando il numero di valori cambia. Stiamo andando a rettificare questo ora.
Aggiungendolo alle Opzioni
var defaults = // Altri valori predefiniti qui cvHeight: 250, // In px;Consentiamo all'utente di impostare l'altezza dell'elemento canvas da solo. Tutti gli altri valori sono calcolati in modo dinamico e applicati secondo necessità.
Aggiornamento del initCanvas Funzione
Il initCanvas la funzione gestisce tutta l'inizializzazione della tela e, quindi, deve essere aggiornata per implementare la nuova funzionalità.
function initCanvas () $ ("#" + dataSource) .after (""); // Prova ad accedere all'elemento canvas cv = $ (" # bargraph - "+ dataSource) .get (0); cv.width = gValues.length * (option.barSpacing + option.barWidth) + option.xOffset + option.barSpacing; cv.height = option.cvHeight; gWidth = cv.width; gHeight = option.cvHeight-20; if (! cv.getContext) return; // Cerca di ottenere un contesto 2D per il canvas e genera un errore se non è in grado di ctx = cv.getContext ('2d'); if (! ctx) return;Dopo aver iniettato l'elemento canvas, otteniamo un riferimento all'elemento creato. La larghezza dell'elemento canvas viene calcolata in funzione del numero di elementi nell'array - gValues , lo spazio tra ogni barra - option.barSpacing, la larghezza di ogni barra stessa - option.barWidth e infine option.xOffset. La larghezza del grafico cambia dinamicamente in base a ciascuno di questi parametri. L'altezza è modificabile dall'utente e il valore predefinito è 220px con l'area di rendering per la barra stessa 220px. Il 20px è allocato alle etichette dell'asse X..
Nascondere la fonte
Ha senso che l'utente possa voler nascondere la tabella di origine una volta che il grafico è stato creato. Con questo in mente, lasciamo all'utente decidere se rimuovere il tavolo o meno.
var defaults = // Altri valori di default qui hideDataSource: true,;if (option.hideDataSource) $ ("#" + dataSource) .remove ();Controlliamo se l'utente vuole nascondere la tabella e se sì, lo rimuoviamo completamente dal DOM usando jQuery rimuovere metodo.
Ottimizzazione del nostro codice
Ora che tutto il lavoro è stato fatto, possiamo rivedere come ottimizzare il nostro codice. Poiché questo codice è stato scritto interamente a fini didattici, la maggior parte del lavoro è stata incapsulata come funzioni separate e inoltre sono molto più prolissi di quanto debbano essere.
Se vuoi davvero che il codice più snello sia possibile, il nostro intero plugin, escludendo l'inizializzazione e il calcolo, può essere riscritto entro due cicli. Un ciclo attraverso il gValues array per disegnare le barre stesse e le etichette dell'asse X; e il secondo ciclo che itera da 0 a numYlabels per rendere la griglia e le etichette dell'asse Y. Il codice sembrerebbe molto più disordinato, tuttavia, dovrebbe portare ad una base di codice significativamente più piccola.
Sommario
Questo è tutto gente! Abbiamo creato un plug-in di alto livello completamente da zero. Abbiamo esaminato una serie di argomenti in questa serie, tra cui:
- Osservando lo schema di rendering dell'elemento canvas.
- Alcuni dei metodi di rendering dell'elemento canvas.
- Normalizzare i valori permettendoci di esprimerlo come funzione di un altro valore.
- Alcune tecniche utili per l'estrazione dei dati usando jQuery.
- La logica principale del rendering del grafico.
- Conversione del nostro script in un vero e proprio plugin jQuery.
- Come migliorarlo visivamente ed estenderlo ulteriormente in termini di funzionalità.
Spero ti sia divertito tanto a leggerlo mentre lo scrivevo. Essendo un lavoro a 270 stravaganze, sono sicuro di aver omesso qualcosa. Sentiti libero di scrivere i commenti e chiedermelo. O criticami. O lodatemi. Sai, è la tua chiamata! Buona programmazione!