mirror of
https://github.com/Lissy93/web-check.git
synced 2025-04-12 03:28:22 +02:00
🦄 Adds a fancy animated background for homepage
This commit is contained in:
parent
11506277a7
commit
e351bc34bb
347
src/components/misc/FancyBackground.tsx
Normal file
347
src/components/misc/FancyBackground.tsx
Normal file
@ -0,0 +1,347 @@
|
||||
|
||||
|
||||
const FancyBackground = (): JSX.Element => {
|
||||
|
||||
const makeAbsolute = (elem: HTMLElement) => {
|
||||
elem.style.position = 'absolute';
|
||||
elem.style.top = '0';
|
||||
elem.style.left = '0';
|
||||
};
|
||||
|
||||
const maxBy = (array: any) => {
|
||||
const chaos = 30;
|
||||
const iteratee = (e: any) => e.field + chaos * Math.random();
|
||||
let result;
|
||||
if (array == null) { return result; }
|
||||
let computed;
|
||||
for (const value of array) {
|
||||
const current = iteratee(value);
|
||||
if (current != null && (computed === undefined ? current : current > computed)) {
|
||||
computed = current;
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const App: any = {};
|
||||
|
||||
App.setup = function () {
|
||||
|
||||
this.lifespan = 1000;
|
||||
this.popPerBirth = 1;
|
||||
this.maxPop = 300;
|
||||
this.birthFreq = 2;
|
||||
this.bgColor = '#141d2b';
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
canvas.style.opacity = '0.5';
|
||||
makeAbsolute(canvas);
|
||||
this.canvas = canvas;
|
||||
const container = document.getElementById('fancy-background');
|
||||
if (container) {
|
||||
container.style.color = this.bgColor;
|
||||
makeAbsolute(container);
|
||||
container.appendChild(canvas);
|
||||
}
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.width = this.canvas.width;
|
||||
this.height = this.canvas.height;
|
||||
this.dataToImageRatio = 1;
|
||||
this.ctx.imageSmoothingEnabled = false;
|
||||
this.ctx.webkitImageSmoothingEnabled = false;
|
||||
this.ctx.msImageSmoothingEnabled = false;
|
||||
this.xC = this.width / 2;
|
||||
this.yC = this.height / 2;
|
||||
|
||||
this.stepCount = 0;
|
||||
this.particles = [];
|
||||
|
||||
|
||||
// Build grid
|
||||
this.gridSize = 8; // Motion coords
|
||||
this.gridSteps = Math.floor(1000 / this.gridSize);
|
||||
this.grid = [];
|
||||
var i = 0;
|
||||
for (var xx = -500; xx < 500; xx += this.gridSize) {
|
||||
for (var yy = -500; yy < 500; yy += this.gridSize) {
|
||||
// Radial field, triangular function of r with max around r0
|
||||
var r = Math.sqrt(xx * xx + yy * yy),
|
||||
r0 = 100,
|
||||
field;
|
||||
|
||||
if (r < r0) field = (255 / r0) * r;
|
||||
else if (r > r0) field = 255 - Math.min(255, (r - r0) / 2);
|
||||
|
||||
this.grid.push({
|
||||
x: xx,
|
||||
y: yy,
|
||||
busyAge: 0,
|
||||
spotIndex: i,
|
||||
isEdge:
|
||||
xx === -500
|
||||
? 'left'
|
||||
: xx === -500 + this.gridSize * (this.gridSteps - 1)
|
||||
? 'right'
|
||||
: yy === -500
|
||||
? 'top'
|
||||
: yy === -500 + this.gridSize * (this.gridSteps - 1)
|
||||
? 'bottom'
|
||||
: false,
|
||||
field: field,
|
||||
});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
this.gridMaxIndex = i;
|
||||
|
||||
// Counters for UI
|
||||
this.drawnInLastFrame = 0;
|
||||
this.deathCount = 0;
|
||||
|
||||
this.initDraw();
|
||||
};
|
||||
App.evolve = function () {
|
||||
var time1 = performance.now();
|
||||
|
||||
this.stepCount++;
|
||||
|
||||
// Increment all grid ages
|
||||
this.grid.forEach(function (e: any) {
|
||||
if (e.busyAge > 0) e.busyAge++;
|
||||
});
|
||||
|
||||
if (
|
||||
this.stepCount % this.birthFreq === 0 &&
|
||||
this.particles.length + this.popPerBirth < this.maxPop
|
||||
) {
|
||||
this.birth();
|
||||
}
|
||||
App.move();
|
||||
App.draw();
|
||||
|
||||
var time2 = performance.now();
|
||||
|
||||
// Update UI
|
||||
document.getElementsByClassName('dead')[0].textContent = this.deathCount;
|
||||
document.getElementsByClassName('alive')[0].textContent =
|
||||
this.particles.length;
|
||||
document.getElementsByClassName('fps')[0].textContent = Math.floor(
|
||||
1000 / (time2 - time1)
|
||||
).toString();
|
||||
document.getElementsByClassName('drawn')[0].textContent =
|
||||
this.drawnInLastFrame;
|
||||
};
|
||||
App.birth = function () {
|
||||
var x, y;
|
||||
var gridSpotIndex = Math.floor(Math.random() * this.gridMaxIndex);
|
||||
var gridSpot = this.grid[gridSpotIndex];
|
||||
x = gridSpot.x;
|
||||
y = gridSpot.y;
|
||||
|
||||
var particle = {
|
||||
hue: 200, // + Math.floor(50*Math.random()),
|
||||
sat: 95, //30 + Math.floor(70*Math.random()),
|
||||
lum: 20 + Math.floor(40 * Math.random()),
|
||||
x: x,
|
||||
y: y,
|
||||
xLast: x,
|
||||
yLast: y,
|
||||
xSpeed: 0,
|
||||
ySpeed: 0,
|
||||
age: 0,
|
||||
ageSinceStuck: 0,
|
||||
attractor: {
|
||||
oldIndex: gridSpotIndex,
|
||||
gridSpotIndex: gridSpotIndex, // Pop at random position on grid
|
||||
},
|
||||
name: 'seed-' + Math.ceil(10000000 * Math.random()),
|
||||
};
|
||||
this.particles.push(particle);
|
||||
};
|
||||
App.kill = function (particleName: any) {
|
||||
const newArray = this.particles.filter(
|
||||
(seed: any) => seed.name !== particleName
|
||||
);
|
||||
this.particles = [...newArray];
|
||||
};
|
||||
App.move = function () {
|
||||
for (var i = 0; i < this.particles.length; i++) {
|
||||
// Get particle
|
||||
var p = this.particles[i];
|
||||
|
||||
// Save last position
|
||||
p.xLast = p.x;
|
||||
p.yLast = p.y;
|
||||
|
||||
// Attractor and corresponding grid spot
|
||||
var index = p.attractor.gridSpotIndex,
|
||||
gridSpot = this.grid[index];
|
||||
|
||||
// Maybe move attractor and with certain constraints
|
||||
if (Math.random() < 0.5) {
|
||||
// Move attractor
|
||||
if (!gridSpot.isEdge) {
|
||||
// Change particle's attractor grid spot and local move function's grid spot
|
||||
var topIndex = index - 1,
|
||||
bottomIndex = index + 1,
|
||||
leftIndex = index - this.gridSteps,
|
||||
rightIndex = index + this.gridSteps,
|
||||
topSpot = this.grid[topIndex],
|
||||
bottomSpot = this.grid[bottomIndex],
|
||||
leftSpot = this.grid[leftIndex],
|
||||
rightSpot = this.grid[rightIndex];
|
||||
|
||||
var maxFieldSpot = maxBy(
|
||||
[topSpot, bottomSpot, leftSpot, rightSpot]
|
||||
);
|
||||
|
||||
var potentialNewGridSpot = maxFieldSpot;
|
||||
if (
|
||||
potentialNewGridSpot.busyAge === 0 ||
|
||||
potentialNewGridSpot.busyAge > 15
|
||||
) {
|
||||
p.ageSinceStuck = 0;
|
||||
p.attractor.oldIndex = index;
|
||||
p.attractor.gridSpotIndex = potentialNewGridSpot.spotIndex;
|
||||
gridSpot = potentialNewGridSpot;
|
||||
gridSpot.busyAge = 1;
|
||||
} else p.ageSinceStuck++;
|
||||
} else p.ageSinceStuck++;
|
||||
|
||||
if (p.ageSinceStuck === 10) this.kill(p.name);
|
||||
}
|
||||
|
||||
// Spring attractor to center with viscosity
|
||||
const k = 8, visc = 0.4;
|
||||
var dx = p.x - gridSpot.x,
|
||||
dy = p.y - gridSpot.y,
|
||||
dist = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Spring
|
||||
var xAcc = -k * dx,
|
||||
yAcc = -k * dy;
|
||||
|
||||
p.xSpeed += xAcc;
|
||||
p.ySpeed += yAcc;
|
||||
|
||||
// Calm the f*ck down
|
||||
p.xSpeed *= visc;
|
||||
p.ySpeed *= visc;
|
||||
|
||||
// Store stuff in particle brain
|
||||
p.speed = Math.sqrt(p.xSpeed * p.xSpeed + p.ySpeed * p.ySpeed);
|
||||
p.dist = dist;
|
||||
|
||||
// Update position
|
||||
p.x += 0.1 * p.xSpeed;
|
||||
p.y += 0.1 * p.ySpeed;
|
||||
|
||||
// Get older
|
||||
p.age++;
|
||||
|
||||
// Kill if too old
|
||||
if (p.age > this.lifespan) {
|
||||
this.kill(p.name);
|
||||
this.deathCount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
App.initDraw = function () {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.rect(0, 0, this.width, this.height);
|
||||
this.ctx.fillStyle = this.bgColor;
|
||||
this.ctx.fill();
|
||||
this.ctx.closePath();
|
||||
};
|
||||
App.draw = function () {
|
||||
this.drawnInLastFrame = 0;
|
||||
if (!this.particles.length) return false;
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.rect(0, 0, this.width, this.height);
|
||||
this.ctx.fillStyle = this.bgColor;
|
||||
this.ctx.fill();
|
||||
this.ctx.closePath();
|
||||
|
||||
for (var i = 0; i < this.particles.length; i++) {
|
||||
var p = this.particles[i];
|
||||
|
||||
var last = this.dataXYtoCanvasXY(p.xLast, p.yLast),
|
||||
now = this.dataXYtoCanvasXY(p.x, p.y);
|
||||
var attracSpot = this.grid[p.attractor.gridSpotIndex],
|
||||
attracXY = this.dataXYtoCanvasXY(attracSpot.x, attracSpot.y);
|
||||
var oldAttracSpot = this.grid[p.attractor.oldIndex],
|
||||
oldAttracXY = this.dataXYtoCanvasXY(oldAttracSpot.x, oldAttracSpot.y);
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.strokeStyle = '#9fef00';
|
||||
this.ctx.fillStyle = '#9fef00';
|
||||
|
||||
// Particle trail
|
||||
this.ctx.moveTo(last.x, last.y);
|
||||
this.ctx.lineTo(now.x, now.y);
|
||||
|
||||
this.ctx.lineWidth = 1.5 * this.dataToImageRatio;
|
||||
this.ctx.stroke();
|
||||
this.ctx.closePath();
|
||||
|
||||
// Attractor positions
|
||||
this.ctx.beginPath();
|
||||
this.ctx.lineWidth = 1.5 * this.dataToImageRatio;
|
||||
this.ctx.moveTo(oldAttracXY.x, oldAttracXY.y);
|
||||
this.ctx.lineTo(attracXY.x, attracXY.y);
|
||||
this.ctx.arc(
|
||||
attracXY.x,
|
||||
attracXY.y,
|
||||
1.5 * this.dataToImageRatio,
|
||||
0,
|
||||
2 * Math.PI,
|
||||
false
|
||||
);
|
||||
|
||||
this.ctx.strokeStyle = '#9fef00';
|
||||
this.ctx.fillStyle = '#9fef00';
|
||||
|
||||
this.ctx.stroke();
|
||||
this.ctx.fill();
|
||||
|
||||
this.ctx.closePath();
|
||||
|
||||
// UI counter
|
||||
this.drawnInLastFrame++;
|
||||
}
|
||||
};
|
||||
App.dataXYtoCanvasXY = function (x: number, y: number) {
|
||||
var zoom = 1.6;
|
||||
var xx = this.xC + x * zoom * this.dataToImageRatio,
|
||||
yy = this.yC + y * zoom * this.dataToImageRatio;
|
||||
|
||||
return { x: xx, y: yy };
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
App.setup();
|
||||
App.draw();
|
||||
|
||||
var frame = function () {
|
||||
App.evolve();
|
||||
requestAnimationFrame(frame);
|
||||
};
|
||||
frame();
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
<div className='ui' id='fancy-background'>
|
||||
<p><span className='dead'>0</span></p>
|
||||
<p><span className='alive'>0</span></p>
|
||||
<p><span className='drawn'>0</span></p>
|
||||
<p><span className='fps'>0</span></p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FancyBackground;
|
Loading…
Reference in New Issue
Block a user