解释:


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
浏览:386

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]删除
最新教程 更多>
  • 如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 识别今天生日的用户使用 MySQL 确定今天是否是用户的生日涉及查找生日匹配的所有行今天的日期。这可以通过一个简单的 MySQL 查询来实现,该查询将存储为 UNIX 时间戳的生日与今天的日期进行比较。以下 SQL 查询将获取今天有生日的所有用户: FROM USERS ...
    编程 发布于2024-11-16
  • 除了“if”语句之外:还有哪些地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    除了“if”语句之外:还有哪些地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    无需强制转换即可上下文转换为 bool您的类定义了对 bool 的显式转换,使您能够在条件语句中直接使用其实例“t”。然而,这种显式转换提出了一个问题:“t”在哪里可以在不进行强制转换的情况下用作 bool?上下文转换场景C 标准指定了四种值可以根据上下文转换为 bool 的主要场景:语句:if、w...
    编程 发布于2024-11-16
  • 什么时候应该使用 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() 函数查找该子字符串在输入字符串中的位置。然后,...
    编程 发布于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 p...
    编程 发布于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

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3