作者:楊于葳
使用 HTML 檔案練習 Canvas 的簡易實作演練,呈現:下雨特效、重力加速度、四面八方的飛、煙火爆炸畫面。
畫布的基本操作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas 繪圖</title> </head> <body> <h3>Canvas 繪圖</h3> <canvas width="600" height="600" id="cvs" style="border: 1px solid black;"></canvas> <script> let cvs = document.querySelector("#cvs"); // 取得 canvas 物件 let ctx = cvs.getContext("2d"); // 取得對應的 context 物件 // 填滿 ctx.fillStyle = "red"; ctx.fillRect(50, 50, 100, 100); ctx.fillStyle = "black"; ctx.fillText("Hello World", 200, 50); // 描邊 ctx.strokeStyle = "blue"; ctx.strokeRect(50, 200, 50, 50); // 路徑 ctx.beginPath(); ctx.moveTo(200, 200); ctx.lineTo(300, 250); ctx.lineTo(250, 300); ctx.fill(); // 圓形 ctx.beginPath(); ctx.arc(400, 50, 20, 0, 2 * Math.PI); ctx.stroke(); // 曲線 ctx.beginPath(); ctx.moveTo(400, 400); ctx.quadraticCurveTo(450, 200, 550, 350); // 決定控制點和終點 ctx.stroke(); // 圖片 let img = new Image(); img.src = "conan.jpg"; img.addEventListener("load", function () { ctx.drawImage(img, 50, 350, 200, 200); }); </script> </body> </html>
分子系統:下雨特效
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas 繪圖</title> </head> <body> <h3>Canvas 繪圖</h3> <canvas width="600" height="600" id="cvs" style="border: 1px solid black;"></canvas> <script> class Rect { constructor(x, y) { this.x = x; this.y = y; this.vx = Math.random() * 2; // 0 ~ 2 之間的亂數 this.vy = Math.random() * 1; // 0 ~ 1 之間的亂數 } update() { this.x = this.x + this.vx; this.y = this.y + this.vy; } draw() { ctx.fillRect(this.x, this.y, 4, 4); } } let cvs = document.querySelector("#cvs"); // 取得 canvas 物件 let ctx = cvs.getContext("2d"); // 取得對應的 context 物件 let particles = []; // 建立一個空陣列 function refresh() { particles.push(new Rect(50, 50)); // 更新資料 for (let i = 0; i < particles.length; i++) { particles[i].update(); } // 清空畫面 ctx.clearRect(0, 0, cvs.width, cvs.height); // 根據新的資料重新畫過 for (let i = 0; i < particles.length; i++) { particles[i].draw(); } } window.setInterval(refresh, 10); </script> </body> </html>
分子系統:重力加速度
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas 繪圖</title> </head> <body> <h3>Canvas 繪圖</h3> <canvas width="600" height="600" id="cvs" style="border: 1px solid black;"></canvas> <script> class Rect { constructor(x, y) { this.x = x; this.y = y; this.vx = Math.random() * 2; // 0 ~ 2 之間的亂數 this.vy = Math.random() * 1; // 0 ~ 1 之間的亂數 } update() { this.x = this.x + this.vx; this.y = this.y + this.vy; this.vy = this.vy + 0.05; // 重力加速度 // return 是否死掉 return this.x > cvs.width || this.y > cvs.height; } draw() { ctx.fillRect(this.x, this.y, 4, 4); } } let cvs = document.querySelector("#cvs"); // 取得 canvas 物件 let ctx = cvs.getContext("2d"); // 取得對應的 context 物件 let particles = []; // 建立一個空陣列 function refresh() { particles.push(new Rect(50, 50)); // 更新資料 for (let i = 0; i < particles.length; i++) { let isDead = particles[i].update(); if (isDead) { particles.splice(i, 1); i--; } } // 清空畫面 ctx.clearRect(0, 0, cvs.width, cvs.height); // 根據新的資料重新畫過 for (let i = 0; i < particles.length; i++) { particles[i].draw(); } } window.setInterval(refresh, 10); </script> </body> </html>
分子系統:四面八方的飛
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas 繪圖</title> </head> <body> <h3>Canvas 繪圖</h3> <canvas width="600" height="600" id="cvs" style="border: 1px solid black;"></canvas> <script> // 分子系統 Particle System class Rect { constructor(x, y) { this.x = x * 5.5; this.y = y * 5.5; this.vx = (Math.random() - 0.5) * 4; // 隨機範圍在 -2 到 2 之間 this.vy = (Math.random() - 0.5) * 2; // 隨機範圍在 -1 到 1 之間 } update() { this.x = this.x + this.vx; this.y = this.y + this.vy; // this.vy = this.vy + 0.05; // 重力加速度 // return 是否死掉 return this.x > cvs.width || this.y > cvs.height; } draw() { ctx.fillRect(this.x, this.y, 4, 4); ctx.fillStyle = "#77bfd9"; } } let cvs = document.querySelector("#cvs"); // 取得 canvas 物件 let ctx = cvs.getContext("2d"); // 取得對應的 context 物件 let particles = []; // 建立一個空陣列 function refresh() { particles.push(new Rect(50, 50)); // 更新資料 for (let i = 0; i < particles.length; i++) { let isDead = particles[i].update(); if (isDead) { particles.splice(i, 1); i--; } } // 清空畫面 ctx.clearRect(0, 0, cvs.width, cvs.height); // 根據新的資料重新畫過 for (let i = 0; i < particles.length; i++) { particles[i].draw(); } } window.setInterval(refresh, 10); </script> </body> </html>
分子系統:煙火爆炸的飛+黑色背景
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas 繪圖</title> </head> <body style="background-color: black;"> <h3 style="color: white;">Canvas 繪圖</h3> <canvas width="600" height="600" id="cvs" style="border: 1px solid black;"></canvas> <script> // 分子系統 Particle System class Rect { constructor(x, y) { this.x = x * 5.5; this.y = y * 5.5; this.vx = (Math.random() - 0.5) * 4; // 隨機範圍在 -2 到 2 之間 this.vy = (Math.random() - 0.5) * 2; // 隨機範圍在 -1 到 1 之間 } update() { this.x = this.x + this.vx; this.y = this.y + this.vy; this.vy = this.vy + 0.01; // 重力加速度 // return 是否死掉 // return this.x > cvs.width || this.y > cvs.height; return this.alpha < 0.02; } draw() { ctx.fillStyle = "#FFD700"; // 將顏色設定為黃色 ctx.fillRect(this.x, this.y, 4, 4); } } let cvs = document.querySelector("#cvs"); // 取得 canvas 物件 let ctx = cvs.getContext("2d"); // 取得對應的 context 物件 let particles = []; // 建立一個空陣列 function refresh() { particles.push(new Rect(50, 50)); // 更新資料 for (let i = 0; i < particles.length; i++) { let isDead = particles[i].update(); if (isDead) { particles.splice(i, 1); i--; } } // 清空畫面 ctx.clearRect(0, 0, cvs.width, cvs.height); // 根據新的資料重新畫過 for (let i = 0; i < particles.length; i++) { particles[i].draw(); } } window.setInterval(refresh, 10); </script> </body> </html>
分子系統:冒煙
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas 繪圖</title> </head> <body> <h3>Canvas 繪圖</h3> <canvas width="600" height="600" id="cvs" style="border: 1px solid black;"></canvas> <script> // 分子系統 Particle System class Rect { constructor(x, y) { this.x = x; this.y = y; this.vx = (Math.random() - 0.5) * 4; // 隨機範圍在 -2 到 2 之間 this.vy = (Math.random() - 0.5) * 2; // 隨機範圍在 -1 到 1 之間 } update() { this.x = this.x + this.vx; this.y = this.y + this.vy; // this.vy = this.vy + 0.01; // 重力加速度 // return 是否死掉 return this.x > cvs.width || this.y > cvs.height; } draw() { ctx.fillRect(this.x, this.y, 4, 4); ctx.fillStyle = "#77bfd9"; } } class Smoke { constructor(x, y) { this.x = x; this.y = y; this.size = 20; this.alpha = 1; this.vx = Math.random() * 0.8 - 0.4; // -0.3 ~ 0.5 this.vy = Math.random() * 1.5 - 2; // -2 ~ -0.5 } update() { this.x += this.vx; this.y += this.vy; this.size += 0.25; this.alpha -= 0.002; return this.alpha < 0.03; } draw() { ctx.globalAlpha = this.alpha; ctx.drawImage(smokeImg, this.x - (this.size / 2), this.y - (this.size / 2), 30, 30 ); } } let cvs = document.querySelector("#cvs"); // 取得 canvas 物件 let ctx = cvs.getContext("2d"); // 取得對應的 context 物件 let particles = []; // 建立一個空陣列 function refresh() { particles.push(new Smoke(cvs.width / 2, 420)); // 更新資料 for (let i = 0; i < particles.length; i++) { let isDead = particles[i].update(); if (isDead) { particles.splice(i, 1); i--; } } // 清空畫面 ctx.clearRect(0, 0, cvs.width, cvs.height); // 根據新的資料重新畫過 for (let i = 0; i < particles.length; i++) { particles[i].draw(); } } // 預先載入需要的圖片 let smokeImg = new Image(); smokeImg.src = "smoke.png"; smokeImg.addEventListener("load", function () { window.setInterval(refresh, 10); }); </script> </body> </html>
分子系統:移動的火把
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas 繪圖</title> </head> <body> <h3>Canvas 繪圖</h3> <canvas width="600" height="600" id="cvs" style="border: 1px solid black;"></canvas> <script> class Torch { constructor(x, y) { this.x = x; this.vx = 1; this.y = y; this.smokes = []; } update() { this.x += this.vx; if (this.x > cvs.width) { this.vx = -1; } if (this.x < 0) { this.vx = 1; } this.smokes.push(new Smoke(this.x, this.y)); for (let i = 0; i < this.smokes.length; i++) { let isDead = this.smokes[i].update(); if (isDead) { this.smokes.splice(i, 1); i--; } } } draw() { ctx.fillStyle = "#803C49"; ctx.fillRect(this.x - 5, this.y, 10, 30); for (let i = 0; i < this.smokes.length; i++) { this.smokes[i].draw(); } } } class Smoke { constructor(x, y) { this.x = x; this.y = y; this.size = 20; this.alpha = 1; this.vx = Math.random() * 0.8 - 0.4; // -0.3 ~ 0.5 this.vy = Math.random() * 1.5 - 2; // -2 ~ -0.5 } update() { this.x += this.vx; this.y += this.vy; this.size += 0.25; this.alpha -= 0.009; return this.alpha < 0.05; } draw() { ctx.globalAlpha = this.alpha; ctx.drawImage(smokeImg, this.x - (this.size / 2), this.y - (this.size / 2), 20, 20 ); } } class Rect { constructor(x, y) { this.x = x; this.y = y; this.vx = (Math.random() - 0.5) * 4; // 隨機範圍在 -2 到 2 之間 this.vy = (Math.random() - 0.5) * 2; // 隨機範圍在 -1 到 1 之間 } update() { this.x = this.x + this.vx; this.y = this.y + this.vy; this.vy = this.vy + 0.01; // 重力加速度 //return 是否死掉 return this.x > cvs.width || this.y > cvs.height; } draw() { ctx.fillRect(this.x, this.y, 4, 4); ctx.fillStyle = "#77bfd9"; } } // 分子系統 Particle System let cvs = document.querySelector("#cvs"); // 取得 canvas 物件 let ctx = cvs.getContext("2d"); // 取得對應的 context 物件 let particles = [new Torch(cvs.width / 2, 400)]; // 建立一個空陣列 function refresh() { // 更新資料 for (let i = 0; i < particles.length; i++) { let isDead = particles[i].update(); if (isDead) { particles.splice(i, 1); i--; } } // 清空畫面 ctx.clearRect(0, 0, cvs.width, cvs.height); // 根據新的資料重新畫過 for (let i = 0; i < particles.length; i++) { particles[i].draw(); } } // 預先載入需要的圖片 let smokeImg = new Image(); smokeImg.src = "fire.png"; smokeImg.addEventListener("load", function () { window.setInterval(refresh, 10); }); </script> </body> </html>
自由新增會移動的火把
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas 繪圖</title> </head> <body> <h3>Canvas 繪圖</h3> <canvas width="600" height="600" id="cvs" style="border: 1px solid black;"></canvas> <script> class Torch { constructor(x, y) { this.x = x; this.vx = 1; this.y = y; this.smokes = []; } update() { this.x += this.vx; if (this.x > cvs.width) { this.vx = -1; } if (this.x < 0) { this.vx = 1; } this.smokes.push(new Smoke(this.x, this.y)); for (let i = 0; i < this.smokes.length; i++) { let isDead = this.smokes[i].update(); if (isDead) { this.smokes.splice(i, 1); i--; } } } draw() { ctx.fillStyle = "#803C49"; ctx.fillRect(this.x - 5, this.y, 10, 30); for (let i = 0; i < this.smokes.length; i++) { this.smokes[i].draw(); } } } class Smoke { constructor(x, y) { this.x = x; this.y = y; this.size = 20; this.alpha = 1; this.vx = Math.random() * 0.8 - 0.4; // -0.3 ~ 0.5 this.vy = Math.random() * 1.5 - 2; // -2 ~ -0.5 } update() { this.x += this.vx; this.y += this.vy; this.size += 0.25; this.alpha -= 0.009; return this.alpha < 0.05; } draw() { ctx.globalAlpha = this.alpha; ctx.drawImage(smokeImg, this.x - (this.size / 2), this.y - (this.size / 2), 20, 20 ); } } class Rect { constructor(x, y) { this.x = x; this.y = y; this.vx = (Math.random() - 0.5) * 4; // 隨機範圍在 -2 到 2 之間 this.vy = (Math.random() - 0.5) * 2; // 隨機範圍在 -1 到 1 之間 } update() { this.x = this.x + this.vx; this.y = this.y + this.vy; this.vy = this.vy + 0.01; // 重力加速度 //return 是否死掉 return this.x > cvs.width || this.y > cvs.height; } draw() { ctx.fillRect(this.x, this.y, 4, 4); ctx.fillStyle = "#77bfd9"; } } // 分子系統 Particle System let cvs = document.querySelector("#cvs"); // 取得 canvas 物件 let ctx = cvs.getContext("2d"); // 取得對應的 context 物件 cvs.addEventListener("click", function (e) { particles.push(new Torch(e.offsetX, e.offsetY)); }); let particles = [new Torch(cvs.width / 2, 400)]; // 建立一個空陣列 function refresh() { // 更新資料 for (let i = 0; i < particles.length; i++) { let isDead = particles[i].update(); if (isDead) { particles.splice(i, 1); i--; } } // 清空畫面 ctx.clearRect(0, 0, cvs.width, cvs.height); // 根據新的資料重新畫過 for (let i = 0; i < particles.length; i++) { particles[i].draw(); } } // 預先載入需要的圖片 let smokeImg = new Image(); smokeImg.src = "fire.png"; smokeImg.addEventListener("load", function () { window.setInterval(refresh, 10); }); </script> </body> </html>