Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', function() { | |
| // Luxury Cursor System | |
| class PremiumCursor { | |
| constructor() { | |
| this.cursor = document.createElement('div'); | |
| this.cursorFollower = document.createElement('div'); | |
| this.cursorInner = document.createElement('div'); | |
| this.cursorText = document.createElement('div'); | |
| this.posX = 0; | |
| this.posY = 0; | |
| this.mouseX = 0; | |
| this.mouseY = 0; | |
| this.isHovering = false; | |
| this.isClicking = false; | |
| this.currentText = ''; | |
| this.initCursor(); | |
| this.initEvents(); | |
| this.animate(); | |
| this.addHoverTextEffects(); | |
| } | |
| initCursor() { | |
| // Outer circle (magnetic field) | |
| this.cursor.style.position = 'fixed'; | |
| this.cursor.style.width = '48px'; | |
| this.cursor.style.height = '48px'; | |
| this.cursor.style.border = '1px solid rgba(14, 165, 233, 0.3)'; | |
| this.cursor.style.borderRadius = '50%'; | |
| this.cursor.style.pointerEvents = 'none'; | |
| this.cursor.style.zIndex = '9999'; | |
| this.cursor.style.transform = 'translate(-50%, -50%) scale(0)'; | |
| this.cursor.style.transition = 'transform 0.4s cubic-bezier(0.33, 1, 0.68, 1), border-color 0.4s ease, width 0.4s ease, height 0.4s ease'; | |
| this.cursor.style.mixBlendMode = 'exclusion'; | |
| // Follower | |
| this.cursorFollower.style.position = 'fixed'; | |
| this.cursorFollower.style.width = '12px'; | |
| this.cursorFollower.style.height = '12px'; | |
| this.cursorFollower.style.backgroundColor = 'rgba(14, 165, 233, 0.8)'; | |
| this.cursorFollower.style.borderRadius = '50%'; | |
| this.cursorFollower.style.pointerEvents = 'none'; | |
| this.cursorFollower.style.zIndex = '10000'; | |
| this.cursorFollower.style.transform = 'translate(-50%, -50%)'; | |
| this.cursorFollower.style.transition = 'transform 0.1s ease, background-color 0.3s ease'; | |
| // Inner dot | |
| this.cursorInner.style.position = 'fixed'; | |
| this.cursorInner.style.width = '4px'; | |
| this.cursorInner.style.height = '4px'; | |
| this.cursorInner.style.backgroundColor = 'rgba(255, 255, 255, 0.9)'; | |
| this.cursorInner.style.borderRadius = '50%'; | |
| this.cursorInner.style.pointerEvents = 'none'; | |
| this.cursorInner.style.zIndex = '10001'; | |
| this.cursorInner.style.transform = 'translate(-50%, -50%)'; | |
| document.body.appendChild(this.cursor); | |
| document.body.appendChild(this.cursorFollower); | |
| document.body.appendChild(this.cursorInner); | |
| } | |
| initEvents() { | |
| document.addEventListener('mousemove', (e) => { | |
| this.mouseX = e.clientX; | |
| this.mouseY = e.clientY; | |
| this.cursorInner.style.left = `${this.mouseX}px`; | |
| this.cursorInner.style.top = `${this.mouseY}px`; | |
| }); | |
| const interactiveElements = document.querySelectorAll('a, button, [data-interactive], .professional-btn, .professional-card'); | |
| interactiveElements.forEach(el => { | |
| el.addEventListener('mouseenter', () => { | |
| this.isHovering = true; | |
| this.cursor.style.borderColor = 'rgba(124, 58, 237, 0.8)'; | |
| this.cursor.style.transform = 'translate(-50%, -50%) scale(1.2)'; | |
| this.cursorFollower.style.transform = 'translate(-50%, -50%) scale(0.5)'; | |
| this.cursorFollower.style.backgroundColor = 'rgba(124, 58, 237, 0.6)'; | |
| }); | |
| el.addEventListener('mouseleave', () => { | |
| this.isHovering = false; | |
| this.cursor.style.borderColor = 'rgba(14, 165, 233, 0.5)'; | |
| this.cursor.style.transform = 'translate(-50%, -50%) scale(1)'; | |
| this.cursorFollower.style.transform = 'translate(-50%, -50%) scale(1)'; | |
| this.cursorFollower.style.backgroundColor = 'rgba(14, 165, 233, 0.8)'; | |
| }); | |
| }); | |
| } | |
| animate() { | |
| this.posX += (this.mouseX - this.posX) / 6; | |
| this.posY += (this.mouseY - this.posY) / 6; | |
| this.cursor.style.left = `${this.posX}px`; | |
| this.cursor.style.top = `${this.posY}px`; | |
| this.cursorFollower.style.left = `${this.posX}px`; | |
| this.cursorFollower.style.top = `${this.posY}px`; | |
| requestAnimationFrame(() => this.animate()); | |
| } | |
| } | |
| // Initialize luxury cursor system | |
| const luxuryCursor = new PremiumCursor(); | |
| // Add click animation | |
| document.addEventListener('mousedown', () => { | |
| luxuryCursor.startClick(); | |
| }); | |
| document.addEventListener('mouseup', () => { | |
| luxuryCursor.endClick(); | |
| }); | |
| const cursorTrail = document.createElement('div'); | |
| cursorTrail.style.position = 'fixed'; | |
| cursorTrail.style.width = '24px'; | |
| cursorTrail.style.height = '24px'; | |
| cursorTrail.style.background = 'radial-gradient(circle, rgba(124,58,237,0.8) 0%, rgba(14,165,233,0.4) 70%, transparent 100%)'; | |
| cursorTrail.style.borderRadius = '50%'; | |
| cursorTrail.style.pointerEvents = 'none'; | |
| cursorTrail.style.zIndex = '9999'; | |
| cursorTrail.style.transform = 'translate(-50%, -50%) scale(0)'; | |
| cursorTrail.style.mixBlendMode = 'overlay'; | |
| cursorTrail.style.transition = 'transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1)'; | |
| cursorTrail.style.filter = 'blur(1px)'; | |
| document.body.appendChild(cursorTrail); | |
| // Professional cursor dot | |
| const cursorDot = document.createElement('div'); | |
| cursorDot.style.position = 'fixed'; | |
| cursorDot.style.width = '6px'; | |
| cursorDot.style.height = '6px'; | |
| cursorDot.style.backgroundColor = 'rgba(124,58,237,0.9)'; | |
| cursorDot.style.borderRadius = '50%'; | |
| cursorDot.style.pointerEvents = 'none'; | |
| cursorDot.style.zIndex = '10000'; | |
| cursorDot.style.transform = 'translate(-50%, -50%)'; | |
| document.body.appendChild(cursorDot); | |
| let posX = 0, posY = 0; | |
| let mouseX = 0, mouseY = 0; | |
| let isHovering = false; | |
| document.addEventListener('mousemove', (e) => { | |
| mouseX = e.clientX; | |
| mouseY = e.clientY; | |
| cursorDot.style.left = `${mouseX}px`; | |
| cursorDot.style.top = `${mouseY}px`; | |
| }); | |
| const updateCursor = () => { | |
| posX += (mouseX - posX) / 6; | |
| posY += (mouseY - posY) / 6; | |
| cursorTrail.style.left = `${posX}px`; | |
| cursorTrail.style.top = `${posY}px`; | |
| cursorTrail.style.transform = `translate(-50%, -50%) scale(${isHovering ? 1.5 : 1})`; | |
| requestAnimationFrame(updateCursor); | |
| }; | |
| updateCursor(); | |
| // Professional hover effect for interactive elements | |
| const interactiveElements = document.querySelectorAll('a, button, [data-interactive], .professional-btn, .professional-card'); | |
| interactiveElements.forEach(el => { | |
| el.addEventListener('mouseenter', () => { | |
| isHovering = true; | |
| cursorTrail.style.background = 'radial-gradient(circle, rgba(255,255,255,0.9) 0%, rgba(124,58,237,0.7) 70%, transparent 100%)'; | |
| cursorTrail.style.filter = 'blur(1.5px)'; | |
| cursorDot.style.transform = 'translate(-50%, -50%) scale(1.5)'; | |
| cursorDot.style.backgroundColor = 'rgba(255,255,255,0.9)'; | |
| }); | |
| el.addEventListener('mouseleave', () => { | |
| isHovering = false; | |
| cursorTrail.style.background = 'radial-gradient(circle, rgba(124,58,237,0.8) 0%, rgba(14,165,233,0.4) 70%, transparent 100%)'; | |
| cursorTrail.style.filter = 'blur(1px)'; | |
| cursorDot.style.transform = 'translate(-50%, -50%) scale(1)'; | |
| cursorDot.style.backgroundColor = 'rgba(124,58,237,0.9)'; | |
| }); | |
| }); | |
| // Smooth scrolling for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const targetId = this.getAttribute('href'); | |
| if (targetId === '#') return; | |
| const targetElement = document.querySelector(targetId); | |
| if (targetElement) { | |
| targetElement.scrollIntoView({ | |
| behavior: 'smooth' | |
| }); | |
| } | |
| }); | |
| }); | |
| // Artwork modal functionality | |
| const artworkModal = document.createElement('artwork-modal'); | |
| document.body.appendChild(artworkModal); | |
| const artworkButtons = document.querySelectorAll('.professional-card button'); | |
| artworkButtons.forEach((button, index) => { | |
| button.addEventListener('click', () => { | |
| const card = button.closest('.professional-card'); | |
| const img = card.querySelector('img'); | |
| const title = card.querySelector('h3').textContent; | |
| const meta = card.querySelector('p').textContent; | |
| artworkModal.open({ | |
| image: img.src, | |
| title: title, | |
| medium: meta.split('|')[0].trim(), | |
| year: meta.split('|')[1].trim(), | |
| description: 'This artwork represents a unique blend of digital techniques and creative vision. Each element was carefully crafted to evoke emotion and tell a visual story.' | |
| }); | |
| }); | |
| }); | |
| // Form submission | |
| const contactForm = document.querySelector('form'); | |
| if (contactForm) { | |
| contactForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| // Show success state | |
| const submitButton = this.querySelector('button[type="submit"]'); | |
| submitButton.innerHTML = '<i data-feather="check"></i> Message Sent'; | |
| submitButton.disabled = true; | |
| // Add micro-interaction | |
| gsap.to(submitButton, { | |
| scale: 0.95, | |
| duration: 0.2, | |
| yoyo: true, | |
| repeat: 1, | |
| ease: "power1.inOut", | |
| onComplete: () => { | |
| setTimeout(() => { | |
| this.reset(); | |
| submitButton.innerHTML = 'Send Message <i data-feather="send" class="ml-2 inline"></i>'; | |
| submitButton.disabled = false; | |
| feather.replace(); | |
| }, 2000); | |
| } | |
| }); | |
| feather.replace(); | |
| }); | |
| } | |
| // Enhanced Intersection Observer for animations | |
| const animateOnScroll = () => { | |
| const elements = document.querySelectorAll('[data-animate]'); | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| const animationType = entry.target.getAttribute('data-animate'); | |
| const delay = entry.target.getAttribute('data-delay') || '0'; | |
| entry.target.style.setProperty('--delay', delay); | |
| entry.target.classList.add(`animate-${animationType}`); | |
| // Remove observer if not a repeating animation | |
| if (!entry.target.hasAttribute('data-repeat')) { | |
| observer.unobserve(entry.target); | |
| } | |
| } else if (entry.target.hasAttribute('data-repeat')) { | |
| entry.target.classList.remove(`animate-${animationType}`); | |
| } | |
| }); | |
| }, { | |
| threshold: 0.1, | |
| rootMargin: '-50px' | |
| }); | |
| elements.forEach(element => { | |
| observer.observe(element); | |
| }); | |
| }; | |
| // Parallax effect for hero background | |
| const heroSection = document.querySelector('.hero-section'); | |
| if (heroSection) { | |
| window.addEventListener('scroll', () => { | |
| const scrollPosition = window.pageYOffset; | |
| heroSection.style.backgroundPositionY = `${scrollPosition * 0.5}px`; | |
| }); | |
| } | |
| // Hover tilt effect for artwork cards | |
| const artworkCards = document.querySelectorAll('.artwork-card'); | |
| artworkCards.forEach(card => { | |
| card.addEventListener('mousemove', (e) => { | |
| const xAxis = (window.innerWidth / 2 - e.pageX) / 25; | |
| const yAxis = (window.innerHeight / 2 - e.pageY) / 25; | |
| card.style.transform = `rotateY(${xAxis}deg) rotateX(${yAxis}deg)`; | |
| }); | |
| card.addEventListener('mouseenter', () => { | |
| card.style.transition = 'transform 0.1s ease'; | |
| }); | |
| card.addEventListener('mouseleave', () => { | |
| card.style.transition = 'transform 0.5s ease'; | |
| card.style.transform = 'rotateY(0deg) rotateX(0deg)'; | |
| }); | |
| }); | |
| // Initialize animations | |
| animateOnScroll(); | |
| // Animate elements when they become visible | |
| const animatedElements = document.querySelectorAll('[data-animate-on-visible]'); | |
| const visibilityObserver = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('visible'); | |
| } | |
| }); | |
| }, { threshold: 0.1 }); | |
| animatedElements.forEach(element => { | |
| visibilityObserver.observe(element); | |
| }); | |
| // Premium text reveal animation | |
| const textRevealElements = document.querySelectorAll('.text-reveal'); | |
| textRevealElements.forEach(element => { | |
| const words = element.textContent.split(' '); | |
| element.innerHTML = words.map(word => | |
| `<span class="text-reveal-word">${word.split('').map(letter => | |
| `<span class="text-reveal-char">${letter}</span>` | |
| ).join('')}</span>` | |
| ).join(' '); | |
| const chars = element.querySelectorAll('.text-reveal-char'); | |
| const wordsContainers = element.querySelectorAll('.text-reveal-word'); | |
| // Character animation | |
| gsap.from(chars, { | |
| y: '120%', | |
| opacity: 0, | |
| duration: 0.8, | |
| ease: 'back.out(1.7)', | |
| stagger: { | |
| each: 0.02, | |
| from: 'random' | |
| }, | |
| scrollTrigger: { | |
| trigger: element, | |
| start: 'top 85%', | |
| toggleActions: 'play none none none' | |
| } | |
| }); | |
| // Word container animation | |
| gsap.from(wordsContainers, { | |
| y: '20px', | |
| opacity: 0, | |
| duration: 1.2, | |
| ease: 'power3.out', | |
| stagger: 0.08, | |
| scrollTrigger: { | |
| trigger: element, | |
| start: 'top 85%', | |
| toggleActions: 'play none none none' | |
| } | |
| }); | |
| }); | |
| // Add subtle parallax to hero section | |
| const heroSection = document.querySelector('.hero-section'); | |
| if (heroSection) { | |
| const bgElements = heroSection.querySelectorAll('div[style*="background"]'); | |
| window.addEventListener('scroll', () => { | |
| const scrollY = window.scrollY; | |
| const speed = 0.3; | |
| bgElements.forEach((el, index) => { | |
| const yPos = -(scrollY * speed * (index + 1) * 0.5); | |
| gsap.to(el, { | |
| y: yPos, | |
| ease: 'none' | |
| }); | |
| }); | |
| }); | |
| } | |
| }); |