160 lines
4.8 KiB
TypeScript
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);
|
|
};
|
|
};
|