WFU

2023年8月9日 星期三

HTML 的 Canvas 繪圖演練

作者:楊于葳




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





關於作者