Lerret fra scratch Pixel Manipulation

I den siste artikkelen lærte du alt om transformasjoner, skygger og gradienter. I dag skal jeg vise deg hvordan du kan manipulere piksler i lerret; fra bare å få tilgang til fargeværdier, for å redigere bilder i lerretet akkurat som et bilderedigeringsprogram.

Dette er lett en av de mest kraftige funksjonene som er bygget direkte inn i lerretet, og når du har lært det, garanterer jeg at du får en rekke spennende ideer.


Setter opp

Du skal bruke samme HTML-mal fra tidligere artikler, så åpne din favorittredaktør og kopier i følgende kode:

   Lerret fra grunnen av          

Dette er ikke noe mer enn en grunnleggende HTML-side med en lerret element og noen JavaScript som kjører etter at DOM har lastet inn. Ingenting galt.


Plasser et bilde på lerretet

Du kan manipulere piksler med alt som er trukket på lerretet, men på grunn av denne opplæringen bruker du bilder. Dette er delvis fordi det er viktig å vise deg hvordan du legger bilder inn i lerretet, men også fordi muligheten til å utføre bildemanipulering (f.eks. Redigering av bilder) er et enormt poengpunkt for denne teknologien.

Før jeg viser deg hvordan du får tilgang til pikselverdier, la vi plassere et bilde på lerretet. Du er velkommen til å bruke hvilket som helst bilde du vil, men for dette eksemplets skyld skal jeg bruke en av mine egne bilder fra Flickr.

Du har tillatelse til å bruke dette bildet hvis du ønsker det, som du kan laste ned i en rekke størrelser.

Å legge et bilde i lerret krever to trinn. Den første er å laste bildet i en HTML bilde element, som kan gjøres ved hjelp av HTML eller ved å opprette et nytt DOM-element direkte innenfor JavaScript. I dette eksemplet skal du opprette et nytt DOM-element - det er dødt enkelt:

var image = nytt bilde (); image.src = "sample.jpg"; $ (bilde) .load (funksjon () );

Alt du gjør her er å skape en ny Bilde DOM-element og tilordne det til en variabel. Du bruker da den variabelen til å laste opp bildet ved å sette inn src Attributt av bildet til den riktige banen. Det er verdt å merke seg at du kan laste inn et fjernbilde ved hjelp av denne teknikken, men dette gir noen problemer for oss lenger nedover linjen, slik at vi holder fast med et lokalt lagret bilde for nå. Det siste trinnet er å lytte etter laste Event som skal avfyres så snart bildet er ferdig lastet og tilgjengelig for bruk.

Når bildet er lastet, kan du da plassere det på lerretet i ett enkelt trinn. Alt du trenger å gjøre er å passere bilde variabel som du nettopp har opprettet til en samtale til drawImage metode for 2d-renderingskonteksten. Plasser den inne i bilde laste hendelsen, slik som:

$ (bilde) .load (funksjon () ctx.drawImage (bilde, 0, 0););

I dette tilfellet er det drawImage Metoden tar tre argumenter; et bildeelement, så vel som x og y koordinere verdier for å plassere bildet på lerretet. Dette vil tegne bildet i full størrelse (500px for dette bildet) og på den angitte plasseringen:

derimot, drawImage kan faktisk ta ytterligere to argumenter som definerer bredden og høyden for å tegne bildet, slik som:

ctx.drawImage (bilde, 0, 0, 250, 166);

Dette ville tegne bildet ved halvparten av originalstørrelsen (250px for dette bildet):

Du kan til og med ta ting et skritt videre og bruke hele ni argumentene for drawImage å bare tegne en liten del av det opprinnelige bildet, slik som:

ctx.drawImage (bilde, 0, 0, 200, 200, 0, 0, 500, 500);

Dette ville ta en 200px firkant fra øverst til venstre på bildet og tegne det på lerretet på 500px kvadrat:

I pseudokode, hele ni drawImage Argumenter kan beskrives som så (s meningskilde, og d betyr destinasjon):

ctx.drawImage (bilde, sx, sy, sw, sh, dx, dy, dw, dh);

Og resultatet er visualisert i følgende illustrasjon:

Enkelt, ikke sant? I all ærlighet er ingenting i lerret så komplisert når du bryter det ned og ser på stykkene individuelt.


Få tilgang til pikselverdier

Nå som du har et bilde på lerretet, er det på tide å få tilgang til pikslene, slik at du kan manipulere dem. Men la oss glemme å manipulere dem for nå og konsentrere seg om å få tilgang til dem som konseptet tar litt tid å få hodet rundt.

Sikkerhetsproblemer

Hvis du vil ha tilgang til piksler med lerret, må du være oppmerksom på sikkerhetsbegrensningene som er involvert. Disse begrensningene tillater bare at du får tilgang til dataene fra bilder lastet på det samme domene som JavaScript. Dette forhindrer deg i å få tilgang til et bilde fra en ekstern server og deretter analysere dens piksler, selv om det er en måte å komme seg rundt, en slags. Dessverre behandler ikke alle nettlesere JavaScript og bilder kjøres lokalt fra filsystemet (dvs. uten domenenavn) som under samme domene, slik at du kanskje mottar sikkerhetsfeil. For å komme seg rundt dette må du enten kjøre resten av denne opplæringen på et lokalt utviklingsmiljø (som MAMP, WAMP eller XAMPP) eller en ekstern webserver og få tilgang til filene ved hjelp av et domenenavn (som example.com).

Med det ut av veien, la oss knekke på og få oss noen piksler!

Å få tilgang til piksler er litt merkelig

Som nevnt i begynnelsen av denne delen tar det litt tid å få tilgang til pikselverdier i lerret for å få hodet rundt. Dette skyldes måten at pikslene lagres av lerret; de er ikke lagret som hele piksler i det hele tatt! I stedet blir piksler oppdelt i fire separate verdier (rød, grønn, blå og alfa), og disse verdiene lagres i et endimensjonalt array med alle fargeværdiene for de andre pikslene. På grunn av dette kan du ikke bare be om data fra en bestemt piksel, i det minste ikke som standard. La meg forklare.

For å få tilgang til piksler i lerret må du ringe getImageData metode for 2d-renderingskonteksten, slik som:

var imageData = ctx.getImageData (x, y, bredde, høyde);

Denne metoden tar fire argumenter som beskriver et rektangulært område av lerretet du vil ha pikseldata fra; en x og y opprinnelse, etterfulgt av a bredde og høyde. Den returnerer a CanvasPixelArray som inneholder alle fargeværdiene for piksler i det definerte området. Det første å legge merke til med CanvasPixelArray er at hver piksel har fire fargeværdier, så indeksen for den første fargeværdien for hver piksel i arrayet vil være et flertall på 4 (0 for den første verdien av den første piksel, 4 for den første verdien av den andre, osv. ):

Det som er interessant om dette arrayet (eller irriterende, avhengig av hvordan du ser på det) er at det ikke er noe konsept for koordinatposisjonen (x, y), noe som betyr at henting av fargeværdier for en bestemt piksel er litt vanskeligere enn å få tilgang til en to- dimensjonal array (f.eks. ved å bruke pixelArray [0] [3] for å få tilgang til piksel på (1, 4)). I stedet må du bruke en liten formel som faktisk er veldig lett å forstå når den er forklart riktig:

var redValueForPixel = ((y - 1) * (bredde * 4)) + ((x - 1) * 4);

Kan du finne ut hva som skjer her? La oss slå det ned og late som om vi vil få pixelfargeverdiene for den innerste piksel i et 3x3 pikselrutenett - piksel på (2, 2).

Hvis du ser på de to foregående bildene, kan du se at fargeværdiene for denne piksel begynner på indeksen 16, men for å utføre dette med kode må du gjøre to ting; Først beregner du indeksen på begynnelsen av raden som pikselet er på (den y posisjon), og legg deretter til den indeksen antall fargeværdier som eksisterer mellom piksel og begynnelsen av raden (den x stilling). Det er litt av en tankebender, men bære med det.

Den første delen er enkel, du vet allerede at det er fire fargeværdier per piksel, og du vet allerede bredden på rutenettet (3 piksler). For å beregne indeksen for piksel på rad y (2) du passerer disse verdiene gjennom den første delen av formelen, som vil se slik ut:

((2 - 1) * (3 * 4))

Dette gir deg en indeks på 12, som du ser matcher opp med den første piksel på den andre raden i de forrige bildene. Så langt så bra.

Det neste trinnet er å beregne antall fargeværdier som eksisterer før pikselet du vil ha på denne raden. For å gjøre det, multipliserer du bare antall piksler før det du vil ha med fire. Enkel. I dette tilfellet vil den andre delen av formelen se slik ut:

((2 - 1) * 4)

Du kan jobbe alt ut hvis du vil, men svaret er 4, som når du legger til forrige verdi, gir deg en indeks på 16. Kul øye?

Jeg ville ikke bekymre meg for mye om å forstå det fullt, bare vet at denne fantastiske lille formel eksisterer slik at du enkelt kan få indeksen for den røde fargeværdien for en piksel. For å få indeksen for de andre fargeværdiene til en piksel (grønn, blå eller alfa), legger du bare til 1, 2 eller 3 til den beregnede indeksen.

Sette dette inn i praksis

Nå som du vet hvordan du får tak i en hvilken som helst piksel du vil, la oss sette dette inn i praksis og ta fargeverdier fra et bilde for å endre fargen på en nettsidebakgrunn. Denne typen teknikk ville fungere bra som en fargevalger for et bildebehandlingswebprogram.

Koden for dette eksemplet er ganske grei, så la oss angripe alt på en gang:

var image = nytt bilde (); image.src = "sample.jpg"; $ (bilde) .load (funksjon () ctx.drawImage (bilde, 0, 0);); $ (lerret) .click (funksjon (e) var canvasOffset = $ (lerret). offset (); var canvasX = Math.floor (e.pageX-canvasOffset.left); var canvasY = Math.floor (e.pageY -canvasOffset.top); var imageData = ctx.getImageData (0, 0, canvas.width, canvas.height); varpiksler = imageData.data; var pixelRedIndex = ((canvasY - 1) * (imageData.width * 4) ) + (, canvasX - 1) * 4); var pixelcolor = "rgba (" + piksler [pixelRedIndex] + "," + piksler [pixelRedIndex + 1] + "," + piksler [pixelRedIndex + 2] + " + piksler [pixelRedIndex + 3] + ")"; $ ("kropp"). css ("backgroundColor", pixelcolor););

Du vil gjenkjenne de første linjene fra de foregående eksemplene. Alle de nye ting er innenfor klikkbehandleren på lerret element, som bruker en liten bit av jQuery for å fortelle deg når lerretet har blitt klikket på.

Innenfor klikkbehandleren vil du utarbeide pikselet som musen har klikket på lerretet. For å gjøre dette må du først beregne forskyvningen i piksler øverst til venstre på lerretet fra øverste venstre kant av nettleservinduet, du kan bruke jQuery offset metode for dette. Du kan da utlede pikselet klikket på lerretet ved å subtrahere offset fra museposisjonen til klikkhendelsen (pageX og pageY). Du bør definitivt bruke litt tid på å lese opp på JavaScript-klikkhendelse hvis du vil forstå dette videre.

Følgende fire linjer tar tak i CanvasPixelArray for lerretet (getImageData), lagre den i en variabel, finn indeksen for den røde fargeværdien for den klikkte pixelen ved å beregne den ved hjelp av formelen du så tidligere, og lagrer deretter pikselfargeværdiene som en CSS RGBA streng. Endelig er det siste trinnet å angi bakgrunnsfargen til kropp elementet til det klikket pikselet.

Og med det er du ferdig. Prøv det selv; klikk på bildet på lerretet og se på bakgrunnen av nettsiden endringsfarge. Hvis det ikke fungerer, må du kontrollere at du kjører demoen på en server med et domenenavn, som beskrevet i avsnittet om sikkerhetsproblemer.

Det har vært en lang reise, men du er nå i stand til raskt og enkelt å hente fargeværdiene til noen piksler på lerretet. Fortalte jeg deg at du også kan endre fargeværdiene til piksler på lerretet? Jeg gjorde det ikke? Oops! La oss ta en titt på det nå da er det dødt kult.


Bruk av effekter på bilder

Nå som du kan få tilgang til billedfargeverdiene på lerretet, er det en bris å endre disse verdiene. Faktisk er å endre disse fargeværdiene like enkelt som å endre verdiene i CanvasPixelArray og deretter tegne den tilbake på lerretet. La oss ta en titt på hvordan du gjør det.

Det første trinnet er å sette opp koden som du gjorde i forrige del. Denne koden laster et bilde, trekker det på lerretet, og tar deretter opp pikseldataene:

var image = nytt bilde (); image.src = "sample.jpg"; kr numPixels = imageData.width * imageData.height;);

Så langt så bra. Det neste trinnet er å løpe gjennom hver piksel på lerretet og endre fargeværdiene. I dette eksemplet skal du reversere fargene ved å trekke den nåværende fargeværdien (0 til 255) fra 255:

for (var i = 0; i < numPixels; i++)  pixels[i*4] = 255-pixels[i*4]; // Red pixels[i*4+1] = 255-pixels[i*4+1]; // Green pixels[i*4+2] = 255-pixels[i*4+2]; // Blue ;

Det er ikke noe galt skjer her; du multipliserer bare pikselnummeret (Jeg) med 4 for å få indeksen for den røde fargeværdien for den piksel i CanvasPixelArray. Ved å legge til 1 eller 2 til det nummeret kan du få og endre henholdsvis de grønne og blå fargeværdiene.

Til slutt, alt du trenger å gjøre nå er å fjerne lerretet (for å kvitte seg med det vanlige bildet), og bruk deretter putImageData Metode for 2d-renderingskonteksten for å tegne den lagrede CanvasPixelArray til lerretet:

ctx.clearRect (0, 0, canvas.width, canvas.height); ctx.putImageData (imageData, 0, 0);

Og det er ærlig alt det er til det; Last inn nettleseren din og ta en titt på deg selv. Kult, er det ikke?


Innpakning Ting opp

Det er så mye mer å pikselmanipulering i lerret, men jeg håper at du har opplevd nok i denne artikkelen for å få juiceene dine til å flyte. Jeg oppfordrer deg til å utforske dette området videre og se hva annet du kan gjøre med piksler. Hvorfor? Fordi alle teknikkene du leant om pixelmanipulering, kan brukes til HTML5-video og bilder. Nå er det kult!

I den neste artikkelen, den endelige i denne serien, tar vi et annet blikk på lerret. Denne gangen lærer du å animere på lerretet, som gir deg det grunnleggende som kreves for å lage tegneserier, animasjoner og spill. Dette er uten tvil min favoritt bruk av lerret.