Hoppa till innehåll

HTML5+JS

Effekter skapas med vanlig JavaScript på ett HTML5 canvas-element. I det här avsnittet går vi igenom grunderna: rita former, rita med iteration och lägga till rörelse.

För den här handledningen använder jag en generisk lightscript-mall:

<head>
<meta description="Template"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Hämta canvas-elementet från DOM
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
var width = 320;
var height = 200;
var hue = 0;
function update() {
// Kod placeras här
window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);
</script>

Oavsett om du använder en linje eller många förblir processen densamma.

  1. ctx.beginPath() startar din form
  2. ctx.moveTo(x, y) anger den första punkten
  3. ctx.lineTo(x, y) anger nästa punkt och en linje tillbaka till den första. Använd flera lineTo för att rita större former.
  4. Ange ctx.strokeStyle och/eller ctx.fillStyle (om formen är sluten)
  5. Använd ctx.stroke() för att rita linjerna och ctx.fill() för att fylla slutna former. Det är viktigt att notera att fram till det här steget är allt hypotetiskt – ingenting ritas förrän dessa kommandon körs.

Här är tre exempel med linjer:

function update() {
// En linje
ctx.beginPath();
ctx.moveTo(30, 30);
ctx.lineTo(30, 170);
ctx.strokeStyle = "blue";
ctx.stroke();
// L
ctx.beginPath();
ctx.moveTo(80, 30);
ctx.lineTo(80, 100);
ctx.lineTo(120, 100);
ctx.strokeStyle = "blue";
ctx.stroke();
// Fylld triangel
ctx.beginPath();
ctx.moveTo(130, 30);
ctx.lineTo(130, 100);
ctx.lineTo(170, 100);
ctx.lineTo(130, 30);
ctx.fillStyle = "red";
ctx.strokeStyle = "blue";
ctx.fill();
ctx.stroke();
window.requestAnimationFrame(update);
}

Det är viktigt att notera ritordningen här: den fyllda triangeln ritas sist, så den skulle täcka de två andra formerna om de befann sig på samma position. Dessutom ritas stroken i triangeln efter fyllningen. Om vi vände på dessa steg skulle den inre halvan av stroken täckas av den röda fyllningen.

Rektanglar är enkla och användbara, särskilt för den genomsnittliga upplösningen på ett RGB-tangentbord. Processen liknar att rita linjer – ersätt bara moveTo- och lineTo-kommandona med rect(x, y, width, height).

function update() {
// Grundläggande kvadrat
ctx.beginPath();
ctx.rect(30, 30, 75, 75);
ctx.strokeStyle = "blue";
ctx.stroke();
// Fylld rektangel
ctx.beginPath();
ctx.rect(125, 30, 125, 100);
ctx.fillStyle = "red";
ctx.strokeStyle = "blue";
ctx.fill();
ctx.stroke();
window.requestAnimationFrame(update);
}

Bågar ritas genom att rotera en yttre punkt runt en central punkt och kan användas för att skapa hela cirklar. Bågkommandot är ctx.arc(x, y, radius, startvinkel, slutvinkel), där båda vinkelmåtten anges i radianer. Till skillnad från rektanglar ritas en båge med formens origo (x, y) i mitten av bågen.

function update() {
// Halvcirkel
ctx.beginPath();
ctx.arc(70, 100, 30, 0, Math.PI);
ctx.strokeStyle = "blue";
ctx.stroke();
// 3/4-cirkel
ctx.beginPath();
ctx.arc(140, 100, 30, 0, Math.PI * (3/2));
ctx.strokeStyle = "blue";
ctx.stroke();
// Hel cirkel
ctx.beginPath();
ctx.arc(210, 100, 30, 0, Math.PI * 2);
ctx.strokeStyle = "blue";
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
window.requestAnimationFrame(update);
}

Som du kan se ritas bågar medurs som standard. Detta kan vändas med en slutvinkel som är mindre än startvinkeln, eller genom att lägga till “true” som ett sista argument till metoden ctx.arc().

Att rita tre former för hand kan lätt ta tjugo rader kod, så om du behöver rita hundra former behöver vi ett mer effektivt tillvägagångssätt. Loopar i JavaScript är ett enkelt sätt att upprepa uppgifter och kan tillämpas på ritning också.

Här är ett exempel med en for-loop för att rita en rad schackrutor:

function update() {
for(let i = 0; i < 8; i++){
// 1. Skapa rektangelstig och rita stroken för var och en.
ctx.beginPath();
ctx.rect(i * 20, 20, 20, 20);
ctx.strokeStyle = "black";
ctx.stroke();
// 2. Om 'i' är jämnt är fillStyle svart. Annars är den vit.
if(i % 2 == 0){
ctx.fillStyle = "black";
} else {
ctx.fillStyle = "white";
}
// 3. Fyll formen efter att ha angett fillStyle.
ctx.fill();
}
window.requestAnimationFrame(update);
}

Här är ett while-loop-exempel för att rita ett schackmönster. Jag ändrar ljusstyrkan på de svarta rutorna och hue hos de vita rutorna något i varje iteration med hjälp av mallbokstavar.

function update() {
var i = 0;
while(i < 16){
// 1. X- och Y-beräkningar med 'row' och 'column'
// Den här operationen avrundar resultatet nedåt till närmaste heltal. För i = (0-3) är raden 0, för i = (4-7) är raden 1, och så vidare
var iRow = Math.floor(i / 4);
// Den här operationen hittar resten efter division, vilket begränsar kolumnen till intervallet (0-3).
var iCol = i % 4;
// Multiplicera med kvadratbredden för att hitta den här kvadratens x-origo
var ix = iCol * 20;
// Multiplicera med kvadrathöjden för att hitta den här kvadratens y-origo
var iy = iRow * 20;
// 2. Stig och stroke
ctx.beginPath();
ctx.rect(ix, iy, 20, 20);
ctx.strokeStyle = "black";
ctx.stroke();
// 3. Om raden är jämn är varannan ruta fylld svart. Om udda, byt de svarta rutorna. Varje iteration lägger till en liten mängd av ljuskomponenten till det svarta, och hue till det vita.
if(iRow % 2 == 0){
if(i % 2 == 0){
// 'Svart'
ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)`
} else {
// 'Vit'
ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`;
}
} else {
if(i % 2 == 0){
// 'Vit'
ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`;
} else {
// 'Svart'
ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)`
}
}
ctx.fill();
i++;
}
window.requestAnimationFrame(update);
}

Animationer på canvas begränsas bara av din fantasi och matematiska kunskap. Om du känner dig osäker med trigonometri eller geometri rekommenderar jag att du gör lite lättviktig forskning för att ta dina färdigheter till nästa nivå. I de kommande exemplen tar jag en stationär cirkel och kopplar varje möjlig variabel till en enkel oscillerande rörelse.

Guldstandarden för oscillation (regelbunden fram-och-tillbaka-rörelse) är att ta sin() eller cosinus() av en timer. Båda operationerna returnerar ett värde mellan -1 och 1 för VILKET värde som helst som skickas in, även om det bara räknar uppåt. Den enda skillnaden är att de är något ur synk – när cos är 0 är sin -1 eller 1, och vice versa. Vi kan använda detta till vår fördel i den här animationen –

function update() {
// Hitta aktuell tid i millisekunder, dela sedan för att sakta ned animationshastigheten
let time = Date.now() / 100;
// Rita en bakgrund så att de gamla formerna inte finns kvar
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
ctx.beginPath();
// ANVÄND ETT EXEMPEL ÅTGÅNGEN
// 1:a exemplet - sida till sida, y är konstant
ctx.arc(15 * Math.cos(time) + 160, 100, 30, 0, Math.PI * 2);
// 2:a exemplet - upp och ned, x är konstant
ctx.arc(160, 15 * Math.sin(time) + 100, 30, 0, Math.PI * 2);
// 3:e exemplet - cirkel, x och y är variabla
ctx.arc(15 * Math.cos(time) + 160, 15 * Math.sin(time) + 100, 30, 0, Math.PI * 2);
ctx.fillStyle = "black";
ctx.fill();
window.requestAnimationFrame(update);
}

Vi har bara några fler attribut att förändra över tid – cirkelradie, fyllning och stroke. Jag lämnar bågens vinklar ifred för att hålla formen enkel eftersom resten kommer att bli ganska bländande.

function update() {
let time = Date.now() / 100;
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
ctx.beginPath();
ctx.arc(15 * Math.cos(time) + 160, 15 * Math.sin(time) + 100, 30 + 15 * Math.sin(time), 0, Math.PI * 2);
ctx.fillStyle = `hsl(${180 + 180 * Math.cos(time)}, 100%, 50%)`;
ctx.fill();
ctx.strokeStyle = `hsl(${180 + 180 * Math.sin(time)}, 100%, 50%)`;
ctx.stroke();
window.requestAnimationFrame(update);
}

Varje attribut på varje form vi har skapat hittills kan förändras över tid, och varje animation jag skapar är en kombination av ovanstående färdigheter. För mer detaljer om att skapa återanvändbara, effektiva animationer, kolla in vår sida Callbacks!