Perhaps one of the most recognizable visual motifs of the last several years is the animation style you should see above; I’m not sure it has a name, but I call it a dynamic point mesh animation. This is my variation on the style, derived from previous work by Daniel Mayovskiy.
过去几年中,最知名的视觉主题之一可能是您应该在上面看到的动画样式; 我不确定它的名称,但我称其为动态点网格动画 。 这是我对风格的变化,它是由Daniel Mayovskiy的先前作品衍生而来的。
Frequently such animations are layered behind other content, so it makes sense to size this example to the same size as the viewport:
通常,此类动画会位于其他内容的后面,因此可以将本示例的大小调整为与视口相同的大小:
<canvas id="canvas"></canvas>
The CSS:
CSS:
body {
background: #222;
margin: 0rem;
min-height: 100vh;
}
#canvas {
position: absolute;
display: block;
top: 0;
left: 0;
z-index: -1;
}
The Codepen version of this code has a little more detail, including the CSS and markup for placing the text on top of the animation.
To ensure that the <canvas>
is always the full height and width of the viewport, the first part of the JavaScript at the end of the page is a resize function:
为了确保<canvas>
始终是视口的完整高度和宽度,页面结尾处的JavaScript的第一部分是一个调整大小的函数:
let resizeReset = function() {
w = canvasBody.width = window.innerWidth;
h = canvasBody.height = window.innerHeight;
}
创建点 (Creating the Dots)
opts
is an object that contains a series of properties that act as default values for the script:
opts
是一个对象 ,其中包含一系列用作脚本默认值的属性:
const opts = {
particleColor: "rgb(200,200,200)",
lineColor: "rgb(200,200,200)",
particleAmount: 40,
defaultSpeed: 1,
variantSpeed: 1,
defaultRadius: 2,
variantRadius: 2,
linkRadius: 200,
}
The variants for speed and radius are used to add randomness to the size and movement of the mesh points; linkRadius
is how close the points must come before they are joined with lines.
速度和半径的变体用于增加网格点的大小和运动的随机性 。 linkRadius
是点与线连接之前必须达到的接近程度。
The <canvas>
element must be resized so that the particles always meet the edge of the browser window. The resizeReset()
function is called once on script load, but needs to be throttled or “debounced” so that it doesn’t slow down the script during operation of the rest of the page:
必须调整<canvas>
元素的大小,以使粒子始终与浏览器窗口的边缘相交。 resizeReset()
函数在脚本加载时被调用一次,但需要进行限制或“反跳”处理,以使其在页面其余部分的操作期间不会降低脚本的运行速度:
let delay = 200, tid;
window.addEventListener("resize", function(){
deBouncer();
});
let deBouncer = function() {
clearTimeout(tid);
tid = setTimeout(function() {
resizeReset();
}, delay);
};
The Particle
object is a fairly large function that generates each of the “dots”:
Particle
对象是一个相当大的函数,它会生成每个“点”:
Particle = function(xPos, yPos){
this.x = Math.random() * w;
this.y = Math.random() * h;
this.speed = opts.defaultSpeed + Math.random() * opts.variantSpeed;
this.directionAngle = Math.floor(Math.random() * 360);
this.color = opts.particleColor;
this.radius = opts.defaultRadius + Math.random() * opts. variantRadius;
this.vector = {
x: Math.cos(this.directionAngle) * this.speed,
y: Math.sin(this.directionAngle) * this.speed
};
this.update = function(){
this.border();
this.x += this.vector.x;
this.y += this.vector.y;
};
this.border = function(){
if (this.x >= w || this.x <= 0) {
this.vector.x *= -1;
}
if (this.y >= h || this.y <= 0) {
this.vector.y *= -1;
}
if (this.x > w) this.x = w;
if (this.y > h) this.y = h;
if (this.x < 0) this.x = 0;
if (this.y < 0) this.y = 0;
};
this.draw = function(){
drawArea.beginPath();
drawArea.arc(this.x, this.y, this.radius, 0, Math.PI*2);
drawArea.closePath();
drawArea.fillStyle = this.color;
drawArea.fill();
};
};
In the context of the script, this
refers to each particle, as it is created:
在脚本的上下文中, this
是指创建时每个粒子 :
- the initial position, speed and angle of each particle are decided randomly; the particle’s color is determined from the associated option setting. 每个粒子的初始位置,速度和角度是随机确定的; 粒子的颜色由相关的选项设置确定。
this.vector
stores the direction of the particle: ifthis.vector.x
is1
, it’s moving to the right; if-1
, it’s moving to the left. Similarly, ifthis.vector.y
is negative, it’s moving up, if positive, it’s moving down.this.vector
存储粒子的方向 :如果this.vector.x
为1
,则向右移动; 如果为-1
,则向左移动。 同样,如果this.vector.y
为负数 ,则向上移动;如果为正数 ,则向下移动。this.update
calculates the next coordinates for each particle. First, it checks if the particle is touching the border; if the particle goes past the dimensions of the canvas, it’s vector is changed, multiplied by-1
to produce the opposite directionthis.update
计算每个粒子的下一个坐标。 首先,它检查粒子是否接触边界。 如果粒子超出画布的尺寸,则其向量将更改,乘以-1
即可产生相反的方向a window resize may leave a particle further across the perimeter than the
border
function will capture, so a series ofif
statements checks if that’s the case, resetting the position of the particle to the current limits of the canvas.调整窗口大小可能会使粒子在边界上保留的距离超出
border
函数捕获的border
,因此一系列的if
语句检查是否存在这种情况,将粒子的位置重置为画布的当前限制。- finally, the dots are drawn in place. 最后,将点绘制到位。
To start it all moving, we need the following:
要开始这一切,我们需要以下内容:
function setup(){
particles = [];
for (let i = 0; i < opts.particleAmount; i++){
particles.push( new Particle() );
}
window.requestAnimationFrame(loop);
}
The setup
function creates a particles
array, and fills it with a series of particle elements before calling the loop
function, using requestionAnimationFrame
.
setup
函数创建一个particles
数组,并使用requestionAnimationFrame
在调用loop
函数之前用一系列粒子元素填充它。
That loop
function looks like this:
该loop
函数如下所示:
function loop(){
window.requestAnimationFrame(loop);
drawArea.clearRect(0,0,w,h);
for (let i = 0; i < particles.length; i++){
particles[i].update();
particles[i].draw();
}
}
The loop
function clears the canvas area, updates each particle position, and draws it; the constant refreshing with requestAnimationFrame()
creates the impression of animation.
loop
功能清除画布区域,更新每个粒子位置并绘制; 使用requestAnimationFrame()
不断刷新可创建动画效果。
Everything is started by calling the setup()
function, after setting a few other constants and variables and initializing the resizeReset
function:
在设置了其他一些常量和变量并初始化resizeReset
函数之后,通过调用setup()
函数开始一切操作:
const canvasBody = document.getElementById("canvas"),
drawArea = canvasBody.getContext("2d");
let delay = 200, tid;
resizeReset();
setup();
At this point, the animation will look like a series of dots moving around the canvas:
此时,动画将看起来像一系列围绕画布移动的点:
See the Pen Dynamic Point Animation with HTML5 Canvas by Dudley Storey (@dudleystorey) on CodePen.
见笔动态点动画与HTML5画布由达德利·斯托里( @dudleystorey )上CodePen 。
To create the mesh, we must add a little more code.
要创建网格,我们必须添加一些代码。
创建线 (Creating the Lines)
To draw the lines, the loop()
function is added to, becoming:
为了画线,将loop()
函数添加到其中,成为:
function loop(){
window.requestAnimationFrame(loop);
drawArea.clearRect(0,0,w,h);
for (let i = 0; i < particles.length; i++){
particles[i].update();
particles[i].draw();
}
for (let i = 0; i < particles.length; i++){
linkPoints(particles[i], particles);
}
}
The linkPoints
function is called for every particle. That function also uses a piece of code called checkDistance
:
每个粒子都会调用linkPoints
函数。 该函数还使用了一段称为checkDistance
的代码:
let checkDistance = function(x1, y1, x2, y2){
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
};
The checkDistance
function determines the distance between each point; if that distance is less than linkDistance
, the calculated opacity
of the line will be greater than 0, and it will be drawn between the matching points.
checkDistance
函数确定每个点之间的距离; 如果该距离小于linkDistance
,则计算出的线的opacity
将大于0,并将在匹配点之间绘制。
Before we get there, the rgb
color is broken into its components:
在到达那里之前, rgb
颜色分为以下成分:
let rgb = opts.lineColor.match(/\d+/g);
The linkPoints
function checks each point against the other particles (referred to as “hubs” in the context of the function) and draws the line at the determined level of opacity, using template literals:
linkPoints
函数将每个点与其他粒子(在该函数的上下文中称为“集线器”)进行对照,并使用模板文字以确定的不透明度级别绘制线条:
let linkPoints = function(point1, hubs){
for (let i = 0; i < hubs.length; i++) {
let distance = checkDistance(point1.x, point1.y, hubs[i].x, hubs[i].y);
let opacity = 1 - distance / opts.linkRadius;
if (opacity > 0) {
drawArea.lineWidth = 0.5;
drawArea.strokeStyle = `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${opacity})`;
drawArea.beginPath();
drawArea.moveTo(point1.x, point1.y);
drawArea.lineTo(hubs[i].x, hubs[i].y);
drawArea.closePath();
drawArea.stroke();
}
}
}
结论 (Conclusion)
The result (using aspects of ES6) is very efficient, and I encourage you to experiment with the various options in the script.
结果(使用ES6的各个方面)非常有效,我鼓励您尝试使用脚本中的各种选项。
Note that the animation can be brought to its knees by adding too many points and/or too great a link distance (which creates too many lines). It would also be nice to have the speed of the particles slow down as the viewport narrows: at small sizes, the particles appear to move faster in a more confined space.
请注意,可以通过添加太多点和/或太大链接距离(从而创建太多线)来使动画屈服。 当视口变窄时,让粒子的速度减慢也将是一件好事:在较小的尺寸下,粒子似乎在更狭窄的空间中移动得更快。
翻译自: https://thenewcode.com/1159/Create-a-Dynamic-Point-Mesh-Animation-with-HTML5-Canvas