Lerret fra grunnen Transformasjoner og gradienter

I denne artikkelen skal jeg gå gjennom transformasjoner i lerretet, i tillegg til skygger og gradienter. Transformasjoner er et ekstremt verdifullt sett med metoder som lar deg begynne å være kreativ med måten du tegner objekter på lerretet på. La oss komme i gang etter hoppet!


Setter opp

Du skal bruke samme HTML-mal fra de forrige artiklene, så åpne din favorittredigerer og lim inn følgende kode:

   Lerret fra grunnen av          

Her har vi 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.


Oversettelser i aksjon

Oversett i hovedsak skifter hele koordinatsystemet.

En av de enkleste transformasjonene i lerret er oversette. Dette lar deg flytte opprinnelsespunktet til 2d-renderingskonteksten; posisjonen (0, 0) på lerretet. La meg vise deg hva dette betyr.

Først legger du et torg i lerret på stillingen (0, 0):

ctx.fillRect (0, 0, 100, 100);

Den vil tegne seg selv på lerretets øverste venstre kant. Fortsatt - ingenting utenom det vanlige her.

Prøv nå å oversette 2d-renderingskonteksten og tegne en annen firkant i samme posisjon:

ctx.save (); ctx.translate (100, 100); ctx.fillStyle = "rgb (0, 0, 255)"; ctx.fillRect (0, 0, 100, 100); ctx.restore ();

Hva tror du vil skje? Har en gullstjerne hvis du gjettet at det nye torget blir trukket på stillingen (100, 100). Ingen spilltid for de som gjettet feil. Unnskyld!

Så hva skjedde her da da? Når det gjelder koden for tegning av det andre torget, tok du det på samme sted som det første torget. Årsaken til dette er at du i utgangspunktet skiftet hele koordinatsystemet til lerretet slik at dets (0, 0) posisjon er nå på stedet (100, 100).

Gjør det litt mer fornuftig nå? Jeg håper det. Det kan ta litt tid å få hodet rundt, men det er et enkelt konsept når du forstår det.

Du vil sannsynligvis ikke bruke denne transformasjonen for mye på egenhånd, da du bare kan tegne den andre firkanten på (100, 100) for å få samme effekt. Skjønnheten i oversette, men det er at du kan kombinere det med andre transformasjoner for å gjøre noen ganske kule ting.

La oss ta en titt på neste transformasjon på listen.


Skalere dine visualer

Som du sikkert har gjettet, så skala transformasjon brukes til resizing. Mer spesifikt brukes skalaomformingen til å skalere 2d-renderingskonteksten.

Fjern koden du jobbet med med oversette eksempel, og legg til følgende kode:

ctx.fillRect (100, 100, 100, 100);

Dette vil tegne en standardfirkant på stillingen (100, 100), med bredde og høyde på 100 piksler. Så hvordan skaler vi dette?

Egenskaper i skala er multiplikatorer for x- og y-dimensjonene.

De skala transformasjon brukes på en lignende måte som oversette, ved at det kalles før du tegner objektene du vil at den skal brukes på. Det er viktig å påpeke at egenskapene i skala er multiplikatorer for x og y dimensjoner. Dette betyr at a skala av (1, 1) vil formere størrelsen på 2d-renderingskonteksten av en, slik at den blir den samme størrelsen som den var før. EN skala av (5, 5) vil formere størrelsen på 2d-renderingskonteksten med fem, noe som gjør den fem ganger større enn den var tidligere. Enkel.

I ditt tilfelle vil du doble størrelsen på torget, så du bruker en skala av (2, 2):

ctx.save (); ctx.scale (2, 2); ctx.fillRect (100, 100, 100, 100); ctx.restore ();

Som resulterer i en firkant som er to ganger så stor som:

Vær imidlertid oppmerksom på hvordan torget nå trekkes i en annen posisjon enn den ble tegnet før du søkte skala. Årsaken til dette er det skala multipliserer størrelsen på alt i 2d-renderingskonteksten, inkludert koordinater. I ditt tilfelle blir stillingen (100, 100) nå (200, 200); koordinatene er dobbelt så store som de ville være uten å skaleres.

For å komme seg rundt dette kan vi utføre en oversette som flytter opprinnelsen til 2d-renderingskonteksten til posisjonen du vil tegne plassen. Hvis du da søker skala og tegne plassen på posisjon (0, 0), vil posisjonen ikke skiftes:

ctx.save (); ctx.translate (100, 100); ctx.scale (2, 2); ctx.fillRect (0, 0, 100, 100); ctx.restore ();

Som resulterer i et firkant som er dobbelt så stort som originalen, men det trekkes i samme posisjon som originalen:

Det er å være klar over disse små kjennskapene i transformasjoner som virkelig hjelper når de bruker dem. De fleste av de vanlige problemene med transformasjoner synes å være et resultat av at de ikke forstår hvordan de fungerer.


Roterende elementer

Hittil har alle transformasjonene du har behandlet vært ganske unexciting. Heldigvis rotere transformasjon er her for å redde dagen, og det er lett min favoritt av gjengen.

jeg er sikker rotere trenger ingen introduksjon, så la oss hoppe rett inn og rotere en firkant 45 grader (husk at grader må være i radianer):

ctx.save (); ctx.rotate (Math.PI / 4); // Roter 45 grader (i radianer) ctx.fillRect (100, 100, 100, 100); ctx.restore ();

Hvilket posisjonerer en firkant på (100, 100) og roterer? Wow, hang på! Dette ser ikke riktig ut:

Se hva som skjedde? Torget ser ut til å forsøke å unnslippe nettleservinduet, i stedet for å rotere på stedet på stillingen (100, 100). Dette er fordi rotere, som alle transformasjonene, påvirker hele 2d-renderingskonteksten, og ikke objekter individuelt.

Her er en illustrasjon av hva som skjer med koordinatsystemet når du utfører en 45 grader rotere:

Legg merke til hvordan hele koordinatsystemet har rotert 45 grader fra opprinnelsesstedet (0, 0)? Dette har forårsaket at firkanten ser ut som om det var å rømme nettleservinduet, bare fordi stillingen (100, 100) hadde blitt rotert klaff på kanten av nettleseren.

Den enkle måten å komme seg rundt på dette problemet er å kombinere rotere med oversette, som så:

ctx.save (); ctx.translate (150, 150); // Oversett til midten av kvadrat ctx.rotate (Math.PI / 4); // Roter 45 grader ctx.fillRect (-50, -50, 100, 100); // Senter ved rotasjonspunktet ctx.restore ();

Utfører oversette Flytter opprinnelsespunktet til 2d-renderingskonteksten (0, 0) til det som skal være midtpunktet på torget (150, 150). Dette betyr at en rotasjon vil nå være basert rundt posisjonen (150, 150). Hvis du deretter tegner en firkant med en negativ x og y posisjon, lik halvparten av kvadratets bredde og høyde, vil du ende opp med å tegne en firkant som ser ut som om den har blitt rotert rundt sitt sentrale punkt:

De rotere transformasjon er sannsynligvis det vanskeligste av dem alle å forstå fullt ut. Det er viktig å huske at transformasjoner utføres på hele 2d-renderingskonteksten, og hvis du vil rotere en form rundt sitt sentrale punkt, må du kombinere rotere med oversette.

La oss flytte inn på noe litt mer visuelt imponerende.


Legge til skygger

Å legge skygger til objekter er herlig enkelt.

Lerret leveres med noen få egenskaper for å manipulere utseendet på objektene som er trukket på det, og ett sett med disse egenskapene lar deg legge til skygger.

Å legge skygger til objekter er herlig enkelt. Det krever bare shadowColor egenskap som skal settes i 2d-renderingskonteksten til en farge som ikke er gjennomsiktig svart, og en av de shadowBlur, shadowOffsetX, eller shadowOffsetY egenskaper som skal settes til en annen verdi enn 0.

Prøv følgende kode:

ctx.save (); ctx.shadowBlur = 15; ctx.shadowColor = "rgb (0, 0, 0)"; ctx.fillRect (100, 100, 100, 100); ctx.restore ();

Dette vil gi skyggen en usynlig piksel, og vil sette fargen til solid svart:

Ganske standard ting så langt.

Hvis du setter inn shadowBlur til 0, endre shadowColor til en lysegrå, og gi en positiv shadowOffsetX og shadowOffsetY:

ctx.save (); ctx.shadowBlur = 0; ctx.shadowOffsetX = 6; ctx.shadowOffsetY = 6; ctx.shadowColor = "rgba (125, 125, 125, 0,5)"; // Transparent grå ctx.fillRect (300, 100, 100, 100); ctx.restore ();

Du vil ende opp med en solid skygge som vises litt til høyre og under objektet som er tegnet:

Så kult som skygger er, de kan være litt av en ressurs hog.

Det er viktig å huske at skygger påvirker alt som er trukket etter at de er definert, så det er nyttig å bruke lagre og restaurere metoder for å redde deg fra å måtte tilbakestille skyggegenskapene når du har brukt dem.

Husk at ytelsen kan lide når du søker en skygge på mange ting samtidig. I noen tilfeller kan det være verdt å bruke et PNG-bilde med en skygge i stedet for å tegne et objekt manuelt og bruke en dynamisk skygge ved hjelp av kode. Vi vil dekke hvordan du bruker bilder med lerret i neste avdeling av denne serien.


Opprette gradienter

Du kan lage to typer gradienter i lerret - lineært og radialt.

De siste funksjonene som jeg vil dekke med deg i denne opplæringen er gradienter. Det er to typer gradienter i lerret, den første er lineære (rette) gradienter. Du kan opprette en lineær gradient ved hjelp av createLinearGradient metode (overraskende nok), som ser slik ut i pseudokode:

ctx.createLinearGradient (startX, startY, endX, endY);

Det første settet med to argumenter er x og y posisjonen til begynnelsen av gradienten, og det andre settet av argumenter er x og y posisjon på slutten av gradienten. Det er også viktig å påpeke at en gradient i lerret egentlig er en type fargevare, så du bruker dem til fyllstil og strokeStyle eiendommer.

Her er et eksempel på hvordan du lager en lineær gradient som går fra toppen av lerretet helt til bunnen:

var gradient = ctx.createLinearGradient (0, 0, 0, canvas.height); gradient.addColorStop (0, "rgb (255, 255, 255)"); gradient.addColorStop (1, "rgb (0, 0, 0)"); ctx.save (); ctx.fillStyle = gradient; ctx.fillRect (0, 0, canvas.width, canvas.height); ctx.restore ();

Legg merke til hvordan du tilordner gradienten til en variabel, og bruk den variabelen til å ringe addColorStop metode. Denne metoden lar deg sette fargen på bestemte punkter langs gradienten. For eksempel representerer posisjonen 0 begynnelsen av gradienten (den første x og y posisjon), og 1 ville representere slutten av gradienten (den andre x og y stilling). Du kan også bruke desimaltall mellom 0 og 1 for å tilordne en farge på et annet punkt langs gradienten, som 0,5 ville være halvveis langs.

Ved å bruke gradientvariabelen til fyllstil Eiendom, du ender med en fin gradient som går fra hvitt (på posisjon 0 øverst på lerretet), til svart (på stilling 1 nederst på lerretet):

Men du trenger ikke alltid å bruke lineære gradienter; Du kan også opprette radiale gradienter!

Radialgradienter er opprettet med createRadialGradient metode som ser slik ut i pseudokode:

ctx.createRadialGradient (startX, startY, startRadius, endX, endY, endRadius);

Det første settet med tre argumenter er x og y posisjon så vel som radiusen til sirkelen ved begynnelsen av graden, med de tre siste argumentene som representerer x og y posisjon så vel som radiusen til sirkelen på slutten av graden.

Lyd forvirrende, ikke sant? Det er litt, så la oss hoppe inn og lage en radial gradient for å se hva som skjer:

var gradient = ctx.createRadialGradient (350, 350, 0, 50, 50, 100); gradient.addColorStop (0, "rgb (0, 0, 0)"); gradient.addColorStop (1, "rgb (125, 125, 125)"); ctx.save (); ctx.fillStyle = gradient; ctx.fillRect (0, 0, canvas.width, canvas.height); ctx.restore ();

Du har opprettet en radial gradient som har et utgangspunkt ved 350, 350 med en radius på 0 og et sluttpunkt på (50, 50) med en radius på 100. Kan du gjette hvordan dette vil se ut? 20 poeng hvis du gjettet det ville se slik ut:

Hvis du er noe som meg, det er ikke det jeg forventet å se. Jeg har brukt radiale gradienter før i applikasjoner som Adobe Photoshop, og de ser ingenting ut som det! Så hvorfor ser det ut som dette da? Vel, det er det som er ment å se ut, rar.

Sjekk ut dette diagrammet som viser nøyaktig hvordan en radial gradient fungerer i lerret:

Interessant, er det ikke? Det gir deg i utgangspunktet en konisk form, men hva om du vil lage en skikkelig radial gradient som den i Photoshop? Heldigvis er det enkelt.

Å skape en skikkelig radial gradient krever bare at du plasserer de to sirkler av gradienten på nøyaktig det samme x og y posisjon, sørg for at en av gradientkretsene er større enn den andre:

var canvasCentreX = canvas.width / 2; var canvasCentreY = canvas.height / 2; var gradient = ctx.createRadialGradient (canvasCentreX, canvasCentreY, 250, canvasCentreX, canvasCentreY, 0); gradient.addColorStop (0, "rgb (0, 0, 0)"); gradient.addColorStop (1, "rgb (125, 125, 125)"); ctx.save (); ctx.fillStyle = gradient; ctx.fillRect (0, 0, canvas.width, canvas.height); ctx.restore ();

Koden ovenfor skaper en radial gradient som sitter i midten av lerretet. En av kretsene i gradienten har en radius på 0, mens den andre har en radius på 250. Resultatet er en tradisjonell radiell gradient som beveger seg fra sentrum av lerretet utover, slik som:

Det ser bedre ut! Jeg ble ærlig forbauset da jeg så hvordan radialgradienter ble implementert i lerret. Jeg slår på at det er trukket mange mennesker opp når de ser den kjegleformen. Åh, i det minste vet du hvordan du skal skape de riktige de nå.

Det er verdt å påpeke at gradienter i lerret er også ganske intensiv operasjoner. Hvis du vil dekke hele lerretet i en gradient, vil jeg først vurdere å bruke en CSS3-gradientbakgrunn til selve lerretelementet.


Wrapping Up

I denne artikkelen har vi vurdert hvordan å utføre grunnleggende transformasjoner på lerretet, inkludert oversettelser, skalering og rotasjon. Du lærte også hvordan du legger til skygger i objekter, og hvordan du oppretter gradienter. Det høres ikke ut som mye, men transformasjoner, spesielt, danner ryggraden til noen av de kuleste tingene som kan oppnås i lerret.

I neste oppføring i "Lerret fra grunnen", kommer vi til å bryte vekk fra tegneobjekter og se på hvordan du kan manipulere bilder og video i lerretet. Det er her ting begynner å bli veldig interessant! Følg med!