解释:


2. 使用 CSS 设置画布样式

让我们添加一个简单的样式,为画布提供黑色背景,并确保删除所有内边距和边距。

* {    margin: 0;    padding: 0;    box-sizing: border-box;}canvas {    background-color: black;}

解释:


3. 粒子类:创造魔法

Particle类是动画的核心所在。每个粒子在画布上移动,留下其过去位置的痕迹,创造流动的效果。

class Particle {    constructor(effect) {        this.effect = effect;        this.x = Math.floor(Math.random() * this.effect.width);        this.y = Math.floor(Math.random() * this.effect.height);        this.speedModifier = Math.floor(Math.random() * 5   1);        this.history = [{ x: this.x, y: this.y }];        this.maxLength = Math.floor(Math.random() * 200   10);        this.timer = this.maxLength * 2;        this.colors = [\\'#4C026B\\', \\'#8E0E00\\', \\'#9D0208\\', \\'#BA1A1A\\', \\'#730D9E\\'];        this.color = this.colors[Math.floor(Math.random() * this.colors.length)];    }    draw(context) {        context.beginPath();        context.moveTo(this.history[0].x, this.history[0].y);        for (let i = 1; i < this.history.length; i  ) {            context.lineTo(this.history[i].x, this.history[i].y);        }        context.strokeStyle = this.color;        context.stroke();    }    update() {        this.timer--;        if (this.timer >= 1) {            let x = Math.floor(this.x / this.effect.cellSize);            let y = Math.floor(this.y / this.effect.cellSize);            let index = y * this.effect.cols   x;            let angle = this.effect.flowField[index];            this.speedX = Math.cos(angle);            this.speedY = Math.sin(angle);            this.x  = this.speedX * this.speedModifier;            this.y  = this.speedY * this.speedModifier;            this.history.push({ x: this.x, y: this.y });            if (this.history.length > this.maxLength) {                this.history.shift();            }        } else if (this.history.length > 1) {            this.history.shift();        } else {            this.reset();        }    }    reset() {        this.x = Math.floor(Math.random() * this.effect.width);        this.y = Math.floor(Math.random() * this.effect.height);        this.history = [{ x: this.x, y: this.y }];        this.timer = this.maxLength * 2;    }}

解释:


4.效果类:组织动画

Effect 类处理粒子的创建和流场本身,它控制粒子的运动。

class Effect {    constructor(canvas) {        this.canvas = canvas;        this.width = this.canvas.width;        this.height = this.canvas.height;        this.particles = [];        this.numberOfParticles = 3000;        this.cellSize = 20;        this.flowField = [];        this.curve = 5;        this.zoom = 0.12;        this.debug = true;        this.init();    }    init() {        this.rows = Math.floor(this.height / this.cellSize);        this.cols = Math.floor(this.width / this.cellSize);        for (let y = 0; y < this.rows; y  ) {            for (let x = 0; x < this.cols; x  ) {                let angle = (Math.cos(x * this.zoom)   Math.sin(y * this.zoom)) * this.curve;                this.flowField.push(angle);            }        }        for (let i = 0; i < this.numberOfParticles; i  ) {            this.particles.push(new Particle(this));        }    }    drawGrid(context) {        context.save();        context.strokeStyle = \\'white\\';        context.lineWidth = 0.3;        for (let c = 0; c < this.cols; c  ) {            context.beginPath();            context.moveTo(c * this.cellSize, 0);            context.lineTo(c * this.cellSize, this.height);            context.stroke();        }        for (let r = 0; r < this.rows; r  ) {            context.beginPath();            context.moveTo(0, r * this.cellSize);            context.lineTo(this.width, r * this.cellSize);            context.stroke();        }        context.restore();    }    render(context) {        if (this.debug) this.drawGrid(context);        this.particles.forEach(particle => {            particle.draw(context);            particle.update();        });    }}

解释:


5. 通过动画循环让它变得栩栩如生

为了让一切正常工作,我们需要一个动画循环来不断清除画布并重新渲染粒子:

const effect = new Effect(canvas);function animate() {    ctx.clearRect(0, 0, canvas.width, canvas.height);    effect.render(ctx);    requestAnimationFrame(animate);}animate();

解释:


结论

通过分解 Particle 和 Effect 类,我们仅使用普通 JavaScript 创建了流体和动态流场动画。 HTML 画布的简单性与 JavaScript 的三角函数相结合,使我们能够构建这些令人着迷的视觉效果。

随意使用粒子数、颜色或流场公式来创建您自己的独特效果!

","image":"http://www.luping.net/uploads/20241022/17296041676717aa472ee02.jpg","datePublished":"2024-11-09T01:12:23+08:00","dateModified":"2024-11-09T01:12:23+08:00","author":{"@type":"Person","name":"luping.net","url":"https://www.luping.net/articlelist/0_1.html"}}
」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 流場螢幕

流場螢幕

發佈於2024-11-09
瀏覽:322

Flow Field Screen

使用 Vanilla JS 和 HTML Canvas 的动态流场

您是否曾被抽象粒子动画迷住过?这些流动的动态视觉效果可以通过使用纯 JavaScript 和 HTML canvas 元素的极其简单的技术来实现。在本文中,我们将分解创建一个流场的过程,该流场可以为数千个粒子提供动画,让它们自然运动。

1. 设置项目

首先,我们需要三个文件:一个用于设置画布的 HTML 文件、一个用于样式设置的 CSS 文件以及一个用于处理逻辑的 JavaScript 文件。



    Flow Fields

解释:

  • 我们定义一个 元素,所有动画都将在其中发生。
  • styles.css 将链接到画布样式。
  • 主要动画逻辑包含在script.js中。

2. 使用 CSS 设置画布样式

让我们添加一个简单的样式,为画布提供黑色背景,并确保删除所有内边距和边距。

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

canvas {
    background-color: black;
}

解释:

  • 将边距和填充设置为零可确保画布填满整个屏幕。
  • 黑色背景为白色颗粒提供了良好的对比度。

3. 粒子类:创造魔法

Particle类是动画的核心所在。每个粒子在画布上移动,留下其过去位置的痕迹,创造流动的效果。

class Particle {
    constructor(effect) {
        this.effect = effect;
        this.x = Math.floor(Math.random() * this.effect.width);
        this.y = Math.floor(Math.random() * this.effect.height);
        this.speedModifier = Math.floor(Math.random() * 5   1);
        this.history = [{ x: this.x, y: this.y }];
        this.maxLength = Math.floor(Math.random() * 200   10);
        this.timer = this.maxLength * 2;
        this.colors = ['#4C026B', '#8E0E00', '#9D0208', '#BA1A1A', '#730D9E'];
        this.color = this.colors[Math.floor(Math.random() * this.colors.length)];
    }

    draw(context) {
        context.beginPath();
        context.moveTo(this.history[0].x, this.history[0].y);
        for (let i = 1; i = 1) {
            let x = Math.floor(this.x / this.effect.cellSize);
            let y = Math.floor(this.y / this.effect.cellSize);
            let index = y * this.effect.cols   x;
            let angle = this.effect.flowField[index];

            this.speedX = Math.cos(angle);
            this.speedY = Math.sin(angle);
            this.x  = this.speedX * this.speedModifier;
            this.y  = this.speedY * this.speedModifier;

            this.history.push({ x: this.x, y: this.y });
            if (this.history.length > this.maxLength) {
                this.history.shift();
            }
        } else if (this.history.length > 1) {
            this.history.shift();
        } else {
            this.reset();
        }
    }

    reset() {
        this.x = Math.floor(Math.random() * this.effect.width);
        this.y = Math.floor(Math.random() * this.effect.height);
        this.history = [{ x: this.x, y: this.y }];
        this.timer = this.maxLength * 2;
    }
}

解释:

  • 构造函数:每个粒子都用随机位置和移动速度初始化。历史数组跟踪过去的位置以创建轨迹。
  • draw():该函数根据粒子的历史记录绘制粒子的路径。粒子留下彩色轨迹,增加视觉效果。
  • update():这里,通过计算与流场的角度来更新粒子的位置。速度和方向由三角函数控制。
  • reset():当粒子完成其轨迹时,它会重置到新的随机位置。

4.效果类:组织动画

Effect 类处理粒子的创建和流场本身,它控制粒子的运动。

class Effect {
    constructor(canvas) {
        this.canvas = canvas;
        this.width = this.canvas.width;
        this.height = this.canvas.height;
        this.particles = [];
        this.numberOfParticles = 3000;
        this.cellSize = 20;
        this.flowField = [];
        this.curve = 5;
        this.zoom = 0.12;
        this.debug = true;
        this.init();
    }

    init() {
        this.rows = Math.floor(this.height / this.cellSize);
        this.cols = Math.floor(this.width / this.cellSize);
        for (let y = 0; y  {
            particle.draw(context);
            particle.update();
        });
    }
}

解释:

  • 构造函数:初始化画布尺寸、粒子数量、流场。
  • init():通过组合每个网格单元的三角函数来计算流场的角度。该场影响粒子的移动方式。
  • drawGrid():绘制将画布划分为单元格的网格,调试时使用。
  • render():调用每个粒子的绘制和更新方法,以在画布上为粒子设置动画。

5. 通过动画循环让它变得栩栩如生

为了让一切正常工作,我们需要一个动画循环来不断清除画布并重新渲染粒子:

const effect = new Effect(canvas);

function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    effect.render(ctx);
    requestAnimationFrame(animate);
}
animate();

解释:

  • clearRect():清除每一帧上的画布,以避免覆盖之前的帧。
  • requestAnimationFrame:通过递归调用animate()函数来保持动画流畅。

结论

通过分解 Particle 和 Effect 类,我们仅使用普通 JavaScript 创建了流体和动态流场动画。 HTML 画布的简单性与 JavaScript 的三角函数相结合,使我们能够构建这些令人着迷的视觉效果。

随意使用粒子数、颜色或流场公式来创建您自己的独特效果!

版本聲明 本文轉載於:https://dev.to/ibra-kdbra/flow-field-screen-567c?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 什麼時候應該使用 jmap 的 -F 選項進行堆轉儲?
    什麼時候應該使用 jmap 的 -F 選項進行堆轉儲?
    jmap 操作期間無法開啟套接字檔案:-F 選項嘗試使用jmap 取得堆轉儲時遇到問題,導致錯誤訊息:「無法開啟套接字檔案」。這表示 HotSpot JVM 未載入或目標程序無回應。 為了解決這個問題,使用 -F 選項來利用一種不同的機制,稱為 HotSpot Serviceability Agen...
    程式設計 發佈於2024-11-16
  • 如何讓 CSS 動畫在 Webkit 完成後保持原狀?
    如何讓 CSS 動畫在 Webkit 完成後保持原狀?
    理解Webkit CSS動畫持久性使用CSS3動畫時,經常會遇到動畫元素恢復到原始狀態的情況動畫完成。雖然此行為符合動畫停止的標準邏輯,但在某些用例中似乎違反直覺。 考慮提供的範例,其中使用 Webkit CSS 語法將「drop_box」元素設定為下降 100 像素的動畫。動畫完成後,框中的文字跳...
    程式設計 發佈於2024-11-16
  • 如何使用 Selenium 點選具有複雜 HTML 結構的按鈕?
    如何使用 Selenium 點選具有複雜 HTML 結構的按鈕?
    Selenium 點擊具有複雜HTML 結構的按鈕當您嘗試使用Selenium 點擊具有複雜HTML 結構的按鈕時,您可能會遇到NoSuchElementException。當按鈕的 HTML 包含多個具有 onclick 屬性的類別或元素時,可能會發生這種情況。 若要精確按一下此類按鈕,請依照下列...
    程式設計 發佈於2024-11-16
  • 如何刪除 PHP 中特定子字串之後的所有內容?
    如何刪除 PHP 中特定子字串之後的所有內容?
    如何刪除PHP 中特定子字串之後的部分字串您可以使用下列指令刪除PHP 中特定子字串之後的所有內容substr() 函數。 substr()函數接受三個參數:輸入字串起始位置長度 要刪除某個子字串之後的所有內容,可以使用strpos() 函數來找出該子字串在輸入字串中的位置。然後,您可以使用 sub...
    程式設計 發佈於2024-11-16
  • 在 Go 中使用 WebSocket 進行即時通信
    在 Go 中使用 WebSocket 進行即時通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    程式設計 發佈於2024-11-16
  • 我們如何計算 JavaScript 物件的大致記憶體佔用量?
    我們如何計算 JavaScript 物件的大致記憶體佔用量?
    確定 JavaScript 物件的記憶體佔用在 JavaScript 中,了解物件的記憶體消耗對於優化效能和避免記憶體洩漏至關重要。本文解決了獲取 JavaScript 物件大小的查詢,深入研究了估計此類物件所佔用的大致記憶體的解決方案。 確定物件大小In JavaScript 中沒有專門設計的內建...
    程式設計 發佈於2024-11-16
  • 如何使用 CSS 設定圖片地圖區域的樣式?
    如何使用 CSS 設定圖片地圖區域的樣式?
    您可以使用 CSS 在圖片映射上設定滑鼠懸停樣式嗎? 圖像映射用於描繪圖像中的可單擊區域。預設顯示為簡單幾何形狀的這些區域的樣式可以用作視覺互動介面。人們可能會想知道這對 CSS 是否可行,CSS 是網站美觀不可或缺的工具。雖然 CSS 無法直接設定圖像映射區域的樣式,但有一些巧妙的解決方法可以實現...
    程式設計 發佈於2024-11-16
  • 大批
    大批
    方法是可以在物件上呼叫的 fns 數組是對象,因此它們在 JS 中也有方法。 slice(begin):將陣列的一部分提取到新數組中,而不改變原始數組。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index ...
    程式設計 發佈於2024-11-16
  • 當我的傳單地圖位於資料切換標籤內時,為什麼我無法下載它?
    當我的傳單地圖位於資料切換標籤內時,為什麼我無法下載它?
    資料切換標籤阻礙傳單地圖下載當資料切換標籤中的傳單地圖無法正確下載時,就會出現此問題。地圖以前在選項卡外部顯示時可以正常工作。 原因Leaflet 在讀取容器大小時初始化地圖。當容器最初被隱藏或其尺寸發生變化時,Leaflet 仍然不知道這些變化,導致錯誤的切片下載。在 Bootstrap 等框架中...
    程式設計 發佈於2024-11-16
  • 如何在 C++ 中實現 Go 風格的 Defer 而不犧牲效能?
    如何在 C++ 中實現 Go 風格的 Defer 而不犧牲效能?
    C 語言中的 Defer 實作 Go 風格的 defer 概念允許乾淨簡潔的資源清理,在 C 語言中很受歡迎。然而,為此功能找到標準或支援良好的庫實現可能具有挑戰性。 儘管標準範本庫 (STL) 或 Boost 中缺乏對 defer 的內建支持,但仍有可用的外部實作。其中一個實作是輕量級、零開銷的解...
    程式設計 發佈於2024-11-16
  • 如何在 Go 中使用反射自訂 JSON 解組?
    如何在 Go 中使用反射自訂 JSON 解組?
    使用反射自訂 JSON 解組在 Go 中,將 JSON 解組為結構體是一個簡單的過程。然而,當處理具有自訂標籤的欄位時,例如 json:"some_field",標準的解組機制可能不夠。 處理這種情況的一種方法是使用反射。透過使用反射檢查結構體的字段,我們可以檢查字段是否具有特定...
    程式設計 發佈於2024-11-16
  • 如何輕鬆地將程式碼傳輸到 Python 解釋器中而不出現縮排問題?
    如何輕鬆地將程式碼傳輸到 Python 解釋器中而不出現縮排問題?
    便捷的代碼傳輸:繞過Python 的空白敏感度由於語言嚴格的空白,將代碼直接複製粘貼到Python 解釋器中可能會出現問題敏感度。這通常會導致意外的程式碼執行或語法錯誤。 IPython 作為解決方案IPython 是一種高級 Python 命令 shell,透過其專用命令。 %cpaste:將剪貼...
    程式設計 發佈於2024-11-16
  • Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta:列偏移的刪除和恢復Bootstrap 4 在其Beta 1 版本中引入了重大更改柱子偏移了。然而,隨著 Beta 2 的後續發布,這些變化已經逆轉。 從 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    程式設計 發佈於2024-11-16
  • Python 請求如何使用 Javascript 處理動態網站?
    Python 請求如何使用 Javascript 處理動態網站?
    克服 Python 請求的 Javascript 障礙傳統的 Python 請求旨在從靜態 HTML 頁面中提取資訊。然而,許多現代網站使用 Javascript 來動態獲取數據,這給 Requests 帶來了挑戰。 是否有解決方法可以利用 Javascript 頁面的 Requests ? 絕對可...
    程式設計 發佈於2024-11-16
  • 從本地主機到生產:OneBootcamp 的問題優先 SRE 之旅
    從本地主機到生產:OneBootcamp 的問題優先 SRE 之旅
    我在X(以前的Twitter)上遇到了One2N 舉辦的一個問題優先的SRE 訓練營(在這裡你可以構建一個應用程序,並將其從本地主機擴展到生產環境),我想,「哎呀是啊!我會嘗試一下。」這篇部落格文章開啟了我的旅程,我將記錄我在訓練營中處理每項練習的經驗。 這篇介紹文章將連結到其他條目,詳細介紹我對...
    程式設計 發佈於2024-11-16

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3