Canvas From Scratch Manipularea pixelilor

În ultimul articol, ați învățat totul despre transformări, umbre și declivități. Astăzi, vă voi arăta cum să manipulați pixeli în panza; de la accesarea pur și simplu a valorilor de culoare, la editarea imaginilor din panza la fel ca un editor foto.

Aceasta este cu ușurință una dintre cele mai puternice caracteristici construite în pânză direct, și odată ce ați învățat-o, vă garantez că veți avea o întreagă gamă de idei interesante.


Configurare

Veți utiliza același șablon HTML din articolele anterioare, deschideți astfel editorul dvs. preferat și copiați-l în următorul cod:

   Canvas de la zero          

Aceasta nu este altceva decât o pagină HTML de bază cu un pânză element și unele JavaScript care rulează după încărcarea DOM. Nimic nebun.


Plasarea unei imagini pe panza

Puteți manipula pixeli cu ceva desenat pe panza, dar de dragul acestui tutorial, veți folosi imagini. Acest lucru este parțial pentru că este important să vă arătăm cum să încărcați imagini în panza, dar și pentru că capacitatea de a efectua manipularea imaginilor (de exemplu, editarea fotografiilor) este un plus de masiv al acestei tehnologii.

Înainte de a vă arăta cum să accesați valorile pixelilor, hai să plasăm o imagine pe pânză. Simțiți-vă liber să utilizați orice imagine pe care o doriți, dar de dragul acestui exemplu, voi folosi una dintre fotografiile mele de pe Flickr.

Aveți permisiunea de a utiliza această fotografie dacă doriți, pe care o puteți descărca într-o varietate de dimensiuni.

Încărcarea unei imagini în panza necesită doi pași. Primul este de a încărca imaginea într-un HTML imagine element, care se poate face folosind HTML sau prin crearea unui nou element DOM direct în JavaScript. În acest exemplu, veți crea un element DOM nou - este simplu simplu:

var imagine = nou Imagine (); image.src = "sample.jpg"; $ (imagine) .load (funcția () );

Tot ceea ce faceți aici este crearea unui nou Imagine Elementul DOM și atribuirea acestuia unei variabile. Apoi, utilizați această variabilă pentru a încărca imaginea prin setarea src atributul imaginii în calea corectă. Merită remarcat faptul că ați putea încărca o imagine la distanță folosind această tehnică, dar acest lucru ridică câteva probleme pentru noi în continuare pe linie, astfel încât să rămânem cu o imagine stocată local pentru moment. Ultimul pas este să asculți pentru sarcină eveniment care va fi declanșat de îndată ce imaginea sa terminat de încărcat și este disponibilă pentru utilizare.

Odată ce imaginea sa încărcat, o puteți plasa apoi pe panza într-un singur pas ușor. Tot ce trebuie să faceți este să treceți imagine variabilă pe care tocmai ați creat-o într-un apel către drawImage metodă a contextului de redare 2d. Plasați-l în interiorul imagine încărcare eveniment, cum ar fi:

$ (imagine) .load (funcția () ctx.drawImage (imagine, 0, 0););

În acest caz, drawImage metoda ia trei argumente; un element de imagine, precum și X și y coordonează valorile pentru a plasa imaginea pe panza. Acest lucru va atrage imaginea la dimensiunea completă (500px pentru această imagine) și la poziția specificată:

in orice caz, drawImage poate de fapt să ia încă două argumente care definesc lățimea și înălțimea pentru a desena imaginea, cum ar fi:

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

Acest lucru ar atrage imaginea la jumătate din dimensiunea originală (250 px pentru această imagine):

Puteți chiar să faceți lucrurile cu un pas mai departe și să folosiți cele nouă argumente drawImage să atrageți doar o mică parte a imaginii originale, cum ar fi:

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

Acest lucru ar necesita un pătrat de 200px din partea stângă sus a imaginii și să-l desenați pe panza la pătrat de 500px:

În pseudocod, cele nouă drawImage argumentele pot fi descrise ca fiind o sursă de semnificație (și sensul de destinație d):

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

Iar rezultatul este vizualizat în următoarea ilustrație:

Simplu, nu? Cu toată sinceritatea, nimic în panza nu este atât de complicat când o descompuneți și să priviți individual piesele.


Accesarea valorilor Pixel

Acum, că aveți o imagine pe pânză, este timpul să accesați pixelii, astfel încât să îi puteți manipula. Cu toate acestea, haideți să uităm de manipularea lor pentru moment și să se concentreze pur pe accesarea lor ca conceptul ia ceva timp pentru a obține capul în jurul.

Probleme de securitate

Dacă doriți să accesați pixeli folosind panza, trebuie să știți despre limitările de securitate implicate. Aceste limitări vă permit doar să accesați datele din imaginile încărcate pe acestea domeniu ca JavaScript. Acest lucru vă împiedică să accesați o imagine de pe un server de la distanță și apoi să analizați pixelii, deși există o modalitate de a obține în jur, cumva. Din păcate, nu toate browserele tratează JavaScript și imaginile rulează local din sistemul de fișiere (de ex., Fără un nume de domeniu) ca fiind în același domeniu, astfel încât este posibil să primiți erori de securitate. Pentru a obține acest lucru, trebuie fie să rulați restul acestui tutorial într-un mediu local de dezvoltare (cum ar fi MAMP, WAMP sau XAMPP) sau un server Web de la distanță și să accesați fișierele utilizând un nume de domeniu (ca example.com).

Cu asta din drum, hai să ne aruncăm niște pixeli!

Accesarea pixelilor este un pic ciudat

Așa cum am menționat la începutul acestei secțiuni, accesarea valorilor pixelilor în panza durează puțin timp pentru a obține capul în jurul valorii. Acest lucru se datorează modului în care pixelii sunt stocați de panza; ele nu sunt stocate deloc ca pixeli întregi! În schimb, fiecare pixel este împărțit în patru valori separate (roșu, verde, albastru și alfa), iar aceste valori sunt stocate într-o matrice unidimensională, cu toate valorile de culoare pentru ceilalți pixeli. Din acest motiv, nu puteți solicita doar datele dintr-un anumit pixel, cel puțin nu în mod implicit. Lasă-mă să explic.

Pentru a accesa pixeli în panza, trebuie să apelați getImageData metodă a contextului de redare 2d, după cum urmează:

var imagineData = ctx.getImageData (x, y, lățime, înălțime);

Această metodă are patru argumente care descriu o zonă dreptunghiulară a pânzei pe care doriți să o faceți din datele pixelilor; un X și y origine, urmată de a lăţime și înălţime. Se întoarce a CanvasPixelArray care conține toate valorile de culoare pentru pixelii din zona definită. Primul lucru pe care trebuie să-l observați cu CanvasPixelArray este faptul că fiecare pixel are patru valori de culoare, astfel încât indicele primei valori de culoare pentru fiecare pixel din cadrul matricei va fi un multiplu de 4 (0 pentru prima valoare a primului pixel, 4 pentru prima valoare a celui de-al doilea, etc ):

Ce este interesant în legătură cu această matrice (sau enervant, în funcție de modul în care te uiți la ea) este că nu există niciun concept de poziționare a coordonatelor (x, y), ceea ce înseamnă că extragerea valorilor de culoare pentru un anumit pixel este puțin mai dificilă decât accesarea unei intervale de două (de exemplu, folosind pixelArray [0] [3] pentru a accesa pixelul la (1, 4)). În schimb, trebuie să utilizați o formulă mică care este de fapt foarte ușor de înțeles după ce este explicată corect:

var redValueForPixel = ((y - 1) * (lățime * 4)) + ((x - 1) * 4);

Puteți afla ce se întâmplă aici? Să ne despărțim și să ne prefacem că vrem să obținem valorile culorilor pixelilor pentru cel mai luminos pixel într-o rețea de pixeli de 3x3 - pixelul la (2, 2).

Dacă vă uitați la cele două imagini anterioare, puteți vedea că valorile culorilor pentru acest pixel vor începe la indexul 16, dar pentru a face acest lucru cu codul trebuie să faceți două lucruri; calculați mai întâi indexul de la începutul rândului pe care este activat pixelul ( y poziție) și apoi adăugați la acel index numărul de valori de culoare care există între pixel și începutul rândului ( X poziţie). Este un pic de minte-bender, dar poartă cu ea.

Prima parte este ușor, deja știți că există patru valori de culoare pe pixel și deja cunoașteți lățimea grilei (3 pixeli). Pentru a calcula indicele pixelului la rând y (2) treceți aceste valori prin prima parte a formulei, care ar arăta astfel:

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

Acest lucru vă oferă un indice de 12, pe care veți vedea că se potrivește cu primul pixel din al doilea rând din imaginile anterioare. Până acum, bine.

Următorul pas este să calculați numărul de valori ale culorilor care există înainte de pixelul pe care îl doriți pe acest rând. Pentru a face acest lucru, pur și simplu înmulțiți numărul de pixeli înainte de cel dorit cu patru. Simplu. În acest caz, a doua parte a formulei ar arăta astfel:

((2-1) * 4)

Puteți să faceți totul dacă doriți, dar răspunsul este 4, care când este adăugat la valoarea anterioară vă oferă un indice de 16. Cool, ey?

Nu mi-ar face griji prea mult despre înțelegerea pe deplin, știu doar că această formulă uimitoare puțin există, astfel încât să puteți obține cu ușurință indicele de valoarea de culoare roșie pentru orice pixel. Pentru a obține indicele celorlalte valori de culoare ale unui pixel (verde, albastru sau alfa), adăugați doar 1, 2 sau 3 la indexul calculat.

Punerea în practică

Acum, că știți cum să apucați orice pixel pe care doriți, să o punem în practică și să luăm valori de culoare dintr-o imagine pentru a schimba culoarea unui fundal web. Acest tip de tehnică ar funcționa excelent ca selector de culoare pentru o aplicație Web de editare a fotografiilor.

Codul pentru acest exemplu este destul de simplu, deci hai să atacăm totul într-un singur pas:

var imagine = nou Imagine (); image.src = "sample.jpg"; $ (imagine) .load (funcția () ctx.drawImage (imagine, 0, 0);); (canvas) .click (funcția (e) var canvasOffset = $ (canvas) .offset (); var canvasX = Math.floor (e.pageX-canvasOffset.left) var canvasY = Math.floor -canvasOffset.top) var imagineData = ctx.getImageData (0, 0, canvas.width, canvas.height); var pixels = imagineData.data; var pixelRedIndex = ((canvasY-1) * (imageData.width * 4) ) + (pixelRedIndex + 1) + "," + pixels [pixelRedIndex + 2] + "," + pixelRedIndex + + pixeli [pixelRedIndex + 3] + ")"; $ ("corp"); css ("backgroundColor"; pixelcolor););

Veți recunoaște primele câteva rânduri din exemplele anterioare. Toate lucrurile noi se află în cadrul funcției de preluare a clicurilor de pe pânză element, care utilizează un pic de jQuery pentru a vă spune când a fost apăsat panza.

În cadrul instrumentului de procesare a clicurilor doriți să realizați pixelul pe care mouse-ul a făcut clic pe pânză. Pentru a face acest lucru, trebuie mai întâi să calculați decalajul în pixeli al poziției din stânga sus a panzei din marginea din stânga sus a ferestrei browserului, puteți utiliza jQuery ofset pentru aceasta. Apoi, puteți deduce pixelul care a făcut clic pe panza scăzând offsetul de la poziția mouse-ului a evenimentului clic (PaginaX și PaginaY). Cu siguranță, trebuie să vă petreceți ceva timp pentru a citi lectura evenimentului cu clicuri JavaScript dacă doriți să înțelegeți mai departe acest lucru.

Următoarele patru linii apucați CanvasPixelArray pentru panza (getImageData), stocați-o într-o variabilă, găsiți indexul valorii culorii roșii pentru pixelul apăsat, calculându-l utilizând formula pe care ați văzut-o mai devreme și apoi stocând valorile culorilor pixelilor ca un CSS RGBA şir. În cele din urmă, ultimul pas este să setați culoarea de fundal a corp element cu cel al pixelului apăsat.

Și cu asta ați terminat. Încearcă-te pentru tine; faceți clic pe imaginea de pe panza și urmăriți fundalul modificării culorii site-ului. Dacă nu funcționează, asigurați-vă că rulați demo-ul pe un server cu un nume de domeniu, așa cum este descris în secțiunea privind problemele de securitate.

A fost o călătorie lungă, dar acum puteți acumula rapid și ușor valorile de culoare ale oricărui pixel pe panza. Ți-am spus că poți de asemenea să schimbi valorile de culoare ale pixelilor pe panza? Nu am făcut-o? Hopa! Să aruncăm o privire la asta acum, e moartă.


Aplicarea efectelor asupra imaginilor

Acum, că puteți accesa valorile culorilor pixelilor din panza, modificarea acestor valori este o briză. De fapt, schimbarea acestor valori de culoare este la fel de simplă ca schimbarea valorilor din CanvasPixelArray și apoi trageți-o înapoi pe pânză. Să aruncăm o privire la cum să facem asta.

Primul pas este să configurați codul ca în secțiunea anterioară. Acest cod încarcă o imagine, o desenează pe pânză și apoi prinde datele pixelilor:

var imagine = nou Imagine (); image.src = "sample.jpg"; (imagine, 0, 0); var imagineData = ctx.getImageData (0, 0, canvas.width, canvas.height); var pixels = imageData.data; var numPixels = imageData.width * imageData.height;);

Până acum, bine. Următorul pas este de a buclă prin fiecare pixel pe panza și de a schimba valorile de culoare. În acest exemplu, veți inversa culorile scăzând valoarea curentă a culorii (de la 0 la 255) de la 255:

pentru (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 ;

Nu se întâmplă nimic nebun aici; pur și simplu înmulțiți numărul pixelilor (eu) cu 4 pentru a obține indexul valorii de culoare roșie pentru acel pixel în CanvasPixelArray. Prin adăugarea a 1 sau 2 la acest număr puteți obține și modifica valorile culorii verde și albastru respectiv.

În cele din urmă, tot ce trebuie să faceți acum este să ștergeți panza (pentru a scăpa de imaginea normală), apoi utilizați putImageData metodă a contextului de redare 2d pentru a desena memoria salvată CanvasPixelArray la panza:

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

Și este sincer tot ceea ce există; reîncărcați browserul și căutați-vă singur. Răcoros, nu-i așa??


Înfășurarea lucrurilor

Există atât de mult mai mult de manipulare pixel în pânză, dar sper că ați experimentat suficient în acest articol pentru a obține sucuri curge. Vă încurajez să explorați în continuare acest domeniu și să vedeți ce puteți face cu pixelii. De ce? Deoarece toate tehnicile pe care le leagă despre manipularea pixelilor pot fi folosite pentru video HTML5, precum și pentru imagini. Acum e grozav!

În următorul articol, ultimul din această serie, vom lua o privire diferită la panza. De data aceasta, veți învăța cum să animați pe pânză, care vă va oferi elementele de bază necesare pentru a crea desene animate, animații și jocuri. Aceasta este, fără îndoială, utilizarea mea preferată de pânză.

Cod