// ========================================
// Floating Hearts Animation
// ========================================
function createFloatingHearts() {
const heartsContainer = document.getElementById('heartsContainer');
const heartSymbols = ['❤️', '💕', '💖', '💗', '💓', '💝'];
function addHeart() {
const heart = document.createElement('div');
heart.className = 'floating-heart';
heart.textContent = heartSymbols[Math.floor(Math.random() * heartSymbols.length)];
heart.style.left = Math.random() * 100 + '%';
heart.style.animationDuration = (Math.random() * 5 + 8) + 's';
heart.style.fontSize = (Math.random() * 1.5 + 1) + 'rem';
heartsContainer.appendChild(heart);
setTimeout(() => {
heart.remove();
}, 12000);
}
// Create initial hearts
for (let i = 0; i < 15; i++) {
setTimeout(addHeart, i * 500);
}
// Continue creating hearts
setInterval(addHeart, 1000);
}
// ========================================
// Particle System
// ========================================
function initParticleSystem() {
const canvas = document.getElementById('particleCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const particles = [];
const particleCount = 50;
class Particle {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.size = Math.random() * 3 + 1;
this.speedX = Math.random() * 2 - 1;
this.speedY = Math.random() * 2 - 1;
this.opacity = Math.random() * 0.5 + 0.2;
}
update() {
this.x += this.speedX;
this.y += this.speedY;
if (this.x > canvas.width) this.x = 0;
if (this.x < 0) this.x = canvas.width;
if (this.y > canvas.height) this.y = 0;
if (this.y < 0) this.y = canvas.height;
}
draw() {
ctx.fillStyle = `rgba(255, 255, 255, ${this.opacity})`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
function init() {
for (let i = 0; i < particleCount; i++) {
particles.push(new Particle());
}
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(particle => {
particle.update();
particle.draw();
});
requestAnimationFrame(animate);
}
init();
animate();
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
}
// ========================================
// Countdown Timer
// ========================================
function initCountdown() {
const daysEl = document.getElementById('days');
const hoursEl = document.getElementById('hours');
const minutesEl = document.getElementById('minutes');
const secondsEl = document.getElementById('seconds');
function updateCountdown() {
const now = new Date();
const currentYear = now.getFullYear();
let valentinesDay = new Date(currentYear, 1, 14); // February 14
// If Valentine's Day has passed this year, count to next year
if (now > valentinesDay) {
valentinesDay = new Date(currentYear + 1, 1, 14);
}
const diff = valentinesDay - now;
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
daysEl.textContent = String(days).padStart(2, '0');
hoursEl.textContent = String(hours).padStart(2, '0');
minutesEl.textContent = String(minutes).padStart(2, '0');
secondsEl.textContent = String(seconds).padStart(2, '0');
}
updateCountdown();
setInterval(updateCountdown, 1000);
}
// ========================================
// Scroll Reveal Animation
// ========================================
function initScrollReveal() {
const revealElements = document.querySelectorAll('.reveal-on-scroll');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('revealed');
}
});
}, {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
});
revealElements.forEach(el => observer.observe(el));
}
// ========================================
// Typewriter Effect
// ========================================
function initTypewriter() {
const messageEl = document.getElementById('loveMessage');
const message = `My Dearest Valentine,
Every moment with you feels like a beautiful dream come true. Your smile lights up my world, and your laughter is the sweetest melody I've ever heard.
From the first time we met, I knew you were special. You've brought so much joy, love, and meaning into my life. Every day with you is an adventure, and I cherish every single moment we share together.
Thank you for being my partner, my best friend, and my greatest love. You make every day feel like Valentine's Day.
I love you more than words can express, today and always. 💖`;
let index = 0;
function type() {
if (index < message.length) {
messageEl.textContent += message.charAt(index);
index++;
setTimeout(type, 30);
} else {
messageEl.classList.remove('typewriter');
}
}
// Start typing when element is in view
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && index === 0) {
type();
observer.unobserve(entry.target);
}
});
}, { threshold: 0.5 });
observer.observe(messageEl);
}
// ========================================
// Love Button Interaction
// ========================================
function initLoveButton() {
const loveButton = document.getElementById('loveButton');
const loveCount = document.getElementById('loveCount');
let count = 0;
loveButton.addEventListener('click', function(e) {
count++;
loveCount.textContent = count;
// Create floating hearts from button
for (let i = 0; i < 5; i++) {
createClickHeart(e.clientX, e.clientY);
}
// Add animation class
loveButton.style.transform = 'scale(0.9)';
setTimeout(() => {
loveButton.style.transform = '';
}, 200);
});
function createClickHeart(x, y) {
const heart = document.createElement('div');
heart.textContent = '❤️';
heart.style.position = 'fixed';
heart.style.left = x + 'px';
heart.style.top = y + 'px';
heart.style.fontSize = '2rem';
heart.style.pointerEvents = 'none';
heart.style.zIndex = '9999';
heart.style.animation = 'floatUpClick 2s ease-out forwards';
document.body.appendChild(heart);
setTimeout(() => {
heart.remove();
}, 2000);
}
// Add CSS animation for click hearts
const style = document.createElement('style');
style.textContent = `
@keyframes floatUpClick {
0% {
transform: translateY(0) scale(1);
opacity: 1;
}
100% {
transform: translateY(-200px) scale(0.5);
opacity: 0;
}
}
`;
document.head.appendChild(style);
}
// ========================================
// Smooth Scroll for Navigation
// ========================================
function initSmoothScroll() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
// ========================================
// Gallery Hover Effects
// ========================================
function initGalleryEffects() {
const galleryItems = document.querySelectorAll('.gallery-item');
galleryItems.forEach(item => {
item.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-10px) scale(1.02)';
});
item.addEventListener('mouseleave', function() {
this.style.transform = '';
});
});
}
// ========================================
// Mouse Trail Effect
// ========================================
function initMouseTrail() {
let lastTime = 0;
const throttleDelay = 100;
document.addEventListener('mousemove', function(e) {
const currentTime = Date.now();
if (currentTime - lastTime < throttleDelay) {
return;
}
lastTime = currentTime;
if (Math.random() > 0.7) {
const sparkle = document.createElement('div');
sparkle.textContent = '✨';
sparkle.style.position = 'fixed';
sparkle.style.left = e.clientX + 'px';
sparkle.style.top = e.clientY + 'px';
sparkle.style.fontSize = '1rem';
sparkle.style.pointerEvents = 'none';
sparkle.style.zIndex = '9999';
sparkle.style.animation = 'sparkle 1s ease-out forwards';
document.body.appendChild(sparkle);
setTimeout(() => {
sparkle.remove();
}, 1000);
}
});
// Add sparkle animation
const style = document.createElement('style');
style.textContent = `
@keyframes sparkle {
0% {
transform: scale(0) rotate(0deg);
opacity: 1;
}
100% {
transform: scale(1.5) rotate(180deg);
opacity: 0;
}
}
`;
document.head.appendChild(style);
}
// ========================================
// Initialize All Features
// ========================================
document.addEventListener('DOMContentLoaded', function() {
console.log('💖 Valentine\'s Day Website Loaded!');
// Initialize all features
createFloatingHearts();
initParticleSystem();
initCountdown();
initScrollReveal();
initTypewriter();
initLoveButton();
initSmoothScroll();
initGalleryEffects();
initMouseTrail();
// Add entrance animation to body
document.body.style.opacity = '0';
setTimeout(() => {
document.body.style.transition = 'opacity 1s ease';
document.body.style.opacity = '1';
}, 100);
});
// ========================================
// Performance Optimization
// ========================================
// Pause animations when tab is not visible
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
// Pause heavy animations
console.log('Tab hidden - reducing animations');
} else {
// Resume animations
console.log('Tab visible - resuming animations');
}
});