Przejdź do głównej zawartości

HTML5+JS

Efekty są tworzone za pomocą czystego JavaScript na elemencie HTML5 canvas. W tej sekcji omawiamy podstawy: rysowanie kształtów, rysowanie z iteracją i dodawanie ruchu.

W tym samouczku używam ogólnego szablonu lightscript:

<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>

Niezależnie od tego, czy używasz jednej linii, czy wielu, proces pozostaje taki sam.

  1. ctx.beginPath() rozpoczyna twój kształt
  2. ctx.moveTo(x, y) ustawia pierwszy punkt
  3. ctx.lineTo(x, y) ustawia następny punkt i rysuje linię z powrotem do pierwszego. Użyj wielu lineTo, aby rysować większe kształty.
  4. Ustaw ctx.strokeStyle i/lub ctx.fillStyle (jeśli kształt jest zamknięty)
  5. Użyj ctx.stroke() do rysowania linii i ctx.fill() do wypełniania zamkniętych kształtów. Ważne jest, aby pamiętać, że do tego momentu wszystko jest hipotetyczne — nic nie jest rysowane, dopóki te polecenia nie zostaną wykonane.

Oto trzy przykłady z liniami:

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

Ważne jest, aby zwrócić uwagę na kolejność rysowania: wypełniony trójkąt jest rysowany jako ostatni i przykryłby pozostałe dwa kształty, gdyby znajdowały się w tej samej pozycji. Ponadto linia w trójkącie jest rysowana po wypełnieniu. Gdybyśmy odwrócili te kroki, wewnętrzna połowa linii zostałaby przykryta czerwonym wypełnieniem.

Prostokąty są proste i użyteczne, szczególnie przy przeciętnej rozdzielczości klawiatury RGB. Proces jest podobny do rysowania linii — po prostu zastąp polecenia moveTo i lineTo przez rect(x, y, szerokość, wysokość).

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

Łuki są rysowane przez obracanie zewnętrznego punktu wokół centralnego punktu i mogą być używane do tworzenia pełnych okręgów. Polecenie łuku to ctx.arc(x, y, promień, kąt_początkowy, kąt_końcowy), gdzie oba kąty są podane w radianach. W przeciwieństwie do prostokątów, łuk jest rysowany z początkiem (x, y) kształtu w centrum łuku.

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

Jak widać, łuki są domyślnie rysowane zgodnie z ruchem wskazówek zegara. Można to odwrócić, używając kąta końcowego mniejszego niż kąt początkowy, lub dodając “true” jako ostatni argument metody ctx.arc().

Ręczne rysowanie trzech kształtów może łatwo zająć dwadzieścia linii kodu, więc jeśli musisz narysować sto kształtów, potrzebujemy bardziej efektywnego podejścia. Pętle w JavaScript to prosty sposób na powtarzanie zadań i mogą być również zastosowane do rysowania.

Oto przykład z pętlą for do rysowania rzędu zaznaczonych kwadratów:

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

Oto przykład z pętlą while do rysowania szachownicy. Lekko zmieniam jasność czarnych kwadratów i odcień białych kwadratów w każdej iteracji, używając literałów szablonowych.

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

Animacje na canvas są ograniczone jedynie przez twoją wyobraźnię i wiedzę matematyczną. Jeśli nie jesteś pewny trygonometrii lub geometrii, zalecamy przeprowadzenie trochę badań, aby podnieść swoje umiejętności. W poniższych przykładach biorę nieruchomy okrąg i łączę każdą możliwą zmienną z prostym oscylacyjnym ruchem.

Złotym standardem dla oscylacji (regularnego ruchu tam i z powrotem) jest przyjmowanie sin() lub cosinus() timera. Obie operacje zwracają wartość między -1 a 1 DLA KAŻDEJ przekazanej wartości, nawet jeśli ta wartość tylko rośnie. Jedyna różnica między nimi polega na tym, że są lekko przesunięte — gdy cos wynosi 0, sin wynosi -1 lub 1, i odwrotnie. Możemy to wykorzystać na naszą korzyść w tej animacji:

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

Pozostało nam jeszcze kilka atrybutów do zmiany w czasie: promień okręgu, wypełnienie i linia. Na razie zostawię kąty łuku w spokoju, aby kształt pozostał prosty, ponieważ reszta będzie dość spektakularna.

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

Każdy atrybut każdego kształtu, który do tej pory stworzyliśmy, może być zmieniany w czasie, a każda animacja, którą tworzę, jest kombinacją powyższych umiejętności. Aby uzyskać więcej szczegółów na temat tworzenia wielokrotnego użytku, wydajnych animacji, sprawdź naszą stronę Callbacks!