用 Canvas 编织璀璨星空图
发布日期:2021-05-18 06:13:38 浏览次数:14 分类:精选文章

本文共 4248 字,大约阅读时间需要 14 分钟。

实现粒子系统网格动态效果

最近哈,这个粒子系统网格的效果真是太酷了!当我看到它动起来的时候,脑子里不停地想着该怎么实现它。其实这不是特别难,但得一步步来,仔细思考每一个细节。

首先,我们来分析一下这个效果到底有哪些要点。这个效果主要是由大量粒子组成的网格,每个粒子可以看作是一个微小的点。当你在屏幕上移动鼠标的时候,这些粒子会跟随鼠标移动,而当它们移动到边缘时,会反弹回来。连接粒子的线条也会随着粒子的距离而变化,距离近的粒子之间的连接线比较粗而且透明度低,距离远的则相反。

开始编码

首先是HTML部分,我简单放了一个 canvas 标签,并设置了它的样式,使其填充全屏。

为了让所有元素不留空隙,我还设置了一条全局样式:

*{    margin: 0;    padding: 0;}

JavaScript部分

接下来到了核心代码部分。首先我们要获取到 canvas 和绘图上下文:

var canvasEl = document.getElementById('canvas');var ctx = canvasEl.getContext('2d');var mousePos = [0, 0];var easingFactor = 5.0;var backgroundColor = '#000';var nodeColor = '#fff';var edgeColor = '#fff';

然后我们声明了两个变量来存储“星星”和边:

var nodes = [];var edges = [];

为了确保画布在窗口大小发生变化时能够重新绘制并调整分辨率,我们设置了一个 resize 事件监听器:

window.onresize = function(){    canvasEl.width = document.body.clientWidth;    canvasEl.height = canvasEl.clientHeight;    if (nodes.length === 0)    {        constructNodes();    }    render();};window.onresize();

第一次大小变化后,我们会调用 constructNodes() 来构建所有节点。这是个重要的函数,因为它随机创建了一些点,并将它们的信息存储在一个字典对象中:

function constructNodes(){    for (var i = 0; i < 100; i++)    {        var node = {            drivenByMouse: i === 0,            x: Math.random() * canvasEl.width,            y: Math.random() * canvasEl.height,            vx: Math.random() * 1 - 0.5,            vy: Math.random() * 1 - 0.5,            radius: Math.random() > 0.9 ? 3 + Math.random() * 3 : 1 + Math.random() * 3        };        nodes.push(node);    }    nodes.forEach(function(e)    {        nodes.forEach(function(e2)        {            if (e !== e2)            {                var edge = { from: e, to: e2 };                addEdge(edge);            }        });    });}

接着我们需要构建节点与节点之间的边,但由于双重循环会导致重复添加边的问题,我们引入了一个 addEdge 函数来避免重复:

function addEdge(edge){    var ignore = false;    edges.forEach(function(e)    {        if (e.from === edge.from && e.to === edge.to)        {            ignore = true;            return;        }        if (e.to === edge.from && e.from === edge.to)        {            ignore = true;            return;        }    });    if (!ignore)    {        edges.push(edge);    }}

粒子系统核心逻辑

接下来是让粒子系统动起来的关键部分。我们定义了一个 step() 函数,它会更新每个粒子的位置:

function step(){    nodes.forEach(function(e)    {        if (e.drivenByMouse)        {            return;        }        e.x += e.vx;        e.y += e.vy;        function clamp(min, max, value)        {            if (value > max)            {                return max;            }            else if (value < min)            {                return min;            }            else            {                return value;            }        }        if (e.x <= 0 || e.x >= canvasEl.width)        {            e.vx *= -1;            e.x = clamp(0, canvasEl.width, e.x);        }        if (e.y <= 0 || e.y >= canvasEl.height)        {            e.vy *= -1;            e.y = clamp(0, canvasEl.height, e.y);        }    });    adjustNodeDrivenByMouse();    render();    window.requestAnimationFrame(step);}

我们还引入了 adjustNodeDrivenByMouse 函数来让特定的节点(下面一个星星)跟随鼠标移动:

function adjustNodeDrivenByMouse(){    nodes[0].x += (mousePos[0] - nodes[0].x) / easingFactor;    nodes[0].y += (mousePos[1] - nodes[0].y) / easingFactor;}

最终,我们需要绘制粒子和连接线:

function lengthOfEdge(edge){    return Math.sqrt(Math.pow((edge.from.x - edge.to.x), 2) + Math.pow((edge.from.y - edge.to.y), 2));}function render(){    ctx.fillStyle = backgroundColor;    ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);    edges.forEach(function(e)    {        var l = lengthOfEdge(e);        var threshold = canvasEl.width / 8;        if (l > threshold)        {            return;        }        ctx.strokeStyle = edgeColor;        ctx.lineWidth = (1.0 - l / threshold) * 2.5;        ctx.globalAlpha = 1.0 - l / threshold;        ctx.beginPath();        ctx.moveTo(e.from.x, e.from.y);        ctx.lineTo(e.to.x, e.to.y);        ctx.stroke();    });    ctx.globalAlpha = 1.0;    nodes.forEach(function(e)    {        if (e.drivenByMouse)        {            return;        }        ctx.fillStyle = nodeColor;        ctx.beginPath();        ctx.arc(e.x, e.y, e.radius, 0, 2 * Math.PI);        ctx.fill();    });}

总结

通过以上代码,我们成功地将粒子系统网格效果实现了。这个系统包括了粒子的运动逻辑、边缘碰撞检测以及鼠标跟随功能。通过合理设置参数和优化绘图效果,我们能够创造出一种非常酷炫的网格动态效果。如果你对粒子系统感兴趣,这篇文章提供了一种很好的编程思路和实践经验。

上一篇:从 B 站的秋季主题中学习 “图层组合动画”
下一篇:入门 RESTful

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2025年04月26日 00时21分44秒