2019-04-14 05:47:13 +02:00
|
|
|
(function () {
|
2019-04-23 01:59:08 +02:00
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
/* INITIALISATION
|
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
|
2019-04-14 05:47:13 +02:00
|
|
|
const canvas = document.getElementById("main-header-background");
|
|
|
|
const context = canvas.getContext("2d");
|
|
|
|
|
|
|
|
// Get the node dimensions and enforce the canvas width and height
|
2019-04-23 01:59:08 +02:00
|
|
|
let WIDTH = canvas.clientWidth;
|
|
|
|
let HEIGHT = canvas.clientHeight;
|
2019-04-14 05:47:13 +02:00
|
|
|
|
|
|
|
canvas.setAttribute("width", WIDTH);
|
|
|
|
canvas.setAttribute("height", HEIGHT);
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
// Delay between point positions' updates
|
|
|
|
const POINT_POSITIONS_UPDATE_DELAY = 50; // ms
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
// Number of points and initial position parameters
|
|
|
|
const NB_POINTS_PER_ROW = 4 + Math.round(WIDTH / 200);
|
2019-04-14 05:47:13 +02:00
|
|
|
const NB_POINTS_PER_COL = 5;
|
|
|
|
|
|
|
|
const INIT_ROW_SPACING = HEIGHT / (NB_POINTS_PER_COL - 1);
|
|
|
|
const INIT_COL_SPACING = WIDTH / (NB_POINTS_PER_ROW - 3);
|
|
|
|
|
2019-04-16 20:25:49 +02:00
|
|
|
const X_ORIGIN = -INIT_COL_SPACING;
|
|
|
|
const Y_ORIGIN = -100;
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
const INIT_MAX_X_SHIFT = INIT_ROW_SPACING / 3;
|
|
|
|
const INIT_MAX_Y_SHIFT = INIT_COL_SPACING / 4;
|
|
|
|
|
|
|
|
// Movement of the points
|
|
|
|
const POINT_MIN_X_SPEED = 0.15;
|
|
|
|
const POINT_MAX_X_SPEED = 0.3;
|
|
|
|
|
|
|
|
const POINT_MIN_Y_SPEED = 0.15;
|
|
|
|
const POINT_MAX_Y_SPEED = 0.3;
|
|
|
|
|
2019-04-23 02:20:15 +02:00
|
|
|
const POINT_MIN_X_RADIUS = 1;
|
2019-04-23 01:59:08 +02:00
|
|
|
const POINT_MAX_X_RADIUS = 2;
|
|
|
|
|
2019-04-23 02:20:15 +02:00
|
|
|
const POINT_MIN_Y_RADIUS = 1;
|
2019-04-23 01:59:08 +02:00
|
|
|
const POINT_MAX_Y_RADIUS = 2;
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
// Canvas update halting
|
|
|
|
const NB_CANVAS_UPDATES_DELAYS_TO_RECORD = 100;
|
|
|
|
const ANIM_HALT_TIME_THRESHOLD = 100; // ms
|
2019-04-14 05:47:13 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
|
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
/* GRID OF POINTS
|
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
|
2019-04-14 05:47:13 +02:00
|
|
|
const points = [];
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
// Create all points
|
|
|
|
// Each point is defined by its position
|
|
|
|
// plus several parameters which control its movement
|
|
|
|
function createAllPoints () {
|
|
|
|
for (let row = 0; row < NB_POINTS_PER_COL; row++) {
|
|
|
|
for (let col = 0; col < NB_POINTS_PER_ROW; col++) {
|
|
|
|
points.push({
|
|
|
|
// Initial position
|
|
|
|
x: X_ORIGIN + col * INIT_COL_SPACING + ((row % 2 === 1) ? INIT_COL_SPACING : 0),
|
|
|
|
y: Y_ORIGIN + row * INIT_ROW_SPACING,
|
|
|
|
|
|
|
|
// Parameters of the movement
|
|
|
|
xDirection: (Math.random() > 0.5 ? 1 : -1),
|
|
|
|
yDirection: (Math.random() > 0.5 ? 1 : -1),
|
|
|
|
|
|
|
|
xSpeed: (Math.random() * (POINT_MAX_X_SPEED - POINT_MIN_X_SPEED)) + POINT_MIN_X_SPEED,
|
|
|
|
ySpeed: (Math.random() * (POINT_MAX_Y_SPEED - POINT_MIN_Y_SPEED)) + POINT_MIN_Y_SPEED,
|
|
|
|
|
|
|
|
xRadius: (Math.random() * (POINT_MAX_X_RADIUS - POINT_MIN_X_RADIUS)) + POINT_MIN_X_RADIUS,
|
|
|
|
yRadius: (Math.random() * (POINT_MAX_Y_RADIUS - POINT_MIN_Y_RADIUS)) + POINT_MIN_Y_RADIUS
|
|
|
|
});
|
|
|
|
}
|
2019-04-14 05:47:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
// Move each point a little bit
|
|
|
|
// This will make the triangular structure look more random
|
|
|
|
for (let point of points) {
|
|
|
|
point.x += (2 * Math.random() * INIT_MAX_X_SHIFT) - INIT_MAX_X_SHIFT;
|
|
|
|
point.y += (2 * Math.random() * INIT_MAX_Y_SHIFT) - INIT_MAX_Y_SHIFT;
|
|
|
|
}
|
2019-04-14 05:47:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
createAllPoints();
|
|
|
|
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
|
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
/* TRIANGLE DRAWING
|
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
|
|
|
|
// Draw a line between two points
|
2019-04-14 05:47:13 +02:00
|
|
|
function drawLineBetweenPoints (from, to) {
|
|
|
|
context.beginPath();
|
|
|
|
context.moveTo(from.x, from.y);
|
|
|
|
context.lineTo(to.x, to.y);
|
|
|
|
context.stroke();
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
|
2019-04-23 02:20:15 +02:00
|
|
|
// Draw white triangles with transparent contours
|
|
|
|
function drawTriangles () {
|
|
|
|
// Style of the drawing
|
|
|
|
context.fillStyle = "#FFFFFF";
|
2019-04-23 01:59:08 +02:00
|
|
|
context.lineWidth = 3;
|
|
|
|
|
2019-04-23 02:20:15 +02:00
|
|
|
// Fill the canvas with a white background...
|
|
|
|
context.globalCompositeOperation = "source-over";
|
|
|
|
context.fillRect(0, 0, WIDTH, HEIGHT);
|
|
|
|
|
|
|
|
// ...and erase the contours of the triangles
|
|
|
|
context.globalCompositeOperation = "destination-out";
|
|
|
|
|
2019-04-14 05:47:13 +02:00
|
|
|
for (let row = 0; row < NB_POINTS_PER_COL; row++) {
|
|
|
|
for (let col = 0; col < NB_POINTS_PER_ROW; col++) {
|
|
|
|
// Nothing to do with the last point of each row
|
|
|
|
if (col == NB_POINTS_PER_ROW - 1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the current index and point
|
|
|
|
const index = row * NB_POINTS_PER_ROW + col;
|
|
|
|
const point = points[index];
|
|
|
|
|
|
|
|
// When applicable, draw lines to
|
|
|
|
// - the next point on the same row
|
|
|
|
// - the aligned point on the previous row
|
|
|
|
// - the aligned point on the next row
|
|
|
|
if (! (row % 2 === 1 && col === NB_POINTS_PER_ROW - 2))
|
|
|
|
drawLineBetweenPoints(point, points[index + 1]);
|
|
|
|
|
|
|
|
if (row > 0)
|
|
|
|
drawLineBetweenPoints(point, points[index - NB_POINTS_PER_ROW]);
|
|
|
|
if (row < NB_POINTS_PER_COL - 1)
|
|
|
|
drawLineBetweenPoints(point, points[index + NB_POINTS_PER_ROW]);
|
|
|
|
|
|
|
|
// If the row number is odd, when applicable, also draw lines to
|
|
|
|
// - the next point on the previous row
|
|
|
|
// - the next point on the next row
|
|
|
|
if (row % 2 === 1) {
|
|
|
|
if (row > 0)
|
|
|
|
drawLineBetweenPoints(point, points[index - NB_POINTS_PER_ROW + 1]);
|
|
|
|
if (row < NB_POINTS_PER_COL - 1)
|
|
|
|
drawLineBetweenPoints(point, points[index + NB_POINTS_PER_ROW + 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
/* POINT POSITIONS UPDATE
|
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
|
|
|
|
// N° of the current position update iteration
|
|
|
|
let positionUpdateIter = 0;
|
|
|
|
|
|
|
|
|
2019-04-14 05:47:13 +02:00
|
|
|
function updatePointPositions () {
|
2019-04-23 01:59:08 +02:00
|
|
|
const nbPoints = points.length;
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
for (let i = 0; i < nbPoints; i++) {
|
|
|
|
const point = points[i];
|
|
|
|
|
|
|
|
point.x += point.xDirection * Math.cos(positionUpdateIter * point.xSpeed) * point.xRadius;
|
|
|
|
point.y += point.yDirection * Math.sin(positionUpdateIter * point.ySpeed) * point.yRadius;
|
2019-04-14 05:47:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
positionUpdateIter++;
|
|
|
|
}
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
window.setInterval(updatePointPositions, POINT_POSITIONS_UPDATE_DELAY);
|
2019-04-14 05:47:13 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
|
|
|
|
/*--------------------------------------------------------*/
|
|
|
|
/* CANVAS UPDATE
|
|
|
|
/*--------------------------------------------------------*/
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
// Array of times between canvas updates
|
|
|
|
// It is used to compute whether the drawing process should be halted
|
|
|
|
// (if the device is too slow for the animation to look fluid)
|
|
|
|
const canvasUpdateDelays = new Array(NB_CANVAS_UPDATES_DELAYS_TO_RECORD);
|
|
|
|
canvasUpdateDelays.fill(0);
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
// Last recorded time (it is set for the first time a bit later)
|
|
|
|
let lastRecordedTime = 0;
|
|
|
|
|
|
|
|
// Flag indicating whether the canvas update has been halted or not
|
|
|
|
let animationHasBeenHalted = false;
|
|
|
|
|
|
|
|
|
|
|
|
function resetHaltingMechanism () {
|
|
|
|
canvasUpdateDelays.fill(0);
|
|
|
|
lastRecordedTime = performance.now();
|
2019-04-14 05:47:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
function recordTimeSinceLastCanvasUpdate (time) {
|
|
|
|
canvasUpdateDelays.shift();
|
|
|
|
canvasUpdateDelays.push(time - lastRecordedTime);
|
|
|
|
|
|
|
|
lastRecordedTime = time;
|
|
|
|
}
|
2019-04-14 05:47:13 +02:00
|
|
|
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
function animationShouldBeHalted () {
|
|
|
|
let sumOfTimes = canvasUpdateDelays.reduce((sumOfTimes, time) => {
|
|
|
|
return sumOfTimes + time;
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
// console.log(sumOfTimes / NB_CANVAS_UPDATES_DELAYS_TO_RECORD);
|
|
|
|
return (sumOfTimes / NB_CANVAS_UPDATES_DELAYS_TO_RECORD) > ANIM_HALT_TIME_THRESHOLD;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateCanvas (currentTime) {
|
|
|
|
// Record the time since last update, and halt the animation if required
|
|
|
|
recordTimeSinceLastCanvasUpdate(currentTime);
|
|
|
|
if (animationShouldBeHalted()) {
|
|
|
|
animationHasBeenHalted = true;
|
|
|
|
return;
|
|
|
|
}
|
2019-04-14 05:47:13 +02:00
|
|
|
|
2019-04-23 02:20:15 +02:00
|
|
|
// Repeat the drawing process
|
|
|
|
drawTriangles();
|
2019-04-14 05:47:13 +02:00
|
|
|
|
|
|
|
window.requestAnimationFrame(updateCanvas);
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:59:08 +02:00
|
|
|
resetHaltingMechanism();
|
|
|
|
updateCanvas(lastRecordedTime);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the page is hidden (e.g. on tab change), the web browser
|
|
|
|
* is likely to stop calling requestAnimationFrame (to save ressources).
|
|
|
|
|
|
|
|
* This may halt the animation when the user displays the page again,
|
|
|
|
* since the time since the last redraw will possibly be very high.
|
|
|
|
*
|
|
|
|
* Therefore, on each visibility change, the halting mechanism must be reset!
|
|
|
|
*/
|
|
|
|
document.addEventListener("visibilitychange", () => {
|
|
|
|
resetHaltingMechanism();
|
|
|
|
|
|
|
|
// In case the animation has been halted before this callback was executed,
|
|
|
|
// the canvas update must be restarted
|
|
|
|
if (animationHasBeenHalted)
|
|
|
|
updateCanvas(lastRecordedTime);
|
|
|
|
}, false);
|
2019-04-14 05:47:13 +02:00
|
|
|
})();
|