HTML5+JS
特效是使用原生 JavaScript 在 HTML5 canvas 元素上建立的。在本節中,我們將介紹基礎知識:繪製形狀、使用迭代繪製,以及添加動態效果。
在本教學中,我將使用一個通用的 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> // 從 DOM 取得 canvas 元素 var c = document.getElementById("exCanvas"); var ctx = c.getContext("2d");
var width = 320; var height = 200; var hue = 0;
function update() { // 程式碼在此 window.requestAnimationFrame(update); }
window.requestAnimationFrame(update);</script>無論使用一條線還是多條線,過程都相同。
- ctx.beginPath() 開始您的形狀
- ctx.moveTo(x, y) 設定第一個點
- ctx.lineTo(x, y) 設定下一個點並繪製回第一個點的線條。使用多個 lineTo 來繪製更大的形狀。
- 設定 ctx.strokeStyle 和/或 ctx.fillStyle(如果形狀是封閉的)
- 使用 ctx.stroke() 繪製線條,使用 ctx.fill() 填充任何封閉形狀。需要注意的是,在此之前一切都是假設性的——直到執行這些命令才會真正繪製出來。
以下是三個使用線條的範例:
function update() { // 單條線 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();
// 填充三角形 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);}
請注意這裡的繪製順序:填充三角形最後繪製,因此如果它們在相同位置,它會覆蓋其他兩個形狀。此外,三角形的描邊在填充之後繪製。如果我們顛倒這些步驟,描邊的內部一半將被紅色填充覆蓋。
矩形簡單且實用,尤其對於 RGB 鍵盤的平均解析度而言。過程與繪製線條類似——只需將 moveTo 和 lineTo 命令替換為 rect(x, y, width, height)。
function update() { // 基本正方形 ctx.beginPath(); ctx.rect(30, 30, 75, 75); ctx.strokeStyle = "blue"; ctx.stroke();
// 填充矩形 ctx.beginPath(); ctx.rect(125, 30, 125, 100); ctx.fillStyle = "red"; ctx.strokeStyle = "blue"; ctx.fill(); ctx.stroke();
window.requestAnimationFrame(update); }
弧形是透過將外點圍繞中心點旋轉來繪製的,可用於建立完整的圓形。弧形命令是 ctx.arc(x, y, radius, start angle, end angle),其中兩個角度值都以弧度表示。與矩形不同,弧形的原點 (x, y) 在弧形的中心。
function update() { // 半圓 ctx.beginPath(); ctx.arc(70, 100, 30, 0, Math.PI); ctx.strokeStyle = "blue"; ctx.stroke();
// 3/4 圓 ctx.beginPath(); ctx.arc(140, 100, 30, 0, Math.PI * (3/2)); ctx.strokeStyle = "blue"; ctx.stroke();
// 完整圓形 ctx.beginPath(); ctx.arc(210, 100, 30, 0, Math.PI * 2); ctx.strokeStyle = "blue"; ctx.stroke(); ctx.fillStyle = "red"; ctx.fill();
window.requestAnimationFrame(update); }
如您所見,弧形預設按順時針方向繪製。這可以透過使用小於起始角度的結束角度來反轉,或在 ctx.arc() 方法中添加 “true” 作為最後一個參數。
使用迭代繪製
Section titled “使用迭代繪製”手動繪製三個形狀可能輕易佔用二十行程式碼,因此如果您需要繪製一百個形狀,我們需要更有效率的方法。JavaScript 中的迴圈是重複任務的簡單方式,也可以應用於繪製。
以下是使用 for 迴圈繪製一排棋盤格正方形的範例:
function update() {
for(let i = 0; i < 8; i++){
// 1. 為每個正方形建立矩形路徑並繪製描邊。 ctx.beginPath(); ctx.rect(i * 20, 20, 20, 20); ctx.strokeStyle = "black"; ctx.stroke();
// 2. 如果 'i' 為偶數,fillStyle 為黑色。否則為白色。 if(i % 2 == 0){ ctx.fillStyle = "black"; } else { ctx.fillStyle = "white"; } // 3. 設定 fillStyle 後填充形狀。 ctx.fill(); }
window.requestAnimationFrame(update); }
以下是一個 while 迴圈範例,用於繪製棋盤格。我將在每次迭代中稍微改變黑色正方形的亮度和白色正方形的色相,使用範本字面值。
function update() {
var i = 0;
while(i < 16){
// 1. 使用 'row' 和 'column' 計算 X 和 Y // 此操作將結果向下捨入到最接近的整數。對於 i = (0-3),行將為 0,對於 i = (4-7),行將為 1,依此類推 var iRow = Math.floor(i / 4); // 此操作找到除法後的餘數,將列限制在 (0-3) 範圍內。 var iCol = i % 4; // 乘以正方形寬度以找到此正方形的 x 原點 var ix = iCol * 20; // 乘以正方形高度以找到此正方形的 y 原點 var iy = iRow * 20;
// 2. 路徑和描邊 ctx.beginPath(); ctx.rect(ix, iy, 20, 20); ctx.strokeStyle = "black"; ctx.stroke();
// 3. 如果行為偶數,每隔一個正方形填充黑色。如果為奇數,切換黑色正方形。 // 每次迭代都會在黑色的亮度分量和白色的色相中添加少量值。 if(iRow % 2 == 0){ if(i % 2 == 0){ // '黑色' ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)` } else { // '白色' ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`; } } else { if(i % 2 == 0){ // '白色' ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`; } else { // '黑色' ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)` } }
ctx.fill(); i++; }
window.requestAnimationFrame(update); }
添加動態效果
Section titled “添加動態效果”canvas 中的動畫只受您的想像力和數學知識的限制。如果您對三角學或幾何學感到不確定,我建議您做一些輕度的研究以提升技能。在接下來的幾個範例中,我將取一個靜止的圓形,並將每個可能的變數附加到一個簡單的振盪運動上。
振盪(規律的來回運動)的黃金標準是取定時器的 sin() 或 cosine()。無論傳入任何值,這兩個操作都將返回 -1 到 1 之間的值,即使該值只是在遞增。兩者之間唯一的區別是它們略微不同步——當 cos 為 0 時,sin 為 -1 或 1,反之亦然。我們可以在這個動畫中利用這一點——
function update() {
// 以毫秒為單位取得當前時間,然後除以減慢動畫速度 let time = Date.now() / 100;
// 繪製背景,使舊形狀不會殘留 ctx.fillStyle = "white"; ctx.fillRect(0, 0, 320,200);
ctx.beginPath();
// 每次只使用一個範例 // 第 1 個範例 — 左右移動,y 為常數 ctx.arc(15 * Math.cos(time) + 160, 100, 30, 0, Math.PI * 2); // 第 2 個範例 — 上下移動,x 為常數 ctx.arc(160, 15 * Math.sin(time) + 100, 30, 0, Math.PI * 2); // 第 3 個範例 — 圓形移動,x 和 y 都是變數 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); }


我們還有幾個屬性可以隨時間改變——圓形半徑、填充和描邊。我暫時不改變弧形角度以保持形狀簡單,因為其餘部分會相當炫目。
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); }
我們迄今建立的每個形狀的每個屬性都可以隨時間改變,而我製作的每個動畫都是上述技能的組合。如需製作可重用、高效動畫的更多詳細資訊,請查看我們的 Callbacks 頁面!