Morsomt med lerret Lag en bargrafikkplugin, del 1

I denne todelte serien kombinerer vi det allsidige lerretelementet med det robuste jQuery-biblioteket for å lage et bargrafikk-plugin. I denne første delen skal vi kode kjernelogikken for plugin som en frittstående versjon.

I dag skal vi lage et bardiagramplugin. Ikke en vanlig plugin, husk deg. Vi viser noen jQuery-kjærlighet til lerretelementet for å lage et meget robust plugin.

I denne todelt artikkelen vil vi starte fra begynnelsen ved å implementere logikken til pluginet som et frittstående skript, refactoring det til et plugin og så til slutt legge til alle de ekstra øyeformene på toppen av plugin-koden. I denne første delen skal vi bare avtale med å implementere kjernelogikken.

Trenger du et eksempel før vi kommer i gang? Værsågod!


Ulike grafer opprettet med å levere forskjellige innstillinger til pluginet vårt

Fornøyd? Interessert ennå? La oss begynne.


funksjonalitet

Vår plugin trenger å utføre noen grunnleggende ting mens du ikke gjør noen andre ting. La meg forklare:

  • Som vanlig skal vi bare bruke lærredselementet og JavaScript. Ingen bilder av noe slag, ingen ødelagte CSS-teknikker, ingen prerendering. Vanlig gammel (eller er det nytt?) Lerret element sammen med noen jQuery å lette vår arbeidsbelastning.
  • Med hensyn til datakilden skal vi trekke alle dataene direkte fra et standardbord. Ingen arrays å overføre på plugin ved oppstart. På denne måten kan brukeren bare sette alle dataene i et bord og deretter påkalle vårt plugin. I tillegg er det mye mer tilgjengelig.
  • Ingen spesiell markering for bordet som fungerer som datakilde og absolutt ingen spesielle klassenavn for dataceller. Vi skal bare bruke IDen til bordet og trekke alle dataene derfra.
  • Ingen spinkel tekstoverlegg for å gjengi etikettene og slikt på grafen. Det er ikke bare svært kjedelig, men den gjengitte teksten er ikke en del av grafen når den er lagret. Vi skal bruke fillText og strokeText som definert av WHATWG-spesifikasjonene.

avhengig

Som vi delver i verden av banebrytende, fortsatt ikke helt spesifisert, teknologi, har vi noen avhengigheter. For lerretelementet til arbeid, er de fleste moderne nettlesere tilstrekkelig. Men siden vi bruker den nye tekstgjenopprettings-API-en, trenger vi nyere bygg. Nettlesere som bruker Webkit-motoren r433xx og over eller Gecko-motoren 1.9.1 og høyere, bør være gode plattformer for plugin. Jeg anbefaler å ta en nattlig konstruksjon av Chromium eller Firefox.


Før vi begynner

Jeg vil gjerne nevne at vår plugin er rent for læringsformål. Dette pluginet er på ingen måte ment å erstatte andre fullverdige grafikkplugger som Flot, Plotr og så videre. Også koden kommer til å være så ordentlig som mulig. Du kan skrive langt, langt Mer effektiv kode, men for lærings skyld er alt som skal være så ukomplisert som mulig. Du kan gjerne reflektere den til fordel for effektiviteten i produksjonskoden din.


HTML-merkingen

   OMG WTF HAX   
År salgs~~POS=TRUNC
2009 130
2008 200
2007 145
2006 140
2005 210
2004 250
2003 170
2002 215
2001 115
2000 135
1999 110
1998 180
1997 105

Ingenting spesielt med markeringen. Jeg vil likevel gjøre en rask oversikt.

  • Vi begynner med å inkludere den nødvendige doktypen. Siden vi bruker lerretelementet, bruker vi den riktige for HTML 5.
  • Datakildetabellen defineres deretter. Vær oppmerksom på at ingen spesiell oppføring blir beskrevet eller at nye klasser blir definert og tilordnet i sine medlemmer.
  • Et lerretelement er definert og tilordnet en ID for senere referanse til. Dette bestemte lerretelementet vil bare være her for den frittstående versjonen. I plugin-versjonen blir lerretelementet og dets attributter injisert dynamisk i DOM og deretter manipulert etter behov. For progressiv forbedring fungerer denne måten mye bedre.
  • Til slutt inkluderer vi jQuery-biblioteket og vårt tilpassede skript. Som Jeffrey har nevnt igjen og igjen, inkludert skript på slutten av dokumentet er alltid en god ide.

The Canvas Grid

Før vi starter Javascript, la meg forklare lerretskoordinatsystemet. Det øverste venstre hjørnet virker som opprinnelsen, dvs. (0, 0). Poeng blir da målt med hensyn til opprinnelsen med x økende langs høyresiden og y øker til venstre. For den matematisk tilbøyelige, jobber vi effektivt i fjerde kvadrant bortsett fra at vi tar absolutt verdien av y i stedet for sin negative verdi. Hvis du har jobbet med grafikk på andre språk, bør du være hjemme her.


Lerretskoordinatsystemet

Rektanguleringsgengivelsesrutinen

Canvas 'rektangel rendering rutine vil bli brukt omfattende gjennom ut artikkelen for å gjengi stolper, rutenettet og noen andre elementer. Med det i tankene, la oss ta en kort titt på de rutinene.

Av de tre tilgjengelige rutene vil vi bruke fillRect og strokeRect metoder. De fillRect Metoden fyller faktisk det gjengitte rektangelet mens strokeRect Metoden strekker bare rektanglene. Annet enn det, tar begge metodene de samme parameterne.

  • x - X-koordinaten til punktet hvorfra du skal tegne.
  • y - Y koordinerer med hensyn til opprinnelsen.
  • bredde - Definerer bredden på rektangelet som skal tegnes.
  • høyde - Definerer høyden på rektangelet.

Javascript Magic

Som alltid anbefaler jeg sterkt at du laster ned kildekoden og har den på siden for referanse. Det er lettere å se på det store bildet og analysere hver funksjon en etter en, se hver enkelt funksjon individuelt og deretter lage det store bildet i tankene dine.


Variabel erklæring

 var barSpacing = 20, barWidth = 20, cvHeight = 220, numYlabels = 8, xOffset = 20, gWidth = 550, gHeight = 200; var maxVal, gValues ​​= [], xLabels = [], yLabels = []; var cv, ctx;

Grafvariabler

  • xLabels - En matrise som holder verdien av etikettene til X-aksen.
  • yLabels - Samme som ovenfor, bortsett fra at den inneholder verdiene for Y-akselettene.
  • gValues - Array som inneholder alle grafdataene vi drar av datakilden.
  • CV - Variabel å peke mot lerretelementet.
  • CTX - Variabel for å referere til konteksten av lerretelementet.

Grafalternativvariabler

Disse variablene har hardkodede verdier for å hjelpe oss med posisjonering og layout av grafen og de enkelte stolpene.

  • barSpacing - Definerer avstanden mellom individuelle stenger.
  • barWidth - Definerer bredden på hver enkelt linje.
  • cvHeight - Definerer høyden på lerretelementet. Hardkodd siden vi opprettet lerretelementet på forhånd. Plugin-versjonen varierer i denne funksjonaliteten.
  • numYlabels - Definerer antall etiketter som skal tegnes i Y-aksen.
  • xOffset - Definerer mellomrommet mellom begynnelsen av lerretelementet og selve grafen. Denne plassen benyttes for å tegne etikettene på Y-aksen.
  • gWidth, gHeight - Hardkodede verdier som holder dimensjonen til selve reningsplassen til grafen selv.

Hvordan hver variabel styrer utseendet på grafen

Gripe verdiene

Med jQuery's sterke selektormotor blir det veldig enkelt for oss å få de dataene vi trenger. Her har vi en rekke måter å få tilgang til de nødvendige elementene. La meg forklare noen nedenfor:

 $ ("tr"). barn ("td: oddetall"). hver (funksjon () // kode her);

Den enkleste måten å få tilgang til de nødvendige radene. Ser etter en tr element og deretter tilgang til hverandre td element. Mislykkes når du har mer enn ett bord på siden din.

 $ ("# data"). finn ("td: oddetall"). hver (funksjon () // kode her);

En mye mer rett frem vei. Vi passerer inn i ID på bordet og får tilgang til hver andre rad.

 $ ("# data tr td: odd"). hver (funksjon () // kode her);

Samme som ovenfor, bortsett fra at vi bare bruker CSS stilvelger-syntaks.

 $ ("# data tr td: nth-child (2)"). hver (funksjon () // kode her);

Den versjonen vi skal bruke i dag. Denne måten er mye bedre hvis vi trenger å ta data fra en annen rad eller, om nødvendig, flere rader.

Den endelige versjonen ser slik ut:

 funksjon grabValues ​​() // Få tilgang til den nødvendige tabellcellen, trekk ut og legg til verdien til verdiene array. $ ("# data tr td: nth-child (2)"). hver (funksjon () gValues.push ($ (dette) .text ());); // Få tilgang til den nødvendige tabellcellen, utdrag og legg til verdien til xLabels-arrayet. $ ("# data tr td: nth-barn (1)"). hver (funksjon () xLabels.push ($ (dette) .text ());); 

Ingenting komplisert her. Vi bruker kodestykket som er nevnt ovenfor for å legge til verdien av tabellcellen til gValues array. Deretter gjør vi det samme bortsett fra at vi åpner den første tabellcellen for å trekke ut den nødvendige etiketten for x-aksen. Vi har innkapslet datautvinningslogikken til sin egen funksjon for kodegenbruk og lesbarhet.


Initialisering av lærred

 funksjon initCanvas () // Prøv å få tilgang til lerretelementet og kaste en feil hvis den ikke er tilgjengelig cv = $ ("# graph"). få (0); hvis (! cv) return;  // Prøv å få en 2D-kontekst for lerretet og kaste en feil hvis det ikke er mulig å ctx = cv.getContext ('2d'); hvis (! ctx) return; 

Rutinemessig lærred initialisering. Først prøver vi å få tilgang til lerretelementet selv. Vi kaster en feil hvis ikke. Deretter prøver vi å få en referanse til 2d-renderingskonteksten gjennom getContext metode og kaste en feil hvis vi ikke klarer å gjøre det.


Utility Funksjoner

Før vi går inn i selve gjengivelsen av grafen selv, må vi se på en rekke bruksfunksjoner som hjelper oss sterkt i prosessen. Hver av dem er liten av seg selv, men vil bli brukt mye i hele koden vår.

Bestemme maksimumsverdien

 funksjon maxValues ​​(arr) maxVal = 0; for (i = 0; i 

En liten funksjon som iterates gjennom passordet og oppdaterer Maxval variabel. Vær oppmerksom på at vi blåser maksimumsverdien med 10% til spesielle formål. Hvis maksimumverdien er igjen som den er, vil linjen som representerer den øverste verdien, berøre kanten av lerretelementet som vi ikke vil ha. Med det i betraktning er en 10% økning utstedt.

Normalisering av verdien

 funksjonsskala (param) return Math.round ((param / maxVal) * gHeight); 

En liten funksjon for å normalisere den ekstraherte verdien med hensyn til høyde på lerretelementet. Denne funksjonen brukes mye i andre funksjoner og direkte i vår kode for å uttrykke verdien som en funksjon av lerretets høyde. Tar en enkelt parameter.

Returnerer X-koordinaten

 funksjon x (param) retur (param * barWidth) + ((param + 1) * barSpacing) + xOffset; 

Returnerer x ordinaten til fillRect å hjelpe oss med å plassere hver enkelt bar. Jeg vil forklare dette litt mer detaljert når det blir brukt.

Returnerer Y-koordinaten

 funksjon y (param) retur gHeight - skala (param); 

Returnerer y ordinaten til fillRect metode for å hjelpe oss med posisjoneringen av hver enkelt bar. Flere forklaringer litt senere.

Tilbake til bredden

 funksjonsbredde () return barWidth; 

Returnerer bredden på hver enkelt linje.

Returnerer høyden

 funksjonshøyde (param) returskala (param); 

Returnerer høyden på linjen som skal tegnes. Bruker den skala fungere for å normalisere verdien og deretter returnere den til den som ringer.


Tegne X-akse-etikettene

 funksjon drawXlabels () ctx.save (); ctx.font = "10px 'arial'"; ctx.fillStyle = "# 000"; for (indeks = 0; indeks 

En enkel funksjon for å gjengi etikettene til x-aksen. Vi lagrer først den nåværende tilstanden på lerretet, inkludert alle gjengivelsesinnstillingene, slik at alt vi gjør inne i funksjonene aldri lekker ut. Deretter setter vi størrelsen og fonten på etikettene. Deretter gjenspeiler vi gjennom xLabels array og ring til fillText Metode hver gang å gjøre etiketten. Vi bruker x Fungerer for å hjelpe oss med å plassere etikettene.


Tegne Y-akse-etikettene

 funksjon drawYlabels () ctx.save (); for (indeks = 0; indeks 

En litt mer ordentlig funksjon. Vi lagrer først lerretets nåværende tilstand og fortsetter deretter. Neste deler vi Maxval s Verdien inn i n elementer der variabelen numYlabels dikterer n. Disse verdiene legges da til yLabels array. Nå, som vist ovenfor, fillText Metoden kalles for å tegne de enkelte etikettene med y Funksjonen hjelper oss med å plassere hver enkelt etikett.

Vi gjør en null nederst på lerretet for å fullføre tegningen av Y-etikettene.


Tegning av grafen

 funksjon drawGraph () for (index = 0; index  

Funksjonen som trekker de faktiske stolpene i strekediagrammet. Iterates gjennom gValues array og gjør hver enkelt bar. Vi bruker fillRect metode for å tegne stolpene. Som forklart ovenfor, tar metoden fire parametre, som hver er tatt vare på av våre bruksfunksjoner. Den nåværende indeksen av sløyfen overføres til våre funksjoner som parametere sammen med den faktiske verdien som holdes i matrisen, etter behov.

De x funksjonen returnerer x-koordinaten til linjen. Hver gang økes den med verdien av summen av barWidth og barSpacing variabler.

De y funksjonen beregner forskjellen mellom lerretelementets høyde og de normaliserte dataene og returnerer den. Jeg vet at dette høres litt opp, men dette skyldes det faktum at y-verdiene på lerretgitteret øker ved å flytte ned mens i grafen øker y-verdiene ved å flytte opp. Dermed må vi gjøre litt arbeid for å få det til å fungere som vi ønsker.

De bredde funksjonen returnerer bredden på de enkelte stolpene selv.

De høyde funksjon returnerer bare den normaliserte verdien som skal brukes som høyden på linjen som skal tegnes.


Sammendrag

I denne første delen har vi implementert basalogikken til plugin-modulen vår som en frittstående versjon med utseende og egenskaper av bare ben. Vi har gjennomgått lerretskoordinatsystemet, rektangulære gjengivelsesmetoder, noe greit datautvinning ved hjelp av jQuery's medfødte awesomeness [Har jeg nevnt hvor mye jeg liker jQuery?], Så på hvordan etikettene er trukket, og endelig sett på logikken bak gjengivelsen av selve grafen.

På slutten av denne artikkelen bør utgangen se slik ut.


Neste!

Vår nåværende gjennomføring er ganske mangelfull. Det ser blid ut, kan ikke opprette flere grafer på samme side, og la oss innse det, er ganske spartansk på funksjonens front. Vi skal takle alt det neste uke. I neste artikkel vil vi:

  • Refactor vår kode mot å gjøre det til en fullverdig jQuery-plugin.
  • Legg litt øye snus.
  • Ta med noen flotte små funksjoner.

Spørsmål? Kritikk? Ros? Du er velkommen til å treffe kommentarene. Takk for at du leser, og når du er klar, gå videre til del to!

  • Følg oss på Twitter, eller abonner på NETTUTS RSS Feed for flere daglige webutviklingsverktøy og artikler.