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.
Rysowanie kształtów
Dział zatytułowany „Rysowanie kształtów”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.
- ctx.beginPath() rozpoczyna twój kształt
- ctx.moveTo(x, y) ustawia pierwszy punkt
- 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.
- Ustaw ctx.strokeStyle i/lub ctx.fillStyle (jeśli kształt jest zamknięty)
- 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
Dział zatytułowany „Prostokąty”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().
Rysowanie z iteracją
Dział zatytułowany „Rysowanie z iteracją”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); }
Dodawanie ruchu
Dział zatytułowany „Dodawanie ruchu”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); }Na boki
Dział zatytułowany „Na boki”
W górę i w dół
Dział zatytułowany „W górę i w dół”
Pełny okrąg
Dział zatytułowany „Pełny okrąg”
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!