From 9770aa2be8226d4706daab07a8ea1280f3be47e3 Mon Sep 17 00:00:00 2001 From: Plexi09 Date: Sun, 22 Feb 2026 23:05:51 +0100 Subject: [PATCH] Added main.js --- js/main.js | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 js/main.js diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..26629c1 --- /dev/null +++ b/js/main.js @@ -0,0 +1,228 @@ +document.addEventListener('DOMContentLoaded', () => { + // 1. Initialize Lenis Smooth Scroll + const lenis = new Lenis({ + duration: 1.2, + easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), + direction: 'vertical', + gestureDirection: 'vertical', + smooth: true, + mouseMultiplier: 1, + smoothTouch: false, + touchMultiplier: 2, + infinite: false, + }); + + function raf(time) { + lenis.raf(time); + requestAnimationFrame(raf); + } + requestAnimationFrame(raf); + + // 2. Preloader Animation + const tl = gsap.timeline(); + tl.to('#loader-text', { + y: 0, + duration: 1, + ease: 'power4.out', + delay: 0.2 + }) + .to('#loader-text', { + y: '-100%', + duration: 1, + ease: 'power4.in', + delay: 0.5 + }) + .to('#preloader', { + y: '-100%', + duration: 1, + ease: 'power4.inOut' + }, '-=0.5') + .from('.hero-title', { + y: 100, + opacity: 0, + duration: 1.5, + stagger: 0.2, + ease: 'power4.out' + }, '-=0.5') + .to('.hero-subtitle', { + opacity: 1, + duration: 1, + ease: 'power2.out' + }, '-=1'); + + // 3. Custom Cursor + const cursor = document.getElementById('cursor'); + const cursorDot = document.getElementById('cursor-dot'); + const hoverTargets = document.querySelectorAll('.hover-target'); + + if (window.innerWidth >= 768) { + let mouseX = 0; + let mouseY = 0; + let cursorX = 0; + let cursorY = 0; + + document.addEventListener('mousemove', (e) => { + mouseX = e.clientX; + mouseY = e.clientY; + + // Instant dot + gsap.to(cursorDot, { + x: mouseX, + y: mouseY, + duration: 0 + }); + }); + + // Smooth outer circle + gsap.ticker.add(() => { + cursorX += (mouseX - cursorX) * 0.2; + cursorY += (mouseY - cursorY) * 0.2; + gsap.set(cursor, { x: cursorX, y: cursorY }); + }); + + hoverTargets.forEach(target => { + target.addEventListener('mouseenter', () => { + gsap.to(cursor, { scale: 2.5, backgroundColor: 'rgba(255,255,255,1)', duration: 0.3 }); + gsap.to(cursorDot, { opacity: 0, duration: 0.3 }); + }); + target.addEventListener('mouseleave', () => { + gsap.to(cursor, { scale: 1, backgroundColor: 'transparent', duration: 0.3 }); + gsap.to(cursorDot, { opacity: 1, duration: 0.3 }); + }); + }); + } + + // 4. Interactive Canvas Background (Particles) + const canvas = document.getElementById('hero-canvas'); + const ctx = canvas.getContext('2d'); + let width, height; + let particles = []; + + function resize() { + width = window.innerWidth; + height = window.innerHeight; + canvas.width = width; + canvas.height = height; + } + window.addEventListener('resize', resize); + resize(); + + class Particle { + constructor() { + this.x = Math.random() * width; + this.y = Math.random() * height; + this.vx = (Math.random() - 0.5) * 0.5; + this.vy = (Math.random() - 0.5) * 0.5; + this.radius = Math.random() * 1.5 + 0.5; + } + update() { + this.x += this.vx; + this.y += this.vy; + if (this.x < 0 || this.x > width) this.vx *= -1; + if (this.y < 0 || this.y > height) this.vy *= -1; + } + draw() { + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); + ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; + ctx.fill(); + } + } + + for (let i = 0; i < 100; i++) { + particles.push(new Particle()); + } + + let mouse = { x: null, y: null }; + window.addEventListener('mousemove', (e) => { + mouse.x = e.clientX; + mouse.y = e.clientY; + }); + + function animateParticles() { + ctx.clearRect(0, 0, width, height); + + particles.forEach(p => { + p.update(); + p.draw(); + + // Connect to mouse + if (mouse.x != null) { + const dx = mouse.x - p.x; + const dy = mouse.y - p.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < 150) { + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + ctx.lineTo(mouse.x, mouse.y); + ctx.strokeStyle = `rgba(255, 255, 255, ${1 - dist/150})`; + ctx.lineWidth = 0.5; + ctx.stroke(); + } + } + }); + requestAnimationFrame(animateParticles); + } + animateParticles(); + + // 5. GSAP Scroll Animations + gsap.registerPlugin(ScrollTrigger); + + // Reveal Text + const revealTexts = document.querySelectorAll('.reveal-text'); + revealTexts.forEach(text => { + gsap.from(text, { + scrollTrigger: { + trigger: text, + start: 'top 80%', + }, + y: 50, + opacity: 0, + duration: 1, + ease: 'power3.out' + }); + }); + + // Parallax Images + const parallaxImgs = document.querySelectorAll('.parallax-img'); + parallaxImgs.forEach(img => { + gsap.to(img, { + scrollTrigger: { + trigger: img.parentElement, + start: 'top bottom', + end: 'bottom top', + scrub: true + }, + y: 50, + ease: 'none' + }); + }); + + // Horizontal Scroll for Gallery + const gallery = document.getElementById('gallery'); + let isDown = false; + let startX; + let scrollLeft; + + gallery.addEventListener('mousedown', (e) => { + isDown = true; + gallery.classList.add('active'); + startX = e.pageX - gallery.offsetLeft; + scrollLeft = gallery.scrollLeft; + }); + gallery.addEventListener('mouseleave', () => { + isDown = false; + gallery.classList.remove('active'); + }); + gallery.addEventListener('mouseup', () => { + isDown = false; + gallery.classList.remove('active'); + }); + gallery.addEventListener('mousemove', (e) => { + if (!isDown) return; + e.preventDefault(); + const x = e.pageX - gallery.offsetLeft; + const walk = (x - startX) * 2; // Scroll-fast + gallery.scrollLeft = scrollLeft - walk; + }); +});