1
0
Fork 0
moritzruth.de/assets/js/background-canvas.js
2020-01-12 20:18:56 +01:00

151 lines
3.6 KiB
JavaScript

/* eslint-disable no-extra-parens */
const PROBABILITY = 1 / 10000;
const MIN_RADIUS = 1;
const MAX_RADIUS = 4;
const FADE_DURATION = 5000;
const FADE_PROBABILITY = 1 / 100;
const THREESIXTY_DEGREES = Math.PI / 180 * 360;
const COLORS = [
"0,0,0",
"0,255,100",
"0,100,255"
];
function getStyleString (color, alpha) {
return `rgba(${color},${alpha})`;
}
function randomBetween (min, max) {
return (Math.random() * (max - min)) + min;
}
export class BackgroundCanvas {
constructor (element) {
this._element = element;
this._ctx = element.getContext("2d");
this._destroyed = false;
this._animationFrameRequestID = null;
this._points = [];
this._transformation = [0, 0];
this.width = null;
this.height = null;
this._isTouch = false;
this._windowListeners = {
touchstart: () => {
this._isTouch = true;
},
resize: () => {
this._points = [];
this._init();
},
mousemove: event => {
if (this._isTouch) return;
const { clientX: x, clientY: y } = event;
this._transformation = [(this.width / 2) - x, (this.height / 2) - y];
}
};
Object.entries(this._windowListeners)
.forEach(([event, listener]) => window.addEventListener(event, listener, { passive: true }));
this._init();
this._loop();
}
destroy() {
if (this._destroyed) {
throw new Error("Instance is already destroyed.");
}
this._destroyed = true;
this._element = null;
this._ctx = null;
Object.entries(this._windowListeners)
.forEach(([event, listener]) => window.removeEventListener(event, listener));
if (this._animationFrameRequestID !== null) {
cancelAnimationFrame(this._animationFrameRequestID);
}
}
get destroyed() {
return this._destroyed;
}
_init() {
this._element.width = window.innerWidth;
this._element.height = window.innerHeight;
this.width = this._element.clientWidth;
this.height = this._element.clientHeight;
for (let x = 0; x < this.width; x += 1) {
for (let y = 0; y < this.height; y += 1) {
if (Math.random() < PROBABILITY) {
const radius = randomBetween(MIN_RADIUS, MAX_RADIUS);
const z = randomBetween(-0.2, 0.2);
const color = Math.round(Math.random() * (COLORS.length - 1));
this._points.push([x, y, z, radius, color]);
}
}
}
console.log("Count of points:", this._points.length);
}
_loop() {
this._animationFrameRequestID = null;
this._draw();
if (!this._destroyed) {
requestAnimationFrame(() => this._loop());
}
}
_draw() {
// eslint-disable-next-line unicorn/prevent-abbreviations
const ctx = this._ctx;
const [transformX, transformY] = this._transformation;
ctx.clearRect(0, 0, this.width, this.height);
let first = true;
for (const point of this._points) {
const [x, y, z, radius, color] = point;
let [,,,,, fadeStartTime] = point;
let fadeProgress = 0;
if (fadeStartTime === undefined) {
if (Math.random() < FADE_PROBABILITY) {
fadeStartTime = Date.now();
point[5] = fadeStartTime;
}
} else {
fadeProgress = (Date.now() - fadeStartTime) / FADE_DURATION;
}
ctx.fillStyle = getStyleString(COLORS[color], fadeProgress);
ctx.beginPath();
ctx.arc(x + (transformX * z), y + (transformY * z), radius, 0, THREESIXTY_DEGREES);
ctx.fill();
point[0] = x + 0.5;
if (point[0] > this.width) {
point[0] = 0;
}
if (first) {
first = false;
}
}
}
}