HTML5+JS
Các hiệu ứng được tạo bằng cách sử dụng vanilla JavaScript trên phần tử HTML5 canvas. Trong phần này, chúng ta sẽ đề cập đến các kiến thức cơ bản: vẽ hình dạng, vẽ với vòng lặp và thêm chuyển động.
Vẽ hình dạng
Phần tiêu đề “Vẽ hình dạng”Đối với hướng dẫn này, tôi sử dụng mẫu Lightscript chung:
<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>Đường thẳng
Phần tiêu đề “Đường thẳng”Dù một hay nhiều đường, quy trình vẫn giống nhau.
- ctx.beginPath() bắt đầu hình dạng của bạn
- ctx.moveTo(x, y) đặt điểm đầu tiên
- ctx.lineTo(x, y) đặt điểm tiếp theo và đường trở lại điểm đầu. Sử dụng nhiều lời gọi lineTo để vẽ các hình dạng lớn hơn.
- Đặt ctx.strokeStyle và/hoặc ctx.fillStyle (nếu hình dạng đóng)
- Sử dụng ctx.stroke() để vẽ đường và ctx.fill() để tô màu các hình đóng. Điều quan trọng cần lưu ý là mọi thứ cho đến thời điểm này đều là giả thuyết — không có gì được vẽ cho đến khi các lệnh này được thực thi.
Đây là ba ví dụ với đường:
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);}
Điều quan trọng cần chú ý là thứ tự vẽ ở đây: tam giác được tô màu được vẽ cuối cùng và sẽ che hai hình kia nếu chúng ở cùng vị trí. Ngoài ra, đường viền trong tam giác được vẽ sau khi tô màu. Nếu chúng ta đảo ngược các bước này, nửa bên trong của đường viền sẽ bị che bởi màu đỏ.
Hình chữ nhật
Phần tiêu đề “Hình chữ nhật”Hình chữ nhật đơn giản và hữu ích, đặc biệt cho độ phân giải trung bình của bàn phím RGB. Quy trình tương tự như vẽ đường — chỉ cần thay thế các lệnh moveTo và lineTo bằng 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); }
Cung tròn
Phần tiêu đề “Cung tròn”Cung tròn được vẽ bằng cách xoay điểm ngoài xung quanh điểm trung tâm và có thể được sử dụng để tạo hình tròn đầy đủ. Lệnh vẽ cung là ctx.arc(x, y, radius, startAngle, endAngle), trong đó cả hai góc đều được biểu thị bằng radian. Không giống như hình chữ nhật, cung được vẽ với điểm gốc (x, y) ở trung tâm của cung.
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); }
Như bạn có thể thấy, các cung mặc định được vẽ theo chiều kim đồng hồ. Điều này có thể được đảo ngược bằng cách đặt góc kết thúc nhỏ hơn góc bắt đầu, hoặc thêm “true” làm đối số cuối cùng của phương thức ctx.arc().
Vẽ với vòng lặp
Phần tiêu đề “Vẽ với vòng lặp”Vẽ thủ công ba hình dạng có thể dễ dàng tốn hai mươi dòng code. Vì vậy, nếu chúng ta cần vẽ một trăm hình, chúng ta cần một cách tiếp cận hiệu quả hơn. Vòng lặp trong JavaScript là cách đơn giản để lặp lại các tác vụ và có thể áp dụng cho việc vẽ.
Đây là ví dụ với vòng lặp for để vẽ một hàng ô bàn cờ:
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); }
Đây là ví dụ với vòng lặp while để vẽ mẫu bàn cờ. Tôi sẽ thay đổi nhẹ độ sáng của các ô đen và sắc độ của các ô trắng trong mỗi lần lặp bằng 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); }
Thêm chuyển động
Phần tiêu đề “Thêm chuyển động”Các animation trên canvas chỉ bị giới hạn bởi trí tưởng tượng và kiến thức toán học của bạn. Nếu bạn chưa tự tin về lượng giác hoặc hình học, tôi khuyên bạn nên nghiên cứu để nâng cao kỹ năng của mình lên một tầm cao mới. Trong các ví dụ sau đây, tôi lấy một vòng tròn đứng yên và gán chuyển động dao động đơn giản cho mỗi biến có thể.
Tiêu chuẩn vàng cho dao động (chuyển động qua lại thường xuyên) là lấy sin() hoặc cos() của bộ hẹn giờ. Mỗi thao tác trả về giá trị từ -1 đến 1 cho MỌI giá trị được truyền vào, ngay cả khi nó chỉ đếm lên. Sự khác biệt duy nhất giữa hai hàm là chúng hơi lệch pha — khi cos bằng 0, sin bằng -1 hoặc 1 và ngược lại. Chúng ta có thể tận dụng điều này trong animation này:
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); }Từ bên này sang bên kia
Phần tiêu đề “Từ bên này sang bên kia”
Lên và xuống
Phần tiêu đề “Lên và xuống”
Vòng tròn đầy đủ
Phần tiêu đề “Vòng tròn đầy đủ”
Chúng ta có thêm một vài thuộc tính có thể thay đổi theo thời gian — bán kính vòng tròn, màu tô và đường viền. Tôi sẽ để các góc cung không thay đổi để giữ hình dạng đơn giản, vì phần còn lại sẽ khá ấn tượng.
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); }
Mỗi thuộc tính của mỗi hình dạng chúng ta đã tạo cho đến nay có thể thay đổi theo thời gian, và mỗi animation tôi tạo ra đều là sự kết hợp của các kỹ năng đã đề cập ở trên. Để biết thêm chi tiết về việc tạo các animation có thể tái sử dụng, hiệu quả, hãy xem trang Callbacks của chúng tôi!