最近幾年,辨識(shí)度最高的虛擬圖案之一也許就是上面你所看到的這種動(dòng)畫形式了;我不確定這種動(dòng)畫形式是否有學(xué)名,這里我姑且稱之為 動(dòng)態(tài)粒子網(wǎng)格動(dòng)畫(dynamic point mesh animation) 。本例中的動(dòng)畫是基于這種動(dòng)畫形式的變種,靈感來自于Daniel Mayovskiy 之前的作品。
通常,這類動(dòng)畫被放置于其他內(nèi)容之后,所以在該例中,將 的大小設(shè)置為與視口相同的大?。?/p>
樣式如下:
body {
background: #222;
margin: 0rem;
min-height: 100vh;
}
#canvas {
position: absolute;
display: block;
top: 0;
left: 0;
z-index: -1;
}
這段代碼的Codepen版本 在動(dòng)畫的頂部放置了一些文本,并為文本添加了些許結(jié)構(gòu)和樣式。
為了確保 能夠鋪滿視口,頁面末尾的第一部分 JavaScript 代碼是一個(gè)縮放函數(shù):
let resizeReset = function() {
w = canvasBody.width = window.innerWidth;
h = canvasBody.height = window.innerHeight;
}
創(chuàng)建點(diǎn)集
opts 是一個(gè) 對(duì)象 ,包含一系列屬性,默認(rèn)屬性如下:
const opts = {
particleColor: "rgb(200,200,200)",
lineColor: "rgb(200,200,200)",
particleAmount: 40,
defaultSpeed: 1,
variantSpeed: 1,
defaultRadius: 2,
variantRadius: 2,
linkRadius: 200,
}
變量 variantSpeed 和 variantRadiut 用來增加點(diǎn)的尺寸大小和移動(dòng)速度的 隨機(jī)性 , linkRadius 表示點(diǎn)與點(diǎn)要連成線必須要靠近的距離范圍。
元素可以被縮放,這樣就會(huì)導(dǎo)致粒子會(huì)碰觸到瀏覽器窗口的邊緣。腳本一加載, resizeReset() 函數(shù)就會(huì)被調(diào)用,但是需要延遲或“去抖”,以便在頁面其余部分的操作期間不會(huì)減慢腳本的運(yùn)行:
let delay = 200, tid;
window.addEventListener("resize", function(){
deBouncer();
});
let deBouncer = function() {
clearTimeout(tid);
tid = setTimeout(function() {
resizeReset();
}, delay);
};
生成每個(gè)點(diǎn)(each of the dots)的 粒子 對(duì)象是一個(gè)比較龐大的函數(shù):
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();
};
};
在腳本的上下文中, this 指代的是每個(gè)創(chuàng)建出來的 粒子 :
每個(gè)粒子的初始速度和角度是隨機(jī)生成的,粒子的顏色通過相關(guān)的設(shè)置選項(xiàng)來確定。
this.vector 用來存儲(chǔ)粒子的 移動(dòng)方向 :如果 this.vector.x 為 1 ,則粒子向右 運(yùn)動(dòng);如果是 -1 ,則粒子向 左 移動(dòng)。同樣,如果 this.vector.y 為 負(fù),則粒子向 上 移動(dòng),如果為 正 ,則粒子向 下 移動(dòng)。
this.update 用來更新每個(gè)粒子 下一個(gè)位置 的坐標(biāo)。首先,進(jìn)行邊緣檢測(cè);如果粒子的移動(dòng)超出了canvas的尺寸,則將方向向量乘以 -1 產(chǎn)生 反向 的運(yùn)動(dòng)方向。
窗口縮放可能會(huì)引起粒子超出邊界,如此一來 邊緣檢測(cè) 函數(shù)就捕捉不到了,所以就需要一系列的 if 語句來檢測(cè)這種情況,將粒子的位置重置為當(dāng)前canvas的邊界。
最后一步,將這些點(diǎn)繪制到畫布上。
我們需要進(jìn)行下面的操作來使粒子運(yùn)動(dòng)起來:
function setup(){