Initial commit.
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Floating Beat Canvas</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="beatCanvas"></canvas>
|
||||
|
||||
<div id="imageAssets" style="display: none;">
|
||||
<img id="img1" src="img1.png">
|
||||
<img id="img2" src="img2.png">
|
||||
<img id="img3" src="img3.png">
|
||||
<img id="img4" src="img4.png">
|
||||
<img id="img5" src="img5.png">
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,211 @@
|
||||
// --- Setup ---
|
||||
const canvas = document.getElementById('beatCanvas'); // Ensure this ID matches your HTML
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// --- Configuration ---
|
||||
const FADE_DURATION = 2000; // 1.5 seconds for fade in/out of background images
|
||||
const DISPLAY_DURATION = 10000; // 4 seconds of full display time for background images
|
||||
const TRANSITION_CYCLE = FADE_DURATION + DISPLAY_DURATION; // Total background image cycle length
|
||||
|
||||
const NUM_SPARKLES = 100; // How many sparkles to have on screen
|
||||
const SPARKLE_SIZE_MIN = 5; // Min radius for a sparkle
|
||||
const SPARKLE_SIZE_MAX = 20; // Max radius for a sparkle
|
||||
const SPARKLE_SPEED_MIN = 0.1; // Min speed for sparkle movement
|
||||
const SPARKLE_SPEED_MAX = 0.5; // Max speed for sparkle movement
|
||||
const SPARKLE_FADE_SPEED = 0.002; // How quickly sparkles fade in/out per frame (adjust for desired effect)
|
||||
|
||||
// --- Global Pulse Configuration ---
|
||||
const BEAT_INTERVAL = 500; // 120 BPM = 500ms per beat
|
||||
let lastBeatTime = 0;
|
||||
let beatPulse = 0; // 0 to 1, indicates how far into the current beat we are
|
||||
const PULSE_DECAY = 0.03; // How quickly the pulse effect fades after the beat hits
|
||||
const PULSE_STRENGTH = 0.3; // Maximum scale increase (e.g., 30% larger)
|
||||
|
||||
let images = []; // Array to hold loaded Image objects for background
|
||||
let currentImageIndex = 0;
|
||||
let nextImageIndex = 1;
|
||||
let cycleStartTime = 0; // Tracks the start time of the current background image display/fade cycle
|
||||
|
||||
let sparkles = []; // Array to hold all sparkle objects
|
||||
|
||||
// --- Sparkle Class (Pulsating) ---
|
||||
class Sparkle {
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.x = Math.random() * canvas.width;
|
||||
this.y = Math.random() * canvas.height;
|
||||
// Store the base radius, which will be scaled in draw()
|
||||
this.baseRadius = SPARKLE_SIZE_MIN + Math.random() * (SPARKLE_SIZE_MAX - SPARKLE_SIZE_MIN);
|
||||
this.speedX = (Math.random() - 0.5) * (SPARKLE_SPEED_MAX - SPARKLE_SPEED_MIN) + SPARKLE_SPEED_MIN;
|
||||
this.speedY = (Math.random() - 0.5) * (SPARKLE_SPEED_MAX - SPARKLE_SPEED_MIN) + SPARKLE_SPEED_MIN;
|
||||
this.alpha = Math.random();
|
||||
this.alphaDirection = Math.random() > 0.5 ? 1 : -1;
|
||||
this.hue = Math.random() * 360;
|
||||
}
|
||||
|
||||
update() {
|
||||
// Movement
|
||||
this.x += this.speedX;
|
||||
this.y += this.speedY;
|
||||
|
||||
// Bounce off edges
|
||||
if (this.x < 0 || this.x > canvas.width) this.speedX *= -1;
|
||||
if (this.y < 0 || this.y > canvas.height) this.speedY *= -1;
|
||||
|
||||
// Fade in/out
|
||||
this.alpha += this.alphaDirection * SPARKLE_FADE_SPEED;
|
||||
if (this.alpha > 1) {
|
||||
this.alpha = 1;
|
||||
this.alphaDirection = -1;
|
||||
} else if (this.alpha < 0) {
|
||||
this.alpha = 0;
|
||||
this.alphaDirection = 1;
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
// Calculate the current pulsating radius based on global beatPulse
|
||||
const currentRadius = this.baseRadius * (1 + beatPulse * PULSE_STRENGTH);
|
||||
|
||||
ctx.globalAlpha = this.alpha;
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = `hsl(${this.hue}, 100%, 75%)`;
|
||||
ctx.arc(this.x, this.y, currentRadius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function resizeCanvas() {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
// When canvas resizes, re-initialize sparkles to match new dimensions
|
||||
sparkles = []; // Clear existing sparkles
|
||||
for (let i = 0; i < NUM_SPARKLES; i++) {
|
||||
sparkles.push(new Sparkle());
|
||||
}
|
||||
}
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
resizeCanvas(); // Initial call to set canvas size and create initial sparkles
|
||||
|
||||
// Load background images from the hidden div
|
||||
const imageElements = Array.from(document.querySelectorAll('#imageAssets img'));
|
||||
let imagesLoadedCount = 0;
|
||||
|
||||
imageElements.forEach(imgEl => {
|
||||
const img = new Image();
|
||||
img.src = imgEl.src;
|
||||
img.onload = () => {
|
||||
images.push(img);
|
||||
imagesLoadedCount++;
|
||||
if (imagesLoadedCount === imageElements.length) {
|
||||
if (images.length < 2) {
|
||||
console.error("Need at least two images for a fade transition.");
|
||||
return;
|
||||
}
|
||||
nextImageIndex = (currentImageIndex + 1) % images.length;
|
||||
requestAnimationFrame(animate); // Start animation once backgrounds are ready
|
||||
}
|
||||
};
|
||||
img.onerror = () => {
|
||||
console.error("Failed to load image:", imgEl.src);
|
||||
};
|
||||
});
|
||||
|
||||
if (imageElements.length === 0) {
|
||||
console.error("No images found in the #imageAssets div. Please check your HTML and paths.");
|
||||
}
|
||||
|
||||
// Function to draw an image to cover the entire canvas without stretching.
|
||||
function drawImageCover(image, alpha = 1) {
|
||||
if (!image) return;
|
||||
|
||||
ctx.globalAlpha = alpha;
|
||||
|
||||
const imgAspectRatio = image.width / image.height;
|
||||
const canvasAspectRatio = canvas.width / canvas.height;
|
||||
|
||||
let sx, sy, sWidth, sHeight;
|
||||
let dx, dy, dWidth, dHeight;
|
||||
|
||||
if (imgAspectRatio > canvasAspectRatio) {
|
||||
sHeight = image.height;
|
||||
sWidth = image.height * canvasAspectRatio;
|
||||
sx = (image.width - sWidth) / 2;
|
||||
sy = 0;
|
||||
} else {
|
||||
sWidth = image.width;
|
||||
sHeight = image.width / canvasAspectRatio;
|
||||
sx = 0;
|
||||
sy = (image.height - sHeight) / 2;
|
||||
}
|
||||
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
dWidth = canvas.width;
|
||||
dHeight = canvas.height;
|
||||
|
||||
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
|
||||
ctx.globalAlpha = 1; // Reset alpha
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- Main Animation Loop (Updated) ---
|
||||
function animate(currentTime) {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
// 1. Clear the canvas for a fresh frame
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// --- Background Image Fading Logic ---
|
||||
if (images.length >= 2) {
|
||||
if (cycleStartTime === 0) {
|
||||
cycleStartTime = currentTime;
|
||||
}
|
||||
|
||||
const elapsedTimeInCycle = currentTime - cycleStartTime;
|
||||
|
||||
if (elapsedTimeInCycle < DISPLAY_DURATION) {
|
||||
drawImageCover(images[currentImageIndex], 1);
|
||||
} else if (elapsedTimeInCycle < TRANSITION_CYCLE) {
|
||||
const fadeTime = elapsedTimeInCycle - DISPLAY_DURATION;
|
||||
const fadeProgress = fadeTime / FADE_DURATION;
|
||||
const alphaCurrent = 1 - fadeProgress;
|
||||
const alphaNext = fadeProgress;
|
||||
|
||||
drawImageCover(images[nextImageIndex], alphaNext);
|
||||
drawImageCover(images[currentImageIndex], alphaCurrent);
|
||||
} else {
|
||||
currentImageIndex = nextImageIndex;
|
||||
nextImageIndex = (currentImageIndex + 1) % images.length;
|
||||
cycleStartTime = currentTime;
|
||||
drawImageCover(images[currentImageIndex], 1);
|
||||
}
|
||||
} else if (images.length === 1) {
|
||||
// If only one image, just display it
|
||||
drawImageCover(images[0], 1);
|
||||
}
|
||||
|
||||
// 3. Beat Pulse Logic (Controls sparkle pulsation)
|
||||
if (currentTime - lastBeatTime >= BEAT_INTERVAL) {
|
||||
lastBeatTime = currentTime;
|
||||
beatPulse = 1; // Beat hits!
|
||||
}
|
||||
beatPulse = Math.max(0, beatPulse - PULSE_DECAY); // Decay the pulse
|
||||
|
||||
|
||||
// --- Sparkle Animation Logic (on top of background) ---
|
||||
sparkles.forEach(sparkle => {
|
||||
sparkle.update();
|
||||
sparkle.draw();
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
// Animation starts after background images are loaded
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
#beatCanvas {
|
||||
display: block;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
Reference in New Issue
Block a user