Lewati ke konten

HTML5+JS

Efek dibuat menggunakan JavaScript vanilla pada elemen canvas HTML5. Di bagian ini, kita akan membahas dasar-dasarnya: menggambar bentuk, menggambar dengan iterasi, dan menambahkan gerakan.

Untuk tutorial ini saya akan menggunakan template lightscript generik:

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

Baik menggunakan satu garis maupun banyak, prosesnya tetap sama.

  1. ctx.beginPath() memulai bentuk Anda
  2. ctx.moveTo(x, y) menetapkan titik pertama
  3. ctx.lineTo(x, y) menetapkan titik berikutnya dan garis kembali ke yang pertama. Gunakan beberapa lineTo untuk menggambar bentuk yang lebih besar.
  4. Atur ctx.strokeStyle dan/atau ctx.fillStyle (jika bentuknya tertutup)
  5. Gunakan ctx.stroke() untuk menggambar garis, dan ctx.fill() untuk mengisi bentuk yang tertutup. Penting untuk dicatat bahwa hingga titik ini, semuanya masih bersifat hipotetis - tidak ada yang digambar sampai perintah-perintah ini dieksekusi.

Berikut tiga contoh menggunakan garis:

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

Penting untuk dicatat urutan penggambaran di sini: segitiga yang terisi digambar terakhir, sehingga akan menutupi dua bentuk lainnya jika berada di posisi yang sama. Selain itu, stroke pada segitiga digambar setelah fill. Jika kita membalik langkah-langkah ini, bagian dalam stroke akan tertutup oleh fill merah.

Persegi panjang sederhana dan berguna, terutama untuk resolusi rata-rata keyboard RGB. Prosesnya mirip dengan menggambar garis - cukup ganti perintah moveTo dan lineTo dengan 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);
}

Busur digambar dengan memutar titik luar di sekitar titik pusat, dan dapat digunakan untuk membuat lingkaran penuh. Perintah busur adalah ctx.arc(x, y, radius, sudut awal, sudut akhir), di mana kedua ukuran sudut diberikan dalam radian. Tidak seperti persegi panjang, busur digambar dengan asal (x, y) di tengah busur.

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

Seperti yang Anda lihat, busur digambar searah jarum jam secara default. Ini dapat dibalik dengan sudut akhir yang lebih kecil dari sudut awal, atau dengan menambahkan “true” sebagai argumen terakhir ke metode ctx.arc().

Menggambar tiga bentuk secara manual dapat dengan mudah membutuhkan dua puluh baris kode, jadi jika Anda perlu menggambar seratus bentuk, kita memerlukan pendekatan yang lebih efisien. Loop dalam JavaScript adalah cara sederhana untuk mengulang tugas, dan dapat diterapkan pada penggambaran juga.

Berikut adalah contoh menggunakan for-loop untuk menggambar baris kotak-kotak berselang:

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

Berikut adalah contoh while-loop untuk menggambar grid berselang. Saya akan mengubah lightness kotak hitam dan hue kotak putih sedikit dalam setiap iterasi menggunakan template literal.

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

Animasi dalam canvas hanya dibatasi oleh imajinasi dan pengetahuan matematika Anda. Jika Anda merasa kurang dalam trigonometri atau geometri, saya sarankan untuk melakukan sedikit riset agar dapat meningkatkan kemampuan Anda ke level berikutnya. Dalam beberapa contoh berikutnya, saya akan mengambil lingkaran yang tidak bergerak dan melampirkan setiap variabel yang mungkin pada gerakan osilasi sederhana.

Standar emas untuk osilasi (gerakan bolak-balik yang teratur) adalah mengambil sin() atau cosinus() dari timer. Kedua operasi akan mengembalikan nilai antara -1 dan 1 untuk NILAI APA PUN yang dimasukkan, bahkan jika hanya terus menghitung naik. Satu-satunya perbedaan antara keduanya adalah bahwa mereka sedikit tidak sinkron - ketika cos adalah 0 sin adalah -1 atau 1, dan sebaliknya. Kita dapat menggunakan ini untuk keuntungan kita dalam animasi ini -

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

Kita hanya memiliki beberapa atribut lagi yang perlu diubah seiring waktu - radius lingkaran, fill, dan stroke. Saya akan membiarkan sudut busur untuk saat ini agar bentuknya tetap sederhana karena sisanya akan cukup mencolok.

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

Setiap atribut dari setiap bentuk yang telah kita buat sejauh ini dapat diubah seiring waktu, dan setiap animasi yang saya buat adalah kombinasi dari keterampilan di atas. Untuk lebih detail tentang membuat animasi yang dapat digunakan kembali dan efisien, lihat halaman Callbacks kami!