Täydellinen yleiskatsaus HTML-kankaasta

Täytyy lukea ennen kuin teet mitään piirtoalustalla, vaikka tiedät sen jo.

Yleiskatsaus

HTML-kangaselementtiä käytetään piirtämään “rasteri” -grafiikkaa verkkosovellukseen. Canvas-sovellusliittymä tarjoaa kaksi piirustuskontekstia: 2D ja 3D, ja tässä oppaassa puhumme 2D: stä (jonka viittaan yksinkertaisesti Canvas-sovellusliittymään).

Ennen kuin aloitan, haluan sinun tietävän yhden erittäin tärkeän asian. Canvas on rasterigrafiikan sovellusliittymä - manipuloit tavaraa pikselitasolla. Tämä tarkoittaa, että taustalla oleva ohjelmisto ei tiedä mallia, jota käytät kontekstisi näyttämiseen - se ei tiedä, piirrätkö suorakulmion vai ympyrän.

Olen jakanut Canvas-sovellusliittymän erillisiksi paloiksi, jotta voit syödä yksi kerrallaan:

  • Polku-sovellusliittymä
  • Piirustustyylit
  • Liukuvärit ja kuviot
  • Suora pikselikäsittely ja kuvat
  • Muutokset
  • Osuma-alueet
  • Tila ja leike () -menetelmä

Perustaa

Käynnistä Canvas-opetusohjelma luomalla HTML-tiedosto ja siihen linkitetty JS-tiedosto.

  Canvas Demo   This will be displayed if your browser doesn't support the canvas element. The closing tag is necessary.    

Teidän canvas-demo.jstiedosto,

// canvas-demo.js const demoCanvas = document.getElementById(’canvas-demo’).getContext(’2d’); window.onload = function() {// make sure to use onload /* Add code here as we go!!! @nodocs */ }

Polut

Polut ovat kokoelma pisteitä kankaan 2D-pikseliruudukossa. Ne piirretään tämän API: n avulla. Kutakin piirtämän polun muotoa kutsutaan W3C-dokumentaatiossa "aliradaksi".

  • beginPath()ja closePath(): Kaikki piirtämäsi muodot lisätään nykyiseen polkuun. Jos soitat stroketai fillmyöhemmin, se soveltaa sitä kaikkiin nykyisen polun piirtämiin muotoihin. Tämän estämiseksi jaat piirustuksesi soittamalla beginPathja closePath.
// Calling this isn't necessary, but a good practice. demoCanvas.beginPath(); /* * Drawing code, copy and paste each example (separately) here */ demoCanvas.closePath();// this is required if you want to draw // in a separate path later
  • moveTo(x,y) : Osoittaa uuden muodon rakentamisen, joka alkaa pisteestä (x, y).
  • lineTo(x,y): Piirtää viivan nykyisen muodon viimeisestä pisteestä kulkemaan pisteeseen. Jos muotoa ei luotu (kautta moveTo), luodaan uusi, joka alkaa kohdasta (x, y) (aivan kuten moveTo).
  • quadraticCurveTo(cpx1,cpy1,x,y)ja bezierCurveTo(cpx1,cpy1,cpx2,cpy2,x,y): piirtää neliöllisen / kuutioisen bezier-käyrän muodon viimeisestä kohdasta alkaen, kulkemalla ohjauspisteiden ( cpx1,cpy1ja cpx2,cpy2) läpi ja päättyen kohtaan x,y. Bezier-käyrä on vain "sileä" käyrä, joka kulkee välipisteiden "kontrollipisteiden" läpi annetuilla loppupisteillä. Huomaa, että käyrän ei tarvitse kulkea tarkasti ohjauspisteiden läpi - se voidaan tasoittaa.
  • arcTo(x1,y1,x2,y2,radius): Tämä on hieman monimutkainen menetelmä. Oletetaan, että polun nykyinen piste on x0,y0- arcTopiirtää sitten kaaren, jossa on kaksi tangenttia, jotka yhdistävät nämä kaksi pisteparia (x1,y1) & (x0,y0)ja (x1,y1) & (x2,y2). Kaaren säde on annettu. Mitä suurempi säde, sitä kauempana kaari on x1,y1, (Katso esimerkki 1.2 visuaalisen selkeyden vuoksi). Jos et ole moveTovielä käyttänyt , niin x0,y0oletuksena on 0,0.
  • arc(x,y,radius,startAngle,endAngle,counterclockwise): Yhdistää polun nykyisen pisteen (oletusarvoisesti 0,0) kaaren alkuun. Se vetää kaaren keskustasta x,yjonka säde radius, mistä startAngleon endAngle. (Huomaa: Toisin kuin kynä- ja paperimatematiikka, kulmat kuvataan myötäpäivään Canvas-sovellusliittymässä); mutta neljässä erityisehdossa - (x0,y0)yhtä suuri (x1,y1), (x1,y1)yhtä suuri (x2,y2), (x0,y0),(x1,y1),(x2,y2)on kolineaarinen tai jos se radiuson nolla, kutsu arcon vastaava lineTo(x1,y1)ja sen sijaan piirretään viiva.
  • rect(x,y,w,h): Piirtää suorakulmion vasemmassa yläkulmassa x,yja leveyden wja korkeuden h.

Esimerkki 1.1:

Nyt meidän on kokeiltava esittelyä - piirretään muutama satunnainen vaakasuora viiva ja sitten luonnos silmästä. Tulos näyttää siltä kuin jotain vasemmalta. Älä unohda käydä läpi koodia ja korjata koodia.

/* Draw horizontal subpaths (shapes) in one path. */ // Draw a pattern of vertically stack horizontal // lines. demoCanvas.moveTo(10, 10);// start at (10,10) demoCanvas.lineTo(110, 10); demoCanvas.moveTo(10, 20);// 10 pts below demoCanvas.lineTo(180, 20); demoCanvas.moveTo(10, 30); demoCanvas.lineTo(150, 30); demoCanvas.moveTo(10, 40); demoCanvas.lineTo(160, 40); demoCanvas.moveTo(10, 50); demoCanvas.lineTo(130, 50); // try removing this moveTo, the quad-curve will then // start from from (130, 50), due to the lineTo. demoCanvas.moveTo(10, 100);// quad-curve starts from here demoCanvas.quadraticCurveTo(110, 55, 210, 100);// curve upward demoCanvas.moveTo(10, 100);// back here, let's draw one below demoCanvas.quadraticCurveTo(110, 145, 210, 100);// curve below // that forms the eye outline demoCanvas.moveTo(132.5, 100);// remove this, a horizontal line will be // drawn from (210, 100) to (132.5, 100) because arc() connects the last // point to the start of the arc. demoCanvas.arc(110, 100, 22.5, 0, 2*Math.PI, false);// pupil (circle) /* We'll talk about this shortly */ demoCanvas.stroke();// draws (by outlining our shapes in the path)

Esimerkki 1.2:

Alla olevassa esimerkissä luon kuutiokäyrän (visuaalisilla ohjeilla), arcTopuhelut keskelle oikealle ja pakkausmiehen kanssa arc()vasemmassa alakulmassa. Ohjauspisteet (kuutiokäyrässä) ovat kulmamuodot kolmen suuntaviivan mukaan.

(x1,y1)sillä arcToon kahden tangentin muodostama kulma.

// comment this block out if you can see the cubic curve demoCanvas.moveTo(100, 100); demoCanvas.lineTo(150, 10); demoCanvas.moveTo(250, 100); demoCanvas.lineTo(200, 190); demoCanvas.moveTo(150, 10); demoCanvas.lineTo(200, 190) demoCanvas.moveTo(100, 100); demoCanvas.bezierCurveTo(150, 10, 200, 190, 250, 100); // arcTo() is too complicated to use // demoCanvas.stroke(); demoCanvas.closePath(); demoCanvas.beginPath(); demoCanvas.moveTo(200, 200);// comment out above line (and comment this line), // then the arc's tangent will come from (0,0)!! Try it. demoCanvas.arcTo(100, 300, 300, 300, 100); demoCanvas.moveTo(200, 200); demoCanvas.arcTo(100, 300, 300, 300, 50); demoCanvas.moveTo(100, 300); demoCanvas.lineTo(300, 300); demoCanvas.moveTo(100, 300); demoCanvas.lineTo(200, 200); demoCanvas.moveTo(50, 300); // packman demoCanvas.arc(50, 300, 35, Math.PI/6, 11*Math.PI/6, false); demoCanvas.lineTo(50, 300); demoCanvas.stroke();

Piirustustyylit

Tähän asti olemme piirtäneet yksinkertaisia ​​ohutlinjaisia ​​polkuja. Piirustustyylit auttavat meitä tekemään piirustuksemme paljon paremmaksi.

Huomaa, että et voi käyttää kahta eri tyyliä samalla polulla. Esimerkiksi, jos haluat piirtää punaisen ja sinisen viivan, sinun on luotava uusi polku sinisen piirtämiseksi. Jos et luo uutta polkua, soittaessasi stroketoisen kerran sen jälkeen, kun olet asettanut näyttötyyliäsi siniseksi, molemmat rivit ovat sinisiä. Siksi tyylejä sovelletaan kaikkiin aliradoihin riippumatta siitä, onko niitä jo ajettu.

demoCanvasTätä tarkoitusta varten määritetään muutama 2D-kontekstiobjektin ominaisuudet :

  • lineWidth: Piirrettävien viivojen paksuus. Oletuksena tämä on 1; siis kahdessa yllä olevassa esimerkissä käytettiin 1 pikselin paksuista ääriviivaa.
  • lineCap: Tämä on alareittien (muodon) päihin käytetty korkki. Se on merkkijono ja sillä voi olla kolme kelvollista arvoa: ”pusku”, “pyöreä”, “neliö” (katso esimerkki 1.3 visuaalisen selkeyden vuoksi). "Butt" päättyy viivoihin, joissa ei ole kantta - jolloin jäykät, kohtisuorat päät ovat ohuiden suorakulmioiden tapaan. "Pyöreä" lisää puoliympyrän päihin, jotta saadaan sileät päät. "Neliö" lisää neliön loppuun, mutta se näyttää "takapuolelta". "Pyöreä" ja "neliö" lisää hieman ylimääräistä pituutta kullekin alireitille.
  • lineJoin: Tämä päättää kuinka kaksi päällekkäistä viivaa liitetään. Jos esimerkiksi haluat luoda oikeanpuoleisen nuolen (>), voit muuttaa kulman muodostumista tällä ominaisuudella. Tällä on kolme kelvollista arvoa: "pyöreä", "viiste" ja "mitra". Tarkista esimerkistä 1.4, kuinka ne muuttavat kulmia. (Oletusarvo on “mitra”). "Pyöreä" muodostaa pyöreät kulmat, kun taas viiste "muodostaa jäykät kolmiulotteiset kulmat ja" mitra "muodostaa terävän reunan.
  • miterLimit: Milloin lineJoin="miter"tämä päättää enimmäisetäisyyden linjan sisä- ja ulkokulmaan nähden. Katso visuaalinen selkeys esimerkistä 1.4 (b). Jos jiiriraja on liian korkea, terävillä nuolilla voi olla suuri yhteinen pinta-ala molemmilla viivoilla. Jos jiiriraja ylitetään, näyttö palaa viistoon.

Esimerkit 1.3 ja 1.4:

Vasemmassa esimerkissä 1.3 näet kuinka pyöreät ja neliömäiset viivat rajatut viivat ovat pidempiä kuin oletusarvoisesti. (HUOMAUTUS: Mitä paksumpi viiva, sitä suurempi pituuden kasvu)

Esimerkissä 1.4 (a) voit nähdä, kuinka pyöreät ja viistot liitokset toimivat. Luodut viivat ovat identtiset ylä- ja alaosassa. Vain lineJoinominaisuudet ovat erilaisia.

Esimerkissä 4.1 (b) voit nähdä, kuinka mitered-liitos toimii ja mitä tapahtuu, jos mitered-pituus ylitetään.

Näytön tyylin lisäominaisuudet on määritelty:

  • font: Tämä merkkijono määrittää tekstin tyylin. Esimerkiksi demoCanvas.font="10px Times New Roman"on kelvollinen fontin arvo.
  • textAlign: Kelvolliset arvot ovat - "alku", "loppu", "vasen", "oikea" ja "keskellä". Oletusarvo on “start”.
  • textBaseline: Kelvolliset arvot ovat - "yläosa", "riippuva", "keskimmäinen", "aakkosellinen", "ideografinen", "alaosa". Oletusarvo on "aakkosellinen".

Todelliset piirustusmenetelmät

Tähän asti olevissa esimerkeissä olet ehkä huomannut, että olen käyttänyt niitä demoCanvas.stroke()ennen kunkin polun sulkemista. Aivohalvausmenetelmä tekee varsinaisen piirustuksen osittain noissa esimerkeissä.

  • stroke: Tämä menetelmä piirtää ääriviivat jokaisen aliradan (muodon) ympärille niiden lineWidthja niihin liittyvien ominaisuuksien mukaan.
  • fill: Tämä menetelmä täyttää polun jäljittämän muodon sisäosan. Jos polkua ei ole suljettu, se sulkee sen automaattisesti yhdistämällä viimeisen pisteen ensimmäiseen pisteeseen.
demoCanvas.moveTo(10,10); demoCanvas.lineTo(50, 50); demoCanvas.lineTo(10, 50); demoCanvas.fill();

Yllä oleva koodi ei sulje kolmiota (10,10), (50,50), (10,50), mutta kutsuminen fill()täyttää sen odotetusti.

  • clearRect(x,y,w,h) : Tyhjentää annetuilla parametreilla muodostetun suorakulmion pikselit.
  • strokeRect(x,y,w,h): Vastaa soittamista rectja sitten stroke. Se ei lisää suorakulmiota nykyiseen polkuun - joten voit muuttaa tyyliä myöhemmin ja soittaa strokevaikuttamatta muodostuneeseen suorakulmioon.
  • fillRect(x,y,w,h): Vastaa soittamista rectja sitten fill. Tämä ei myöskään lisää suorakulmiota nykyiseen polkuun.
  • strokeText(text,x,y,maxWidth)ja fillText(text,x,y,maxWidth): kirjoittaa tekstin kohtaan (x, y) strokeStyle/ fillStyle-ominaisuuden mukaan. maxWidthon valinnainen ja määrittää maksimipituuden pikseleinä, jonka haluat tekstin käyttävän. Jos teksti on pidempi, se skaalataan pienempään kirjasimeen. measureText("text").widthvoidaan käyttää tekstikappaleen näyttöleveyden löytämiseen nykyisen perusteella font.

HUOMAUTUS: fillStyleja ne strokeStyleovat ominaisuuksia, jotka voidaan asettaa mille tahansa CSS-värimerkkijonolle täytön ja viivan värien asettamiseksi.

Liukuvärit ja kuviot

2D-konteksti tarjoaa laatikosta lineaariset ja radiaaliset kaltevuudet. createLinearGradientJa createRadialGradientmenetelmät palauttaa CanvasGradientobjekteja, jotka voidaan sitten modifioida mitä haluamme.

  • createLinearGradient(x0,y0,x1,y1): Konstruktit lineaarista gradienttia, joka kulkee linjalla x0,y0on x1,y1.
  • createRadialGradient(x0,y0,r0,x1,y1,r1): Rakentaa säteittäisen kaltevuuden, joka kulkee kartiossa (ympyröissä) säteen yläosan (sisempi ympyrä) ja säteen r0alapuolen (uloin ympyrä) kanssa r1. Ensimmäisen värin säde on r0.

CanvasGradientOn yksi menetelmä: addColorStop(offset,color). Kaltevuus alkaa 0: sta ja päättyy 1: een. Väri kohdassa offsetasetetaan tällä menetelmällä. Esimerkiksi addColorStop(.5, "green")tekee keskimmäisen värin vihreäksi. Värit kaksi / kaksi vierekkäistä pysähdystä interpoloidaan (sekoitetaan).

Esimerkki 1.6:

Vasemmalla olevassa esimerkissä näet, kuinka lineaariset ja radiaaliset kaltevuudet toimivat.

var linearGrad = demoCanvas.createLinearGradient(5,5,100,5); linearGrad.addColorStop(0, "blue"); linearGrad.addColorStop(.5, "green"); linearGrad.addColorStop(1, "red"); demoCanvas.strokeStyle=linearGrad; demoCanvas.lineWidth=50; demoCanvas.moveTo(5,5); demoCanvas.lineTo(100,5); demoCanvas.stroke();// change strokeStyle(l10) to fillStyle(l10) // and stroke() to fill(). Then, change lineTo(100,5) to rect(5,5,95,50). // Results should be almost same. demoCanvas.closePath(); demoCanvas.beginPath(); var radialGrad = demoCanvas.createRadialGradient(50,50,10,50,50,40); radialGrad.addColorStop(0, "blue"); radialGrad.addColorStop(.5, "green"); radialGrad.addColorStop(1, "red"); demoCanvas.fillStyle=radialGrad; demoCanvas.arc(50,50,30,0,2*Math.PI,false); demoCanvas.fill();

Saatat ihmetellä, mitä jos x0,y0ja x1,y1antaa lineaarisen / radial kaltevuus eivät ole yhtä linjaa / kaaren luomme? Katso esimerkki 1.7

Esimerkki 1.7

var linearGrad = demoCanvas.createLinearGradient(5,5,100,5); linearGrad.addColorStop(0, "blue"); linearGrad.addColorStop(.5, "green"); linearGrad.addColorStop(1, "red"); demoCanvas.strokeStyle=linearGrad; demoCanvas.lineWidth=50; demoCanvas.moveTo(50,5); demoCanvas.lineTo(155,5); demoCanvas.stroke();// change strokeStyle(l10) to fillStyle(l10) // and stroke() to fill(). Then, change lineTo(100,5) to rect(5,5,95,50). // Results should be almost same. demoCanvas.closePath(); demoCanvas.beginPath(); var radialGrad = demoCanvas.createRadialGradient(50,50,10,50,50,40); radialGrad.addColorStop(0, "blue"); radialGrad.addColorStop(.5, "green"); radialGrad.addColorStop(1, "red"); demoCanvas.fillStyle=radialGrad; demoCanvas.arc(60,60,30,0,2*Math.PI,false); demoCanvas.fill();

Suora pikselikäsittely ja kuvat

ImageDataKohde voidaan manipuloida yksittäisiä pikseleitä. Sillä on kolme ominaisuutta:

  • width : Kuvatietojen leveys laitteen näyttöpikseleissä.
  • height : Kuvatietojen korkeus laitteen näyttöpikseleissä.
  • data: Tämä on Uint8ClampedArray(MDN-dokumentti tässä), joka sisältää yksittäiset pikselitiedot sarjoissa (R, G, B, A) tavuja ylimmän pikselin ja oikean alakulman pikselin välillä. Joten n: nnen pikselin punainen arvo olisi data[y*width+x], vihreä olisi data[y*width+x+1], sininen olisi data[y*width+x+2]ja alfa olisi data[y*width+x+3].

HUOMAUTUS: RGBA-arvoa voidaan käyttää edustamaan väriä - jossa R, G, B ovat punaisen, vihreän ja sinisen määrän ja A on opasiteetti (alfa-arvo). Canvasissa näillä elementeillä voi olla mikä tahansa kokonaisluku arvossa [0, 255].

Voit hankkia ImageDataobjektin seuraavilla tavoilla Canvas-sovellusliittymässä:

  • createImageData(sw,sh): Tämä luo ImageDatakohde leveys ja korkeus swsekä shmääritellyn kuvapistettä CSS. Kaikki pikselit alustetaan läpinäkyväksi mustaksi (hex R, G, B = 0 ja myös A = 0).
CSS-pikselit saattavat kartoittaa eri määrän itse objektin altistamia todellisia laitepikseleitä
  • createImageData(data) : Kopioi annetut kuvatiedot ja palauttaa kopiot.
  • getImageData(sx,sy,sw,sh): Palauttaa kopion kankaan pikseleistä suorakulmiossa sx,sy,sw,sh, jonka ImageDataobjekti muodostaa. Kankaan ulkopuolella olevat pikselit on asetettu läpinäkyväksi mustaksi.
  • putImageData(imagedata,dx,dy,dirtyX,dirtyY,dirtyWidth,dirtyHeight): (Viimeiset neljä "likaa" argumenttia ovat valinnaisia). Kopioi pikseliarvot imagedatakankaan suorakulmioon dx,dy. Jos annat neljä viimeistä argumenttia, se kopioi vain likaiset pikselit kuvatiedoissa ( dirtyX,dirtyYmitoissa muodostettu suorakaide dirtyWidth*dirtyHeight). Neljän viimeisen argumentin hylkääminen on sama kuin soittaminen putImageData(imagedata,dx,dy,0,0,imagedata.width,imagedata.height).
Kopioi kaikki x: n ja y: n kokonaislukuarvot, joissa likainenX ≤ x <likainenX + likainenLeveys ja likainenY ≤ y <likainenY + likainenKorkeus, kopioi pikselin neljä kanavaa datarakenteen koordinaatilla (x, y) imagedatapikselille koordinaatilla (dx + x, dy + y) kankaan taustalla olevissa pikselitiedoissa.

Esimerkki 1.8:

Olen täyttänyt koko 400x400 -kankaan satunnaisilla väreillä (täysin läpinäkymätön) getImageData/putImageDatamenetelmillä.

Huomaa, beginPath/closePathettä ImageData-sovellusliittymän käyttäminen ei ole välttämätöntä - koska et käytä Canvas-sovellusliittymää muotojen / käyrien muodostamiseen.

/* replace this line with demoCanvas.createImageData(390,390) instead. */ var rectData = demoCanvas.getImageData(10, 10, 390, 390); for (var y=0; y<390; y++) { for (var x=0; x<390; x++) { const offset = 4*(y*390+x);// 4* because each pixel is 4 bytes rectData.data[offset] = Math.floor(Math.random() * 256);// red rectData.data[offset+1] = Math.floor(Math.random() * 256);// green rectData.data[offset+2] = Math.floor(Math.random() * 256);// blue rectData.data[offset+3] = 255;// alpha, fully opaque } } demoCanvas.putImageData(rectData, 10, 10); /* beginPath/closePath aren't required for this code */

Kuvat voidaan piirtää kankaalle suoraan. SitädrawImagevoidaan käyttää kolmella eri tavalla. Se vaatiiCanvasImageSourcepikselilähteen.

A CanvasImageSourcevoi olla jokin seuraavista - HTMLImageElement, HTMLCanvasElement, HTMLVideoElement. Voit kopioida kankaalle käyttämällä a . Voit myös kopioida olemassa olevan kankaan tai kuvakaappauksen videosta !!!
  • drawImage(image,dx,dy): Kopioi kuvalähteen kankaalle ( dx, dy ). Koko kuva kopioidaan.
  • drawImage(image,dx,dy,dw,dh): Kopioi kuvalähteen kankaan suorakulmioon ( dx, dy ) kooltaan ( dw, dh ). Sitä pienennetään tai suurennetaan tarvittaessa.
  • drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh): Kopioi kuvalähteen sx,sy,sw,shsuorakulmion kankaan suorakulmioon dx,dy,dw,dhja skaalaa tarvittaessa ylös tai alas. Kuitenkin, jos suorakulmiossa sx,sy,sw,shon osia varsinaisen lähteen ulkopuolella - lähdekulmio leikataan sisältäen saapuvat osat ja kohdekulmio leikataan samassa suhteessa Sinun ei kuitenkaan pidä siirtää mitään rajojen ulkopuolella olevaa suorakulmiota - pidä se yksinkertaisena, tyhmänä.

Esimerkki 1.9:

var image = document.getElementById('game-img'); demoCanvas.drawImage(image, 50, 50, 200, 200, 100, 100, 200, 200); /* beginPath/closePath aren't required for this code */

HUOMAUTUS: Lisää tämä HTML-koodiin -

Muutokset

Nyt pääsemme Canvas-sovellusliittymän jännittäviin osiin !!!

The Canvas uses a transformation matrix to transform the input (x, y) coordinates into the displayed (x, y) coordinates. Note that pixels drawn before the transformation are not transformed — they are untouched. Only stuff drawn after applying the transformation will be changed.

There are three in-built transformation methods:

  • scale(xf,yf) : This method scales the input by xf in the horizontal direction and yf in the vertical direction. If you want to magnify an image by a factor of m , then pass xf=yf=m . To stretch/squeeze an image horizontally by m , xf=m,yf=1 . To stretch/squeeze an image vertically by m , xf=1,yf=m .
  • rotate(angle) : Rotates the input by an angle of angle in the clockwise direction, in radians.
  • translate(dx,dy) : Shifts the input by dx,dy .

Example 2.0:

var image = document.getElementById('game-img'); demoCanvas.drawImage(image, 0, 0, 400, 400); demoCanvas.rotate(Math.PI / 6); demoCanvas.scale(2, 2); demoCanvas.translate(10, 10); demoCanvas.drawImage(image, 0, 0, 400, 400);
In Example 2.0, notice how the original image is intact. Only the second image (overlay) is transformed by three methods — rotate, scale, transform.

To revert all transformations:

demoCanvas.setTransform(1, 0, 0, 0, 0, 1); // sets the transform to the identity matrix

NOTE:

  • Changing the order of transformation can affect the final result.
  • For advanced users, you may want to look at the transform and setTransform methods. This will let you set the 3D transformation matrix directly.
  • getImageData and putImageData are not affected by the transform. That means if you draw a black rectangle using putImageData , it won’t be transformed (rotated/scaled/translated).
  • As changing the transform only works for drawings done after applying it, you can’t scale/rotate/translate the existing canvas directly (nor does getImageData and then putImageData work). You may have to create another hidden canvas of the same size — and then copy the image-data into the 2nd canvas, then use drawImage on the 2nd canvas.
  • Check this example: //canvasdemo2d.github.io/ (source: //github.com/canvasdemo2d/canvasdemo2d.github.io). Move your cursor over the canvas and see what it does. It won’t work on mobile phones, unfortunately. The cascading effect is due to the fact that I am translating the canvas w.r.t mouse using drawImage . drawImagethen writes to the same canvas it’s reading from, which causes the repeating pattern!

Hit Regions

As of the time of writing (March 2019), support for hit regions is experimental in Chrome and on Firefox. Mobile browser don’t even support it at all. Hence, I will explain to you “what” could hit regions be used for.

Hit regions are used to catch pointer events on the canvas and know “where” the user clicked. For example, you could have two rectangles A & B — when the user clicks A, you want to perform action $A and when the user clicks B, you want to perform action $B. Let’s walk through the whole process!

A hit region is related to these three things:

  • Path: The current path when the hit region was created (for example, a rectangle). All pointer events inside the path are routed to that hit region.
  • Id: An unique id string to identify the hit region by the event handler.
  • Control: An alternative DOM element ( HTMLButtonElement , for example) that gets the pointer events instead.

NOTE: The path is automatically provided by the canvas when adding a new hit region. Only one — id or control — is needed to form a hit region.

Methods for manipulating the hit-region list of a canvas are:

  • addHitRegion(options) : Takes a HitRegionOptions object and forms a hit-region enclosed by the current path. The options argument should be a string id property or a HTMLElementcontrol property.
  • removeHitRegion(id) : Removes the hit region with the id id so that it no longer receives any pointer events.
  • clearHitRegions() : Removes all hit regions.
demoCanvas.fillStyle = 'red'; demoCanvas.rect(10,10,60,60); demoCanvas.fill();// first rectangle demoCanvas.addHitRegion({ id: 'btn1' }); demoCanvas.fillStyle = 'blue'; demoCanvas.rect(10,110,60,60); demoCanvas.fill(); demoCanvas.addHitRegion({ id: 'btn2' }); document.getElementById('demo-canvas').onpointerdown = function(evt) { // demoCanvas is the 2d context, not the HTMLCanvasElement console.log('Hello id: ' + evt.region);// region is hitregion id } // This code might not work due to this being an // unsupported (new) feature of HTML5.

NOTE: Hit regions aren’t supported — but that doesn’t mean you have to use them to capture pointer events. You could create your “own hit-region list” and representations of boundaries of regions (cause you can’t get the current path from the canvas, too bad). In the document.getElementById('demo-canvas').onpointerdown method, get the current clientX,clientY properties and search through the hit region list. Based on the hit region that contains the point, you can perform the intended action.

States and the clip() method

State saving is a convenience provided by the W3C specification. You can save the current state of a canvas and restore it later.

You could also build such a system (partially) by writing your own JavaScript model. But you would have to save a quite of stuff: transformation matrix, hit-region list, style properties, and so on. Furthermore, you cannot revert the clipping area (we’ll get to the clipmethod in some time) directly.

NOTE: The save / restore methods do not save & restore the actual drawing/pixels. They only save other properties.

Hence, I would recommend heavily using the save & restore methods to go back and forth instead of erasing stuff on your own or making your own state-saving mechanism.

The CanvasRendering2DContext object has an associated state stack. The save method will push the current canvas state onto that stack, while the restore method will pop the latest state from the stack.

The Clipping Region

The clipping region is a specific region in which all drawings are to be done. Obviously, by default, the clipping region is the rectangle is the whole canvas. But you may want to draw in a specific region instead of the whole thing. For example, you may want to draw the lower half of a star formed by multiple lineTo methods.

So, for example, let’s say you know how to draw a star in the canvas. It touches all sides of the canvas. But now you want to only display the lower half of the star. In this scenario, you would:

  1. Save the state of the canvas
  2. Clip the lower half region
  3. Draw your star (as if on the whole canvas)
  4. Restore the canvas state

To clip a region, you have to call the clip() method which does the following:

The clip() method must create a new clipping region by calculating the intersection of the current clipping region and the area described by the path, using the non-zero winding number rule. Open subpaths must be implicitly closed when computing the clipping region, without affecting the actual subpaths. The new clipping region replaces the current clipping region.

When the context is initialized, the clipping region must be set to the rectangle with the top left corner at (0,0) and the width and height of the coordinate space.

— W3C Documentation for Canvas 2D Context

demoCanvas.save(); demoCanvas.rect(0, 200, 400, 200);// lower-half rectangle subpath demoCanvas.clip(); /* star drawing method */ demoCanvas.restore();

That’s all for now. I will write an article on animations with the canvas and how to write a custom interface completely on the canvas.

Further reading:

  • How to use Firebase for building Android multiplayer games
  • Kuinka synkronoida pelisovelluksesi useille Android-laitteille
  • Pyöreät riippuvuudet JavaScriptissä

Shukant Pal on Silcos-ytimen luoja. Hän on innokas oppija ja harjoittelee nyt kehittynyttä verkkosovelluskehitystä. Hänellä on käytännön kokemusta Reactista ja sen ekosysteemistä.

Kaikki lainaukset on otettu Canvas 2D -kontekstin W3C-asiakirjoista.

Hei, olen Shukant Pal. Kehitän paljon web-sovelluksia vapaa-ajallaani. Seuraa minua sosiaalisessa mediassa.