Created
May 29, 2025 11:42
-
-
Save aldoyh/08f140774e9aaed98cd45cdb4b70cf5e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Snapping ShadCN-Inspired Landing Page</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" xintegrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> | |
<link rel="preconnect" href="https://fonts.googleapis.com"> | |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Cairo:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
<style> | |
body { | |
font-family: 'Inter', sans-serif; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
scroll-behavior: smooth; /* Fallback for JS scrolling */ | |
} | |
html[lang="ar"] body { | |
font-family: 'Cairo', sans-serif; | |
} | |
:root { | |
--background: 0 0% 100%; | |
--foreground: 222.2 84% 4.9%; | |
--card: 0 0% 100%; | |
--card-foreground: 222.2 84% 4.9%; | |
--popover: 0 0% 100%; | |
--popover-foreground: 222.2 84% 4.9%; | |
--primary: 222.2 47.4% 11.2%; | |
--primary-foreground: 210 40% 98%; | |
--secondary: 210 40% 96.1%; | |
--secondary-foreground: 222.2 47.4% 11.2%; | |
--muted: 210 40% 96.1%; | |
--muted-foreground: 215.4 16.3% 46.9%; | |
--accent: 210 40% 96.1%; | |
--accent-foreground: 222.2 47.4% 11.2%; | |
--destructive: 0 84.2% 60.2%; | |
--destructive-foreground: 210 40% 98%; | |
--border: 214.3 31.8% 91.4%; | |
--input: 214.3 31.8% 91.4%; | |
--ring: 222.2 84% 4.9%; | |
--radius: 0.5rem; | |
--header-height: 4rem; /* 64px for h-16 */ | |
} | |
/* Snapping behavior for main sections */ | |
html { | |
scroll-snap-type: y mandatory; | |
} | |
section.full-screen-section { | |
scroll-snap-align: start; | |
min-height: 100vh; | |
display: flex; /* To help with centering content */ | |
flex-direction: column; /* Stack content vertically */ | |
justify-content: center; /* Center content vertically */ | |
padding-top: var(--header-height); /* Account for fixed header */ | |
padding-bottom: 2rem; /* Some bottom padding */ | |
} | |
/* Hero needs slightly different padding due to its nature */ | |
#hero.full-screen-section { | |
padding-top: 0; /* Hero content is already centered, header overlay is fine */ | |
} | |
.bg-background { background-color: hsl(var(--background)); } | |
.text-foreground { color: hsl(var(--foreground)); } | |
.bg-card { background-color: hsl(var(--card)); } | |
.text-card-foreground { color: hsl(var(--card-foreground)); } | |
.border-border { border-color: hsl(var(--border)); } | |
.rounded-lg { border-radius: var(--radius); } | |
.shadow-md { box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); } | |
.btn { | |
display: inline-flex; | |
align-items: center; | |
justify-content: center; | |
border-radius: var(--radius); | |
font-size: 0.875rem; | |
font-weight: 500; | |
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; | |
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); | |
transition-duration: 150ms; | |
padding: 0.5rem 1rem; | |
border: 1px solid transparent; | |
user-select: none; /* Prevent text selection on rapid clicks */ | |
} | |
.btn-primary { background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); } | |
.btn-primary:hover { background-color: hsl(var(--primary) / 0.9); } | |
.btn-secondary { background-color: hsl(var(--secondary)); color: hsl(var(--secondary-foreground)); border-color: hsl(var(--border));} | |
.btn-secondary:hover { background-color: hsl(var(--secondary) / 0.8); } | |
.btn-ghost { background-color: transparent; color: hsl(var(--primary)); } | |
.btn-ghost:hover { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); } | |
.btn-sm { padding: 0.25rem 0.75rem; font-size: 0.875rem; } | |
#bg-canvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -10; pointer-events: none; } | |
.animate-fadeInUp { opacity: 0; transform: translateY(20px); } | |
/* Confetti styles */ | |
.confetti { | |
position: fixed; | |
width: 8px; | |
height: 8px; | |
background-color: #f00; /* Default color, will be randomized */ | |
opacity: 1; | |
border-radius: 50%; | |
pointer-events: none; /* So they don't interfere with clicks */ | |
z-index: 9999; /* Above everything */ | |
animation: fall 1.5s ease-out forwards; | |
} | |
@keyframes fall { | |
0% { transform: translateY(0) translateX(0) rotate(0deg); opacity: 1; } | |
100% { transform: translateY(200px) translateX(var(--confetti-x-end)) rotate(var(--confetti-rotate-end)); opacity: 0; } | |
} | |
</style> | |
<script> | |
tailwind.config = { | |
darkMode: 'class', | |
theme: { | |
extend: { | |
colors: { | |
border: 'hsl(var(--border))', input: 'hsl(var(--input))', ring: 'hsl(var(--ring))', | |
background: 'hsl(var(--background))', foreground: 'hsl(var(--foreground))', | |
primary: { DEFAULT: 'hsl(var(--primary))', foreground: 'hsl(var(--primary-foreground))' }, | |
secondary: { DEFAULT: 'hsl(var(--secondary))', foreground: 'hsl(var(--secondary-foreground))' }, | |
destructive: { DEFAULT: 'hsl(var(--destructive))', foreground: 'hsl(var(--destructive-foreground))' }, | |
muted: { DEFAULT: 'hsl(var(--muted))', foreground: 'hsl(var(--muted-foreground))' }, | |
accent: { DEFAULT: 'hsl(var(--accent))', foreground: 'hsl(var(--accent-foreground))' }, | |
popover: { DEFAULT: 'hsl(var(--popover))', foreground: 'hsl(var(--popover-foreground))' }, | |
card: { DEFAULT: 'hsl(var(--card))', foreground: 'hsl(var(--card-foreground))' }, | |
}, | |
borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, | |
height: { screen: '100vh' } // Ensure 100vh is available if needed directly in Tailwind | |
} | |
}, | |
plugins: [ | |
function({ addUtilities }) { | |
addUtilities({ | |
'.bg-hero-pattern': { 'background-image': 'url("https://picsum.photos/seed/hero25/1920/1080?blur=1&grayscale=0.3")' }, | |
'.bg-cta-pattern': { 'background-image': 'url("https://picsum.photos/seed/cta36/1600/900?blur=0.5")' } | |
}, ['responsive', 'hover']) | |
} | |
] | |
} | |
</script> | |
</head> | |
<body class="bg-background text-foreground antialiased"> | |
<canvas id="bg-canvas"></canvas> | |
<header class="fixed top-0 left-0 right-0 z-50 bg-background/80 backdrop-blur-md border-b border-border h-16"> | |
<div class="container mx-auto px-4 sm:px-6 lg:px-8"> | |
<div class="flex items-center justify-between h-full"> | |
<div class="flex items-center"> | |
<a href="#hero" class="text-2xl font-bold text-primary" data-en="SnapSite" data-ar="سناپسايت">SnapSite</a> | |
</div> | |
<nav class="hidden md:flex space-x-1 lg:space-x-2 items-center"> | |
<a href="#hero" class="btn btn-ghost btn-sm nav-link" data-en="Home" data-ar="الرئيسية">Home</a> | |
<a href="#services" class="btn btn-ghost btn-sm nav-link" data-en="Services" data-ar="خدماتنا">Services</a> | |
<a href="#portfolio" class="btn btn-ghost btn-sm nav-link" data-en="Portfolio" data-ar="أعمالنا">Portfolio</a> | |
<a href="#cta" class="btn btn-ghost btn-sm nav-link" data-en="Contact" data-ar="اتصل بنا">Contact</a> | |
</nav> | |
<div class="flex items-center"> | |
<button class="btn btn-secondary btn-sm lang-toggle" onclick="toggleLanguage()" data-lang-en="عربي" data-lang-ar="English">عربي</button> | |
<button class="md:hidden btn btn-ghost btn-sm ml-2 rtl:mr-2 rtl:ml-0" id="mobileMenuButton"> | |
<i class="fas fa-bars text-lg"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
<div id="mobileMenu" class="hidden md:hidden bg-background border-t border-border shadow-lg"> | |
<nav class="flex flex-col space-y-1 p-4"> | |
<a href="#hero" class="block btn btn-ghost text-left nav-link" data-en="Home" data-ar="الرئيسية">Home</a> | |
<a href="#services" class="block btn btn-ghost text-left nav-link" data-en="Services" data-ar="خدماتنا">Services</a> | |
<a href="#portfolio" class="block btn btn-ghost text-left nav-link" data-en="Portfolio" data-ar="أعمالنا">Portfolio</a> | |
<a href="#cta" class="block btn btn-ghost text-left nav-link" data-en="Contact" data-ar="اتصل بنا">Contact</a> | |
</nav> | |
</div> | |
</header> | |
<main class="overflow-y-auto"> | |
<section id="hero" class="full-screen-section relative flex items-center justify-center bg-hero-pattern bg-cover bg-center bg-no-repeat"> | |
<div class="absolute inset-0 bg-black/60"></div> | |
<div class="relative container mx-auto px-4 text-center z-10"> | |
<img src="https://picsum.photos/seed/mainlogo77/150/150?gravity=face&shape=circle" alt="Company Logo" class="mx-auto mb-8 rounded-full shadow-xl border-4 border-white animate-fadeInUp" style="animation-delay: 0.2s;"> | |
<h1 class="text-4xl sm:text-5xl md:text-6xl font-bold text-white mb-6 animate-fadeInUp" data-en="Dynamic Web Experiences" data-ar="تجارب ويب ديناميكية" style="animation-delay: 0.4s;">Dynamic Web Experiences</h1> | |
<p class="text-lg sm:text-xl text-gray-200 mb-10 max-w-2xl mx-auto animate-fadeInUp" data-en="Crafting beautiful, snapping single-page applications that captivate and convert." data-ar="نصمم تطبيقات ويب رائعة أحادية الصفحة ذات انتقال سلس تجذب المستخدمين وتحقق الأهداف." style="animation-delay: 0.6s;">Crafting beautiful, snapping single-page applications that captivate and convert.</p> | |
<div class="animate-fadeInUp" style="animation-delay: 0.8s;"> | |
<a href="#services" class="btn btn-primary px-8 py-3 text-lg nav-link" data-en="Explore Services" data-ar="اكتشف خدماتنا">Explore Services</a> | |
</div> | |
</div> | |
</section> | |
<section id="services" class="full-screen-section py-16 lg:py-24 bg-secondary"> | |
<div class="container mx-auto px-4"> | |
<div class="text-center mb-12 lg:mb-16"> | |
<h2 class="text-3xl lg:text-4xl font-bold text-card-foreground mb-3" data-en="Our Expertise" data-ar="خبراتنا">Our Expertise</h2> | |
<p class="text-lg text-muted-foreground max-w-xl mx-auto" data-en="Delivering excellence in every line of code and pixel perfect design." data-ar="نقدم التميز في كل سطر برمجي وتصميم متقن لكل بكسل.">Delivering excellence in every line of code and pixel perfect design.</p> | |
</div> | |
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-8"> | |
<div class="bg-card p-6 rounded-lg shadow-md border border-border transform hover:shadow-xl transition-shadow duration-300 animate-fadeInUp"> | |
<img src="https://picsum.photos/seed/serviceA1/400/220?random=1" alt="Web Apps" class="rounded-md mb-6 w-full h-44 object-cover"> | |
<h3 class="text-xl font-semibold text-card-foreground mb-2" data-en="Custom Web Apps" data-ar="تطبيقات ويب مخصصة"><i class="fas fa-cogs text-primary mr-2 rtl:ml-2 rtl:mr-0"></i> Custom Web Apps</h3> | |
<p class="text-muted-foreground text-sm" data-en="Bespoke web application development tailored to your specific operational needs and goals." data-ar="تطوير تطبيقات ويب مصممة خصيصًا لتلبية احتياجاتك التشغيلية وأهدافك المحددة.">Bespoke web application development tailored to your specific operational needs and goals.</p> | |
</div> | |
<div class="bg-card p-6 rounded-lg shadow-md border border-border transform hover:shadow-xl transition-shadow duration-300 animate-fadeInUp" style="animation-delay: 0.1s;"> | |
<img src="https://picsum.photos/seed/serviceB2/400/220?random=2" alt="UX Research" class="rounded-md mb-6 w-full h-44 object-cover"> | |
<h3 class="text-xl font-semibold text-card-foreground mb-2" data-en="UX Research & Strategy" data-ar="بحث واستراتيجية تجربة المستخدم"><i class="fas fa-users text-primary mr-2 rtl:ml-2 rtl:mr-0"></i> UX Research & Strategy</h3> | |
<p class="text-muted-foreground text-sm" data-en="In-depth user research and strategic planning to create highly effective and user-centric designs." data-ar="بحث متعمق للمستخدم وتخطيط استراتيجي لإنشاء تصميمات فعالة للغاية تتمحور حول المستخدم.">In-depth user research and strategic planning to create highly effective and user-centric designs.</p> | |
</div> | |
<div class="bg-card p-6 rounded-lg shadow-md border border-border transform hover:shadow-xl transition-shadow duration-300 animate-fadeInUp" style="animation-delay: 0.2s;"> | |
<img src="https://picsum.photos/seed/serviceC3/400/220?random=3" alt="API Integration" class="rounded-md mb-6 w-full h-44 object-cover"> | |
<h3 class="text-xl font-semibold text-card-foreground mb-2" data-en="API Integration" data-ar="تكامل واجهات برمجة التطبيقات"><i class="fas fa-project-diagram text-primary mr-2 rtl:ml-2 rtl:mr-0"></i> API Integration</h3> | |
<p class="text-muted-foreground text-sm" data-en="Seamless integration of third-party APIs to extend functionality and streamline your workflows." data-ar="تكامل سلس لواجهات برمجة تطبيقات الطرف الثالث لتوسيع الوظائف وتبسيط سير عملك.">Seamless integration of third-party APIs to extend functionality and streamline your workflows.</p> | |
</div> | |
</div> | |
</div> | |
</section> | |
<section id="portfolio" class="full-screen-section py-16 lg:py-24 bg-background"> | |
<div class="container mx-auto px-4"> | |
<div class="text-center mb-12 lg:mb-16"> | |
<h2 class="text-3xl lg:text-4xl font-bold text-card-foreground mb-3" data-en="Featured Projects" data-ar="مشاريع مميزة">Featured Projects</h2> | |
<p class="text-lg text-muted-foreground max-w-xl mx-auto" data-en="Showcasing innovation and quality in our diverse portfolio of successful projects." data-ar="نعرض الابتكار والجودة في محفظتنا المتنوعة من المشاريع الناجحة.">Showcasing innovation and quality in our diverse portfolio of successful projects.</p> | |
</div> | |
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> | |
<a href="https://picsum.photos/seed/projectX1/1200/800" target="_blank" class="group block rounded-lg overflow-hidden shadow-lg animate-fadeInUp"> | |
<img src="https://picsum.photos/seed/projectX1/600/400?random=4" alt="Project Alpha" class="w-full h-56 object-cover transform group-hover:scale-110 transition-transform duration-300"> | |
</a> | |
<a href="https://picsum.photos/seed/projectY2/1200/800" target="_blank" class="group block rounded-lg overflow-hidden shadow-lg animate-fadeInUp" style="animation-delay: 0.1s;"> | |
<img src="https://picsum.photos/seed/projectY2/600/400?random=5" alt="Project Beta" class="w-full h-56 object-cover transform group-hover:scale-110 transition-transform duration-300"> | |
</a> | |
<a href="https://picsum.photos/seed/projectZ3/1200/800" target="_blank" class="group block rounded-lg overflow-hidden shadow-lg animate-fadeInUp" style="animation-delay: 0.2s;"> | |
<img src="https://picsum.photos/seed/projectZ3/600/400?random=6" alt="Project Gamma" class="w-full h-56 object-cover transform group-hover:scale-110 transition-transform duration-300"> | |
</a> | |
</div> | |
</div> | |
</section> | |
<section id="cta" class="full-screen-section py-20 lg:py-32 bg-cta-pattern bg-cover bg-center"> | |
<div class="absolute inset-0 bg-primary/85"></div> | |
<div class="container mx-auto px-4 text-center relative z-10"> | |
<img src="https://picsum.photos/seed/cta-icon/100/100?gravity=center&random=7" alt="CTA Icon" class="mx-auto mb-6 rounded-lg shadow-lg animate-fadeInUp" style="animation-delay: 0.2s;"> | |
<h2 class="text-3xl lg:text-4xl font-bold text-primary-foreground mb-6 animate-fadeInUp" style="animation-delay: 0.4s;" data-en="Let's Build Something Amazing" data-ar="دعنا نبني شيئًا مذهلاً">Let's Build Something Amazing</h2> | |
<p class="text-lg text-primary-foreground/80 mb-10 max-w-xl mx-auto animate-fadeInUp" style="animation-delay: 0.6s;" data-en="Your vision, our expertise. Together, we can create digital solutions that make an impact." data-ar="رؤيتك وخبرتنا. معًا، يمكننا إنشاء حلول رقمية تحدث تأثيرًا.">Your vision, our expertise. Together, we can create digital solutions that make an impact.</p> | |
<a href="mailto:[email protected]" class="btn btn-secondary px-8 py-3 text-lg animate-fadeInUp" style="animation-delay: 0.8s;" data-en="Start a Conversation" data-ar="ابدأ محادثة">Start a Conversation</a> | |
</div> | |
</section> | |
</main> | |
<footer class="py-8 bg-card border-t border-border text-center"> | |
<div class="container mx-auto px-4 text-muted-foreground text-sm"> | |
<img src="https://picsum.photos/seed/footerlogo2/80/24?grayscale&random=8" alt="SnapSite Small Logo" class="mx-auto mb-3 h-6"> | |
<p data-en="© 2025 SnapSite. Snapping to Perfection." data-ar="© 2025 سنابسايت. نحو الإتقان بانسيابية.">© 2025 SnapSite. Snapping to Perfection.</p> | |
<div class="mt-3 space-x-3 rtl:space-x-reverse"> | |
<a href="#" class="hover:text-primary text-xs" data-en="Privacy" data-ar="الخصوصية">Privacy</a> | |
<span class="text-muted-foreground/50">|</span> | |
<a href="#" class="hover:text-primary text-xs" data-en="Terms" data-ar="الشروط">Terms</a> | |
</div> | |
</div> | |
</footer> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ScrollTrigger.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ScrollToPlugin.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script> | |
<script> | |
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin); | |
const htmlEl = document.documentElement; | |
let currentLang = htmlEl.lang || 'en'; | |
const langToggleButton = document.querySelector('.lang-toggle'); | |
const mobileMenuButton = document.getElementById('mobileMenuButton'); | |
const mobileMenu = document.getElementById('mobileMenu'); | |
const navLinks = document.querySelectorAll('header nav a.nav-link[href^="#"]'); // Target specific nav links | |
function updateTextDirection(lang) { | |
htmlEl.dir = lang === 'ar' ? 'rtl' : 'ltr'; | |
} | |
function translateContent(lang) { | |
document.querySelectorAll('[data-en]').forEach(el => { | |
const text = lang === 'ar' ? el.dataset.ar : el.dataset.en; | |
if (text) el.innerHTML = text; | |
}); | |
} | |
function toggleLanguage() { | |
currentLang = currentLang === 'en' ? 'ar' : 'en'; | |
htmlEl.lang = currentLang; | |
updateTextDirection(currentLang); | |
translateContent(currentLang); | |
langToggleButton.textContent = currentLang === 'ar' ? langToggleButton.dataset.langAr : langToggleButton.dataset.langEn; | |
ScrollTrigger.refresh(); // Recalculate ScrollTrigger positions | |
} | |
updateTextDirection(currentLang); | |
translateContent(currentLang); | |
langToggleButton.textContent = currentLang === 'ar' ? langToggleButton.dataset.langAr : langToggleButton.dataset.langEn; | |
mobileMenuButton.addEventListener('click', () => { | |
mobileMenu.classList.toggle('hidden'); | |
const icon = mobileMenuButton.querySelector('i'); | |
icon.classList.toggle('fa-bars'); | |
icon.classList.toggle('fa-times'); | |
}); | |
mobileMenu.querySelectorAll('a.nav-link').forEach(link => { | |
link.addEventListener('click', () => { | |
mobileMenu.classList.add('hidden'); | |
const icon = mobileMenuButton.querySelector('i'); | |
icon.classList.remove('fa-times'); | |
icon.classList.add('fa-bars'); | |
}); | |
}); | |
// Smooth scroll for navigation links (works with CSS scroll snap) | |
navLinks.forEach(anchor => { | |
anchor.addEventListener('click', function(e) { | |
e.preventDefault(); | |
const targetId = this.getAttribute('href'); | |
const targetElement = document.querySelector(targetId); | |
if (targetElement) { | |
// GSAP ScrollToPlugin handles scrolling smoothly to the snap point | |
gsap.to(window, { | |
scrollTo: { | |
y: targetElement, // Target the element directly for snap | |
autoKill: true | |
}, | |
duration: 1, // Adjust duration as needed | |
ease: 'power3.inOut' | |
}); | |
} | |
}); | |
}); | |
function animateOnScroll() { | |
gsap.utils.toArray('.animate-fadeInUp').forEach(elem => { | |
gsap.fromTo(elem, | |
{ opacity: 0, y: 30 }, | |
{ | |
opacity: 1, y: 0, duration: 0.8, ease: 'power2.out', | |
delay: parseFloat(elem.style.animationDelay) || 0, | |
scrollTrigger: { trigger: elem, start: 'top 90%', toggleActions: 'play none none none', once: true } | |
} | |
); | |
}); | |
gsap.utils.toArray('section h2, section .text-lg.text-muted-foreground, section .text-lg.text-primary-foreground\\/80').forEach(elem => { | |
gsap.from(elem, { | |
opacity:0, y:20, duration: 0.6, ease: 'power1.out', | |
scrollTrigger: { trigger: elem, start: 'top 90%', toggleActions: 'play none none none', once: true } | |
}); | |
}); | |
} | |
document.addEventListener('DOMContentLoaded', animateOnScroll); | |
// Confetti on click | |
document.addEventListener('click', function(event) { | |
createConfetti(event.clientX, event.clientY); | |
}); | |
function createConfetti(x, y) { | |
const confettiCount = Math.floor(Math.random() * 3) + 3; // 3 to 5 tiny pieces | |
const colors = ['#FFC700', '#FF0000', '#2E86C1', '#2ECC71', '#F39C12', '#8E44AD']; | |
for (let i = 0; i < confettiCount; i++) { | |
const confettiPiece = document.createElement('div'); | |
confettiPiece.classList.add('confetti'); | |
const randomColor = colors[Math.floor(Math.random() * colors.length)]; | |
confettiPiece.style.backgroundColor = randomColor; | |
// Position near the click | |
const offsetX = (Math.random() - 0.5) * 30; // Spread within 30px horizontally | |
const offsetY = (Math.random() - 0.5) * 30; // Spread within 30px vertically | |
confettiPiece.style.left = `${x + offsetX}px`; | |
confettiPiece.style.top = `${y + offsetY}px`; | |
// Randomize animation end properties | |
const xEnd = (Math.random() - 0.5) * 100; // Horizontal drift | |
const rotateEnd = (Math.random() - 0.5) * 720; // Random rotation | |
confettiPiece.style.setProperty('--confetti-x-end', `${xEnd}px`); | |
confettiPiece.style.setProperty('--confetti-rotate-end', `${rotateEnd}deg`); | |
document.body.appendChild(confettiPiece); | |
// Remove confetti after animation | |
confettiPiece.addEventListener('animationend', () => { | |
confettiPiece.remove(); | |
}); | |
} | |
} | |
// p5.js Background Animation | |
let particles = []; | |
function setup() { | |
let canvas = createCanvas(windowWidth, windowHeight); | |
canvas.parent('bg-canvas'); | |
for (let i = 0; i < 60; i++) { particles.push(new Particle()); } // Reduced for performance | |
} | |
function draw() { | |
clear(); | |
let particleBaseColor = document.body.classList.contains('dark') ? [200, 200, 220, 50] : [80, 80, 100, 30]; // Adjusted alpha | |
for (let particle of particles) { | |
particle.color = particleBaseColor; | |
particle.update(); | |
particle.display(); | |
particle.connect(); | |
} | |
} | |
function windowResized() { resizeCanvas(windowWidth, windowHeight); } | |
class Particle { | |
constructor() { | |
this.pos = createVector(random(width), random(height)); | |
this.vel = p5.Vector.random2D().mult(random(0.05, 0.3)); // Slower | |
this.size = random(1, 2); // Smaller | |
this.color = [80,80,100,30]; | |
this.maxSpeed = 0.5; | |
this.maxForce = 0.02; | |
} | |
update() { | |
this.pos.add(this.vel); | |
this.edges(); | |
let mouse = createVector(mouseX, mouseY); | |
let desired = p5.Vector.sub(mouse, this.pos); | |
let d = desired.mag(); | |
if (d < 150) { // Wider mouse influence | |
let steer = p5.Vector.sub(desired, this.vel); | |
steer.limit(this.maxForce * (1 - d / 150) * 1.5); | |
this.applyForce(steer); | |
} | |
this.vel.limit(this.maxSpeed); | |
} | |
applyForce(force) { this.vel.add(force); } | |
display() { | |
noStroke(); | |
fill(this.color[0], this.color[1], this.color[2], this.color[3]); | |
ellipse(this.pos.x, this.pos.y, this.size); | |
} | |
connect() { | |
particles.forEach(other => { | |
if (other !== this) { | |
let d = dist(this.pos.x, this.pos.y, other.pos.x, other.pos.y); | |
if (d < 120) { // Wider connection | |
let alpha = map(d, 0, 120, 40, 0); // Adjusted alpha | |
stroke(this.color[0], this.color[1], this.color[2], alpha); | |
strokeWeight(0.3); // Thinner lines | |
line(this.pos.x, this.pos.y, other.pos.x, other.pos.y); | |
} | |
} | |
}); | |
} | |
edges() { | |
if (this.pos.x > width + this.size) this.pos.x = -this.size; | |
else if (this.pos.x < -this.size) this.pos.x = width + this.size; | |
if (this.pos.y > height + this.size) this.pos.y = -this.size; | |
else if (this.pos.y < -this.size) this.pos.y = height + this.size; | |
} | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment