In het laatste artikel leerde je alles over transformaties, schaduwen en hellingen. Vandaag laat ik je zien hoe je pixels in canvas kunt manipuleren; van het eenvoudig toegang krijgen tot kleurwaarden tot het bewerken van afbeeldingen in het canvas, net als een foto-editor.
Dit is gemakkelijk een van de krachtigste functies die direct in canvas is ingebouwd, en als je het eenmaal hebt geleerd, garandeer ik dat je een hele reeks spannende ideeën zult hebben.
Je gaat dezelfde HTML-sjabloon gebruiken van de vorige artikelen, dus open je favoriete editor en kopieer in de volgende code:
Canvas helemaal opnieuw
Dit is niets meer dan een eenvoudige HTML-pagina met een canvas
element en een JavaScript dat wordt uitgevoerd nadat de DOM is geladen. Niks gek.
Je kunt pixels manipuleren met alles dat op het canvas is getekend, maar in het belang van deze tutorial gebruik je afbeeldingen. Dit is gedeeltelijk omdat het belangrijk is om je te laten zien hoe je afbeeldingen in het canvas laadt, maar ook omdat de mogelijkheid om beeldmanipulatie uit te voeren (bijvoorbeeld het bewerken van foto's) een enorm pluspunt is van deze technologie.
Voordat ik u laat zien hoe u toegang krijgt tot pixelwaarden, laten we een afbeelding op het canvas plaatsen. Voel je vrij om elke afbeelding te gebruiken die je maar wilt, maar omwille van dit voorbeeld ga ik een van mijn eigen foto's van Flickr gebruiken.
U hebt toestemming om deze foto te gebruiken als u dat wilt, die u in verschillende formaten kunt downloaden.
Het laden van een afbeelding in canvas vereist twee stappen. De eerste is om de afbeelding in een HTML te laden beeld
element, dat kan worden gedaan met behulp van HTML of door een nieuw DOM-element rechtstreeks in JavaScript te maken. In dit voorbeeld ga je een nieuw DOM-element maken - het is doodeenvoudig:
var image = new Afbeelding (); image.src = "sample.jpg"; $ (afbeelding) .load (function () );
Het enige wat je hier doet is een nieuw maken Beeld
DOM-element en toewijzen aan een variabele. Vervolgens gebruikt u die variabele om uw afbeelding te laden door de src
attribuut van de afbeelding naar het juiste pad. Het is de moeite waard om op te merken dat je een externe afbeelding kunt laden met deze techniek, maar dit werpt een aantal problemen op voor ons verderop in de rij, dus we zullen voorlopig een lokaal opgeslagen beeld behouden. De laatste stap is om te luisteren naar de laden
gebeurtenis die wordt afgevuurd zodra de afbeelding is geladen en beschikbaar is voor gebruik.
Zodra de afbeelding is geladen, kunt u deze in één eenvoudige stap op het canvas plaatsen. Het enige wat u hoeft te doen is de beeld
variabele die u zojuist hebt gemaakt in een oproep naar de drawImage
methode van de 2e rendering-context. Plaats het in de beeld
gebeurtenis laden, zoals zo:
$ (afbeelding) .load (function () ctx.drawImage (afbeelding, 0, 0););
In dit geval, de drawImage
methode neemt drie argumenten; een afbeeldingselement, evenals de X en Y coördineer waarden om de afbeelding op het canvas te plaatsen. Hiermee wordt de afbeelding op volledige grootte getekend (500 px voor deze afbeelding) en op de opgegeven positie:
Echter, drawImage
kan eigenlijk nog twee argumenten nemen die de breedte en hoogte bepalen om de afbeelding te tekenen, zoals zo:
ctx.drawImage (afbeelding, 0, 0, 250, 166);
Hiermee tekent u de afbeelding op de helft van de oorspronkelijke grootte (250 px voor deze afbeelding):
Je kunt zelfs een stap verder gaan en de volledige negen argumenten gebruiken voor drawImage
om slechts een klein deel van de originele afbeelding te tekenen, zoals zo:
ctx.drawImage (afbeelding, 0, 0, 200, 200, 0, 0, 500, 500);
Dit zou een vierkant van 200px van de linkerbovenkant van het beeld vereisen en het op het canvas op 500px vierkant trekken:
In pseudo-code, de volledige negen drawImage
argumenten kunnen worden beschreven als: so (s betekent bron en bestemming bestemming):
ctx.drawImage (afbeelding, sx, sy, sw, sh, dx, dy, dw, dh);
En het resultaat wordt weergegeven in de volgende afbeelding:
Eenvoudig, toch? Eerlijk gezegd is niets op canvas zo gecompliceerd als je het stukmaakt en de stukjes afzonderlijk bekijkt.
Nu je een afbeelding op het canvas hebt, is het tijd om de pixels te openen, zodat je ze kunt manipuleren. Laten we echter vergeten ze voor nu te manipuleren en ons er volledig op te concentreren om ze te gebruiken, aangezien het een tijdje duurt voordat het concept uw aandacht trekt.
Als u toegang wilt tot pixels met behulp van canvas, moet u op de hoogte zijn van de beveiligingsbeperkingen die hierbij betrokken zijn. Deze beperkingen geven u alleen toegang tot de gegevens van afbeeldingen die op dezelfde zijn geladen domein als de JavaScript. Dit voorkomt dat u toegang krijgt tot een afbeelding vanaf een externe server en vervolgens de pixels analyseert, hoewel er een manier is om er omheen te komen, een soort van. Helaas behandelen niet alle browsers JavaScript en afbeeldingen lokaal vanuit het bestandssysteem (dus zonder een domeinnaam) als onder hetzelfde domein, dus u kunt beveiligingsfouten krijgen. Om dit te voorkomen, moet je de rest van deze tutorial uitvoeren op een lokale ontwikkelomgeving (zoals MAMP, WAMP of XAMPP) of een externe webserver en toegang krijgen tot de bestanden met een domeinnaam (zoals example.com).
Met dat uit de weg, laten we kraken en ons wat pixels geven!
Zoals ik aan het begin van dit gedeelte al zei, duurt het even om toegang te krijgen tot pixelwaarden in canvas. Dit komt door de manier waarop de pixels worden opgeslagen op canvas; ze worden helemaal niet als hele pixels opgeslagen! In plaats daarvan worden pixels verdeeld in vier afzonderlijke waarden (rood, groen, blauw en alpha) en deze waarden worden opgeslagen in een eendimensionale matrix met alle kleurwaarden voor de andere pixels. Hierdoor kunt u niet zomaar de gegevens van een bepaalde pixel opvragen, althans niet standaard. Laat het me uitleggen.
Voor toegang tot pixels in canvas moet u de getImageData
methode van de 2d rendering context, zoals zo:
var imageData = ctx.getImageData (x, y, width, height);
Deze methode neemt vier argumenten die een rechthoekig gebied van het canvas beschrijven waarvan u de pixelgegevens wilt hebben; een X en Y oorsprong, gevolgd door een breedte en hoogte. Het levert a CanvasPixelArray
die alle kleurwaarden voor de pixels binnen het gedefinieerde gebied bevat. Het eerste dat opvalt met de CanvasPixelArray
is dat elke pixel vier kleurwaarden heeft, dus de index van de eerste kleurwaarde voor elke pixel in de array is een veelvoud van 4 (0 voor de eerste waarde van de eerste pixel, 4 voor de eerste waarde van de tweede, enz. ):
Wat interessant is aan deze array (of irritant, afhankelijk van hoe je ernaar kijkt) is dat er geen concept van (x, y) coördinaatpositie is, wat betekent dat het ophalen van kleurwaarden voor een specifieke pixel een beetje moeilijker is dan toegang tot een twee- dimensionale array (bijvoorbeeld met pixelArray [0] [3] om toegang te krijgen tot de pixel op (1, 4)). In plaats daarvan moet u een kleine formule gebruiken die eigenlijk heel gemakkelijk te begrijpen is als deze eenmaal goed is uitgelegd:
var redValueForPixel = ((y - 1) * (width * 4)) + ((x - 1) * 4);
Kun je weten wat hier gebeurt? Laten we het opsplitsen en doen alsof we de pixelkleurwaarden voor de binnenste pixel in een 3x3 pixelraster willen krijgen - de pixel op (2, 2).
Als u de vorige twee afbeeldingen bekijkt, kunt u zien dat de kleurwaarden voor deze pixel bij index 16 beginnen, maar om dit met code uit te werken, moet u twee dingen doen; bereken eerst de index aan het begin van de rij waarop de pixel staat (de Y positie) en voeg vervolgens aan die index het aantal kleurwaarden toe dat bestaat tussen de pixel en het begin van de rij (de X positie). Het is een beetje een hersenkraker, maar houd ermee bezig.
Het eerste deel is eenvoudig, je weet al dat er vier kleurwaarden per pixel zijn en je weet al de breedte van het raster (3 pixels). Om de index van de pixel op rij te berekenen Y (2) u geeft deze waarden door via het eerste deel van de formule, dat er als volgt uitziet:
((2 - 1) * (3 * 4))
Dit geeft je een index van 12, die je ziet matchen met de eerste pixel op de tweede rij van de vorige afbeeldingen. Tot zover goed.
De volgende stap is het berekenen van het aantal kleurwaarden dat vóór de gewenste pixel in deze rij bestaat. Om dat te doen, vermenigvuldigt u eenvoudig het aantal pixels vóór dat u wilt met vier. Eenvoudig. In dit geval zou het tweede deel van de formule er als volgt uitzien:
((2 - 1) * 4)
Je kunt het allemaal uitwerken als je wilt, maar het antwoord is 4, en als het wordt toegevoegd aan de vorige waarde, krijg je een index van 16. Cool, ey?
Ik zou me niet al te veel zorgen maken om het volledig te begrijpen, weet gewoon dat deze verbazingwekkende kleine formule bestaat, zodat je gemakkelijk de index van de rode kleurwaarde voor elke pixel kunt krijgen. Als u de index van de andere kleurwaarden van een pixel (groen, blauw of alpha) wilt ophalen, voegt u eenvoudig 1, 2 of 3 toe aan de berekende index.
Nu je weet hoe je elke gewenste pixel moet pakken, laten we dit in de praktijk brengen en kleurwaarden uit een afbeelding halen om de kleur van de achtergrond van een website te veranderen. Dit soort techniek zou geweldig zijn als kleurenkiezer voor een webapplicatie voor het bewerken van foto's.
De code voor dit voorbeeld is redelijk eenvoudig, dus laten we alles in één keer aanvallen:
var image = new Afbeelding (); image.src = "sample.jpg"; $ (afbeelding) .load (function () ctx.drawImage (afbeelding, 0, 0);); $ (canvas) .klik (functie (e) var canvasOffset = $ (canvas) .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); var pixels = imageData.data; var pixelRedIndex = ((canvasY - 1) * (imageData.width * 4) ) + ((canvasX - 1) * 4); var pixelcolor = "rgba (" + pixels [pixelRedIndex] + "," + pixels [pixelRedIndex + 1] + "," + pixels [pixelRedIndex + 2] + "," + pixels [pixelRedIndex + 3] + ")"; $ ("body"). css ("backgroundColor", pixelcolor););
U herkent de eerste paar regels uit de vorige voorbeelden. Alle nieuwe dingen staan in de click handler op de canvas
element, dat een klein beetje jQuery gebruikt om u te vertellen wanneer op het canvas is geklikt.
Binnen de click handler wilt u de pixel berekenen waarop de muis op het canvas heeft geklikt. Om dit te doen moet je eerst de offset in pixels van de linkerbovenhoek van het canvas berekenen vanaf de linkerbovenrand van het browservenster, je kunt het jQuery gebruiken compenseren
methode hiervoor. U kunt vervolgens de pixel afleiden waarop op het canvas is geklikt door de offset van de muispositie van de klikgebeurtenis af te trekken (pageX
en PageY
). Je moet zeker wat tijd besteden aan het lezen van de JavaScript-klikgebeurtenis als je dit verder wilt begrijpen.
De volgende vier regels pakken het CanvasPixelArray
voor het canvas (getImageData
), sla het op in een variabele, zoek de index van de rode kleurwaarde voor de geklikte pixel door deze te berekenen met de formule die u eerder hebt gezien en sla de pixelkleurwaarden op als een CSS RGBA
draad. Ten slotte is de laatste stap het instellen van de achtergrondkleur van de lichaam
element dat van de geklikte pixel.
En daarmee ben je klaar. Probeer het zelf uit; klik op de afbeelding op het canvas en zie de achtergrond van de website van kleur veranderen. Als het niet werkt, zorg er dan voor dat u de demo uitvoert op een server met een domeinnaam, zoals beschreven in de sectie over beveiligingsproblemen.
Het is een lange reis geweest, maar je kunt nu snel en gemakkelijk de kleurwaarden van elke pixel op het canvas ophalen. Heb ik je verteld dat je ook de kleurwaarden van pixels op het canvas kunt wijzigen? Ik niet? Oops! Laten we daar eens naar kijken, het is dood gaaf.
Nu u toegang hebt tot de pixelkleurwaarden van het canvas, is het wijzigen van die waarden een koud kunstje. In feite is het wijzigen van die kleurwaarden net zo eenvoudig als het wijzigen van de waarden in de CanvasPixelArray
en dan terug te tekenen op het canvas. Laten we eens kijken hoe dat te doen.
De eerste stap is om de code in te stellen zoals in het vorige gedeelte. Deze code laadt een afbeelding, tekent deze op het canvas en pakt vervolgens de pixelgegevens:
var image = new Afbeelding (); image.src = "sample.jpg"; $ (afbeelding) .load (function () ctx.drawImage (afbeelding, 0, 0); var imageData = ctx.getImageData (0, 0, canvas.width, canvas.height); var pixels = imageData.data; var numPixels = imageData.width * imageData.height;);
Tot zover goed. De volgende stap is om elke pixel op het canvas door te laten lopen en de kleurwaarden te wijzigen. In dit voorbeeld ga je de kleuren omdraaien door de huidige kleurwaarde (255) af te trekken van 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 ;
Er is hier niets gek aan de hand; je vermenigvuldigt simpelweg het pixelnummer (ik) door 4 om de index van de rode kleurwaarde voor die pixel in de CanvasPixelArray
. Door 1 of 2 toe te voegen aan dat nummer, kunt u respectievelijk de groene en blauwe kleurwaarden ophalen en wijzigen.
Tot slot, alles wat je nu nog moet doen is om het canvas leeg te maken (om van de normale afbeelding af te komen), en dan de putImageData
methode van de 2e rendering-context om de opgeslagen tekening te tekenen CanvasPixelArray
naar het canvas:
ctx.clearRect (0, 0, canvas.width, canvas.height); ctx.putImageData (imageData, 0, 0);
En dat is eerlijk gezegd alles wat er is; herlaad uw browser en neem een kijkje voor uzelf. Cool, is het niet?
Er is zoveel meer aan pixelmanipulatie in canvas, maar ik hoop dat je genoeg hebt ervaren in dit artikel om je sappen te laten stromen. Ik moedig je aan om dit gebied verder te verkennen en te zien wat je nog meer kunt doen met pixels. Waarom? Omdat alle technieken die u over pixelmanipulatie leunt, zowel voor HTML5-video als voor afbeeldingen kunnen worden gebruikt. Dat is cool!
In het volgende artikel, de laatste in deze serie, bekijken we canvas anders. Deze keer leer je hoe je kunt animeren op het canvas, wat je de basisbegrippen geeft die nodig zijn om cartoons, animaties en games te maken. Dit is ongetwijfeld mijn favoriete gebruik van canvas.