/**
* Valentine's Day Website Logic
* Handles animations (Starfield, Confetti) and Interactions.
*/
// --- Canvas Setup ---
const canvas = document.getElementById('starfield');
const ctx = canvas.getContext('2d');
let width, height;
// Resize handling
function resize() {
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
}
window.addEventListener('resize', resize);
resize();
// --- Star Class ---
class Star {
constructor() {
this.reset();
// Start with random progress to avoid "waves"
this.progress = Math.random();
}
reset() {
this.x = Math.random() * width;
this.y = Math.random() * height;
this.size = Math.random() * 2 + 0.5; // 0.5 to 2.5
this.speed = Math.random() * 0.02 + 0.005;
this.alpha = Math.random();
this.direction = Math.random() > 0.5 ? 1 : -1;
}
update() {
this.alpha += this.speed * this.direction;
if (this.alpha >= 1 || this.alpha <= 0) {
this.direction *= -1;
}
}
draw() {
ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
// --- Confetti Class ---
class Confetti {
constructor() {
this.reset();
this.y = Math.random() * -height; // Start above screen
}
reset() {
this.x = Math.random() * width;
this.y = -10;
this.size = Math.random() * 10 + 5;
this.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
this.speedY = Math.random() * 3 + 2;
this.speedX = (Math.random() - 0.5) * 2;
this.rotation = Math.random() * 360;
this.rotationSpeed = (Math.random() - 0.5) * 10;
}
update() {
this.y += this.speedY;
this.x += this.speedX;
this.rotation += this.rotationSpeed;
if (this.y > height) {
this.reset();
}
}
draw() {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation * Math.PI / 180);
ctx.fillStyle = this.color;
ctx.fillRect(-this.size / 2, -this.size / 2, this.size, this.size);
ctx.restore();
}
}
// --- Animation System ---
const stars = Array.from({length: 200}, () => new Star());
let confettis = []; // Empty initially
let isCelebrating = false;
function animate() {
ctx.clearRect(0, 0, width, height);
// Draw Stars
stars.forEach(star => {
star.update();
star.draw();
});
// Draw Confetti if celebrating
if (isCelebrating) {
if (confettis.length < 150) {
confettis.push(new Confetti());
}
confettis.forEach(c => {
c.update();
c.draw();
});
}
requestAnimationFrame(animate);
}
animate();
// --- Interaction Logic ---
const envelopeWrapper = document.getElementById('envelope-wrapper');
const yesBtn = document.getElementById('yesBtn');
const noBtn = document.getElementById('noBtn');
const celebrationOverlay = document.getElementById('celebration-overlay');
// Open Envelope
envelopeWrapper.addEventListener('click', () => {
if (!envelopeWrapper.classList.contains('open')) {
envelopeWrapper.classList.add('open');
// Hide instruction
document.querySelector('.click-instruction').style.display = 'none';
}
});
// "No" Button Evasion
noBtn.addEventListener('mouseover', () => {
const maxX = 100; // Movement range inside the card/actions
const maxY = 50;
const randomX = (Math.random() - 0.5) * 2 * maxX;
const randomY = (Math.random() - 0.5) * 2 * maxY;
noBtn.style.transform = `translate(${randomX}px, ${randomY}px)`;
});
// "Yes" Button Celebration
yesBtn.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent bubbling if needed
isCelebrating = true;
celebrationOverlay.classList.add('active');
// Optional: Change background gradient aggressively
document.body.style.background = 'radial-gradient(circle at center, #ff758c, #ff7eb3)';
});