作者:楊于葳

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