Skip to content

HTML5+JS

Efekti se kreiraju koristeći vanilla JavaScript na HTML5 canvas elementu. U ovom odeljku ćemo pokriti osnove: crtanje oblika, crtanje sa iteracijom i dodavanje kretanja.

Za ovaj vodič koristim generički Lightscript predložak:

<head>
<meta description="Template"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Get the canvas element from the DOM
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
var width = 320;
var height = 200;
var hue = 0;
function update() {
// Code goes here
window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);
</script>

Bilo jedna ili mnogo linija, proces ostaje isti.

  1. ctx.beginPath() pokreće vaš oblik
  2. ctx.moveTo(x, y) postavlja prvu tačku
  3. ctx.lineTo(x, y) postavlja sledeću tačku i liniju nazad ka prvoj. Koristite više lineTo poziva za crtanje većih oblika.
  4. Postavite ctx.strokeStyle i/ili ctx.fillStyle (ako je oblik zatvoren)
  5. Koristite ctx.stroke() za crtanje linija i ctx.fill() za popunjavanje zatvorenih oblika. Važno je napomenuti da je sve do ovog trenutka hipotetičko — ništa se ne crta dok se ove komande ne izvrše.

Evo tri primera sa linijama:

function update() {
// One Line
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();
// Filled Triangle
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);
}

Važno je obratiti pažnju na redosled crtanja ovde: popunjeni trougao se crta poslednji i pokrio bi ostala dva oblika da su bili na istoj poziciji. Pored toga, linija u trouglu se crta posle punjenja. Da smo zamenili ove korake, unutrašnja polovina linije bila bi pokrivena crvenim punjenjem.

Pravougaonici su jednostavni i korisni, posebno za prosečnu rezoluciju RGB tastature. Proces je sličan crtanju linija — samo zamenite komande moveTo i lineTo sa rect(x, y, width, height).

function update() {
// Basic Square
ctx.beginPath();
ctx.rect(30, 30, 75, 75);
ctx.strokeStyle = "blue";
ctx.stroke();
// Filled Rectangle
ctx.beginPath();
ctx.rect(125, 30, 125, 100);
ctx.fillStyle = "red";
ctx.strokeStyle = "blue";
ctx.fill();
ctx.stroke();
window.requestAnimationFrame(update);
}

Lukovi se crtaju rotiranjem spoljašnje tačke oko centralne tačke i mogu se koristiti za kreiranje potpunih krugova. Komanda za luk je ctx.arc(x, y, radius, početniUgao, krajnjiUgao), gde su oba ugla izražena u radijanima. Za razliku od pravougaonika, luk se crta sa ishodištem (x, y) u centru luka.

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

Kao što možete videti, lukovi se podrazumevano crtaju u smeru kazaljke na satu. Ovo se može preokrenuti krajnjim uglom manjim od početnog, ili dodavanjem “true” kao poslednjeg argumenta metodi ctx.arc().

Ručno crtanje tri oblika može lako koštati dvadeset linija koda. Dakle, ako treba da nacrtate sto oblika, potreban nam je efikasniji pristup. Petlje u JavaScript-u su jednostavan način za ponavljanje zadataka i mogu se primeniti i na crtanje.

Evo primera sa for petljom za crtanje reda šahovskih polja:

function update() {
for(let i = 0; i < 8; i++){
// 1. Create rectangle path and draw the stroke for each.
ctx.beginPath();
ctx.rect(i * 20, 20, 20, 20);
ctx.strokeStyle = "black";
ctx.stroke();
// 2. If 'i' is even, the fillStyle is black. Otherwise it's white.
if(i % 2 == 0){
ctx.fillStyle = "black";
} else {
ctx.fillStyle = "white";
}
// 3. Fill the shape after setting the fillStyle.
ctx.fill();
}
window.requestAnimationFrame(update);
}

Evo primera sa while petljom za crtanje šahovskog uzorka. Neznatno ću menjati osvetljenost crnih polja i nijansu belih polja u svakoj iteraciji koristeći template literals.

function update() {
var i = 0;
while(i < 16){
// 1. X and Y calculations with 'row' and 'column'
// This operation rounds the result down to the nearest integer. For i = (0-3) the row will be 0, for i = (4-7) the row will be 1, and so on
var iRow = Math.floor(i / 4);
// This operation finds the remainder after division, limiting column to the (0-3) range.
var iCol = i % 4;
// Multiply by the square width to find this square's x-origin
var ix = iCol * 20;
// Multiply by the square height to find this square's y-origin
var iy = iRow * 20;
// 2. Path and stroke
ctx.beginPath();
ctx.rect(ix, iy, 20, 20);
ctx.strokeStyle = "black";
ctx.stroke();
// 3. If the row is even, every other square is filled black. If odd, switch the black squares. Each iteration adds a small amount of the lightness component to the black, and hue to the white.
if(iRow % 2 == 0){
if(i % 2 == 0){
// 'Black'
ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)`
} else {
// 'White'
ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`;
}
} else {
if(i % 2 == 0){
// 'White'
ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`;
} else {
// 'Black'
ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)`
}
}
ctx.fill();
i++;
}
window.requestAnimationFrame(update);
}

Animacije na canvas-u su ograničene samo vašom maštom i matematičkim znanjem. Ako se ne osećate sigurno u trigonometriji ili geometriji, preporučujem istraživanje kako biste podigli svoje veštine na sledeći nivo. U sledećim primerima uzimam stacionarni krug i dodeljujem jednostavno oscilujuće kretanje svakoj mogućoj promenljivoj.

Zlatni standard za oscilacije (redovno kretanje napred-nazad) je uzimanje sin() ili cos() tajmera. Svaka operacija vraća vrednost između -1 i 1 za SVAKU prosleđenu vrednost, čak i ako samo broji naviše. Jedina razlika između ove dve funkcije je da su malo pomerene — kada je cos 0, sin je -1 ili 1 i obrnuto. Ovo možemo iskoristiti u svojoj korist u ovoj animaciji:

function update() {
// Find current time in milliseconds, then divide to slow down the animation speed
let time = Date.now() / 100;
// Draw a background so the old shapes don't stick around
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
ctx.beginPath();
//USE ONE EXAMPLE AT A TIME
// 1st example - side to side, y is constant
ctx.arc(15 * Math.cos(time) + 160, 100, 30, 0, Math.PI * 2);
// 2nd example - up and down, x is constant
ctx.arc(160, 15 * Math.sin(time) + 100, 30, 0, Math.PI * 2);
// 3rd example - circle, x and y are variable
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);
}

Imamo još nekoliko atributa koje možemo menjati tokom vremena — poluprecnik kruga, punjenje i linija. Ostaviću uglove luka nepromenjenim za sada kako bih zadržao jednostavan oblik, jer će ostatak biti prilično upečatljiv.

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);
}

Svaki atribut svakog oblika koji smo do sada kreirali može se menjati tokom vremena, a svaka animacija koju napravim je kombinacija gore navedenih veština. Za više detalja o kreiranju višekratno upotrebljivih, efikasnih animacija, pogledajte našu stranicu Callbacks!