sitegen/src/file-viewer/scripts/canvas_2022.ts

160 lines
4.8 KiB
TypeScript

(globalThis as any).canvas_2022 = function (canvas: HTMLCanvasElement) {
const isStandalone = canvas.getAttribute("data-standalone") === "true";
// Configuration for the grid of rotating squares
const config = {
gridRotation: 20, // Overall grid rotation in degrees
squareSize: 20, // Size of each square
spacing: 100, // Distance between square centers
moveSpeedX: 0.01, // Horizontal movement speed (pixels per second)
moveSpeedY: 0.01, // Vertical movement speed (pixels per second)
squareColor: "#00220A", // Color of the squares
squareOpacity: 1, // Opacity of the squares
// Function to determine square rotation based on its coordinates and time
// Can be adjusted for different patterns
rotationFunction: (x: number, y: number, time: number): number => {
// Combination of spatial wave and time-based rotation
return Math.sin(x * 0.05) * Math.cos(y * 0.05) * 180;
},
};
// Convert grid rotation to radians
const gridRotationRad = (config.gridRotation * Math.PI) / 180;
// Get the canvas context
const ctx = canvas.getContext("2d");
if (!ctx) {
console.error("Could not get canvas context");
return () => {};
}
// Make canvas transparent
if (isStandalone) {
canvas.style.backgroundColor = "#154226";
} else {
canvas.style.backgroundColor = "transparent";
}
// Animation variables
let width = canvas.width;
let height = canvas.height;
let offsetX = 0;
let offsetY = 0;
let time = 0;
let animationFrameId: number;
let lastTime = 0;
// Update canvas dimensions when resized
const updateDimensions = () => {
width = canvas.width = canvas.clientWidth;
height = canvas.height = canvas.clientHeight;
};
// Calculate the diagonal length of the canvas (to ensure rotation covers corners)
const calculateDiagonal = () => {
return Math.sqrt(width * width + height * height);
};
// Draw a single square with rotation
const drawSquare = (x: number, y: number, size: number, rotation: number) => {
ctx.save();
// Move to the center of the square position, rotate, then draw
ctx.translate(x, y);
ctx.rotate((rotation * Math.PI) / 180); // Convert rotation degrees to radians
// Draw square centered at position
ctx.fillRect(-size / 2, -size / 2, size, size);
ctx.restore();
};
// Draw the entire grid of squares
const drawGrid = () => {
ctx.clearRect(0, 0, width, height);
// Set drawing properties
ctx.fillStyle = config.squareColor;
ctx.globalAlpha = config.squareOpacity;
// Save the current transformation state
ctx.save();
// Move to the center of the canvas, rotate the grid, then move back
const centerX = width / 2;
const centerY = height / 2;
ctx.translate(centerX, centerY);
ctx.rotate(gridRotationRad);
// Calculate how much of the grid to draw based on canvas size
const diagonal = calculateDiagonal();
const gridSize = Math.ceil(diagonal / config.spacing) + 2;
// Adjust for offset to create movement
const adjustedOffsetX = offsetX % config.spacing;
const adjustedOffsetY = offsetY % config.spacing;
// Draw grid with enough squares to cover the rotated canvas
const halfGrid = Math.ceil(gridSize / 2);
for (let y = -halfGrid; y <= halfGrid; y++) {
for (let x = -halfGrid; x <= halfGrid; x++) {
// Calculate actual position with offset
const posX = x * config.spacing + adjustedOffsetX;
const posY = y * config.spacing + adjustedOffsetY;
// Calculate square rotation based on its position and time
const squareRotation = config.rotationFunction(posX, posY, time);
// Draw the square
drawSquare(posX, posY, config.squareSize, squareRotation);
}
}
// Restore the transformation state
ctx.restore();
// Reset global alpha
ctx.globalAlpha = 1.0;
};
// Animation loop
const animate = (currentTime: number) => {
animationFrameId = requestAnimationFrame(animate);
// Calculate time elapsed since last frame
const elapsed = currentTime - lastTime;
lastTime = currentTime;
// Update time variable for rotation function
time += elapsed;
// Update position offsets for movement
offsetX += config.moveSpeedX * elapsed;
offsetY += config.moveSpeedY * elapsed;
// Draw the grid
drawGrid();
};
// Initialize the animation
const init = () => {
// Set up resize handler
window.addEventListener("resize", updateDimensions);
// Initial setup
updateDimensions();
// Start animation
animationFrameId = requestAnimationFrame(animate);
};
// Start the animation
init();
// Return cleanup function
return () => {
window.removeEventListener("resize", updateDimensions);
cancelAnimationFrame(animationFrameId);
};
};