Select Git revision
amd64.dockerfile
background_anim.js 8.18 KiB
// Constants
const FPS = 60;
const DOTS_NUM_VALUES = [100, 75];
const INITIAL_VELOCITY_MAX_VALUES = [25, 15];
const VELOCITY_MAX_VALUES = [40, 30];
const RADIUS_MIN_VALUES = [2, 0.5];
const RADIUS_MAX_VALUES = [6, 2];
const LINE_DISTANCE_MAX_VALUES = [120, 55];
const LINE_WIDTH_VALUES = [0.2, 0.08];
const DOT_PUSH_DISTANCE_VALUES = [25, 20];
const DOT_FILL_COLOUR = "#666";
const DOT_OUTLINE_COLOUR = "#444";
const LINE_COLOUR = "#777";
const VELOCITY_SLOWDOWN_MULTIPLIER = 0.998;
const VELOCITY_PUSH_MULTIPLIER = 1.8;
// ----
// Sort-of constants (change with window size)
let DOTS_NUM = 100;
let INITIAL_VELOCITY_MAX = 25;
let VELOCITY_MAX = 40;
let RADIUS_MIN = 1;
let RADIUS_MAX = 4;
let LINE_DISTANCE_MAX = 150;
let LINE_WIDTH = 0.2;
let DOT_PUSH_DISTANCE = 25;
class Dot {
constructor(x, y, radius, vx, vy) {
this._x = x;
this._y = y;
this._radius = radius;
this._vx = vx;
this._initial_vx = vx;
this._vy = vy;
this._initial_vy = vy;
}
get x() {
return this._x;
}
set x(value) {
this._x = value;
}
get y() {
return this._y;
}
set y(value) {
this._y = value;
}
get radius() {
return this._radius;
}
set radius(value) {
this._radius = value;
}
get vx() {
return this._vx;
}
set vx(value) {
this._vx = value;
}
get initial_vx() {
return this._initial_vx
}
get vy() {
return this._vy;
}
set vy(value) {
this._vy = value;
}
get initial_vy() {
return this._initial_vy;
}
}
const randInt = (min, max) => {
return Math.floor(Math.random() * (max - min)) + min;
};
const distanceBetween = (ax, ay, bx, by) => {
// Calculate the distance between two points using pythagoras
// c = sqrt(a^2 + b^2)
const xSquared = (bx - ax) ** 2;
const ySquared = (by - ay) ** 2;
return Math.sqrt(xSquared + ySquared);
};
const clamp = (number, min, max) => {
return Math.max(min, Math.min(number, max));
};
const canvas = document.getElementById("background");
const ctx = canvas.getContext("2d");
const setDisplayParameters = () => {
// Set canvas width and height
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let index = 0;
if (canvas.width < 768) {
// Set defaults for lower screen resolutions
index = 1;
}
// Set properties
DOTS_NUM = DOTS_NUM_VALUES[index];
INITIAL_VELOCITY_MAX = INITIAL_VELOCITY_MAX_VALUES[index];
VELOCITY_MAX = VELOCITY_MAX_VALUES[index];
RADIUS_MIN = RADIUS_MIN_VALUES[index];
RADIUS_MAX = RADIUS_MAX_VALUES[index];
LINE_DISTANCE_MAX = LINE_DISTANCE_MAX_VALUES[index];
LINE_WIDTH = LINE_WIDTH_VALUES[index];
DOT_PUSH_DISTANCE = DOT_PUSH_DISTANCE_VALUES[index];
};
setDisplayParameters();
let dots = [];
let mousePos = [0, 0];
for (let i = 0; i < DOTS_NUM; i++) {
// Append new dot to the array with a random position, radius, and velocity
dots.push(new Dot(
Math.random() * canvas.width, // position x
Math.random() * canvas.height, // position y
randInt(RADIUS_MIN, RADIUS_MAX), // radius
randInt(-INITIAL_VELOCITY_MAX, INITIAL_VELOCITY_MAX), // velocity x
randInt(-INITIAL_VELOCITY_MAX, INITIAL_VELOCITY_MAX) // velocity y
));
}
const drawDots = () => {
for (let dot of dots) {
// Begin the path
ctx.beginPath();
// Create an arc
ctx.arc(dot.x, dot.y, dot.radius, 0, 2 * Math.PI, false);
// Fill in the arc
ctx.fillStyle = DOT_FILL_COLOUR;
ctx.fill();
// Add an outline
ctx.fillStyle = DOT_OUTLINE_COLOUR;
ctx.stroke();
// End the path
ctx.closePath();
}
};
const drawConnectingLines = () => {
// Draw connecting lines between the dots
ctx.beginPath();
for(let dot of dots) {
// Go to the dot
ctx.moveTo(dot.x, dot.y);
// Loop through all the dots
for (let dot2 of dots) {
if(dot2 !== dot &&
distanceBetween(dot.x, dot.y, dot2.x, dot2.y) < LINE_DISTANCE_MAX
) {
// Draw a line between the dots if the distance is below a threshold
ctx.lineTo(dot2.x, dot2.y);
}
}
}
// Draw the lines
ctx.lineWidth = LINE_WIDTH;
ctx.strokeStyle = LINE_COLOUR;
ctx.stroke();
// End the path
ctx.closePath();
};
const draw = () => {
// Clear the screen
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Set the composite operation
ctx.globalCompositeOperation = "lighten";
// And now, for the dots
// Done after lines so lines are below dots
drawDots();
// Draw connecting lines
drawConnectingLines();
};
const physics = () => {
for (let i = 0; i < dots.length; i++) {
let dot = dots[i]; // We have to do it this way since we want the dots to update
// Set positions of dots based on velocity
dot.x += dot.vx / FPS;
dot.y += dot.vy / FPS;
// Clamp velocities to maximum
dot.vx = clamp(dot.vx, -VELOCITY_MAX, VELOCITY_MAX);
dot.vy = clamp(dot.vy, -VELOCITY_MAX, VELOCITY_MAX);
// Slow down velocity
dot.vx *= VELOCITY_SLOWDOWN_MULTIPLIER;
dot.xy *= VELOCITY_SLOWDOWN_MULTIPLIER;
// Stop velocity from going below initial velocity
if(dot.vx < dot.initial_vx) {
dot.vx = dot.initial_vx;
}
if(dot.vy < dot.initial_vy) {
dot.vy = dot.initial_vy;
}
// Invert velocities if off-screen and not already inverted
if ((dot.x < 0 && dot.vx < 0) || (dot.x > canvas.width && dot.vx > 0)) {
dot.vx = -dot.vx;
dot.vx *= VELOCITY_PUSH_MULTIPLIER;
}
if ((dot.y < 0 && dot.vy < 0) || (dot.y > canvas.height && dot.vy > 0)) {
dot.vy = -dot.vy;
dot.vy *= VELOCITY_PUSH_MULTIPLIER;
}
// Make dots velocities react to mouse movement (deflected by mouse)
if (distanceBetween(dot.x, dot.y, mousePos[0], mousePos[1]) < DOT_PUSH_DISTANCE) {
let ax = mousePos[0];
let ay = mousePos[1];
let bx = dot.x;
let by = dot.y;
let r = DOT_PUSH_DISTANCE;
let cx = ax + (r * ((bx - ax) / Math.sqrt((bx - ax)**2 + (by - ay)**2)));
let cy = ay + (r * ((by - ay) / Math.sqrt((bx - ax)**2 + (by - ay)**2)));
dot.vx = -dot.vx;
dot.vy = -dot.vy;
dot.vx *= VELOCITY_PUSH_MULTIPLIER;
dot.vy *= VELOCITY_PUSH_MULTIPLIER;
dot.x = cx;
dot.y = cy;
}
}
};
let zero = 0;
const tick = (timeStamp) => {
// Calculate time delta
// Some browsers use animationframe timers of > 60FPS
let deltaTime = timeStamp - zero;
// Update if enough time has passed
if(deltaTime >= (1 / FPS) * 1000) {
zero = timeStamp;
draw();
physics();
}
// Request the next animation frame
requestAnimationFrame(tick);
};
window.addEventListener("mousemove", (event) => {
// Scale clientX and clientY in case the screen size has changed
mousePos[0] = (canvas.width / window.innerWidth) * event.clientX;
mousePos[1] = (canvas.height / window.innerHeight) * event.clientY;
});
window.addEventListener("resize", () => {
// Width before resize
let oldWidth = canvas.width;
// Height before resize
let oldHeight = canvas.height;
// Set canvas size and display parameters
setDisplayParameters();
// Calculate old to new width ratio
let widthRatio = canvas.width / oldWidth;
// Calculate old to new height ratio
let heightRatio = canvas.height / oldHeight;
// Loop through all dots
for (let i = 0; i < dots.length; i++) {
let dot = dots[i];
// Reset radius of dots
dot.radius = randInt(RADIUS_MIN, RADIUS_MAX);
// Reset dots positions outside new window if the window has gotten smaller
dot.x = dot.x * widthRatio;
dot.y = dot.y * heightRatio;
}
});
document.addEventListener("DOMContentLoaded", () => {
// Start background animation tick
requestAnimationFrame(tick);
});