← back to Billy Website
js/main.js
289 lines
// Mobile Navigation Toggle
const hamburger = document.querySelector('.hamburger');
const navMenu = document.querySelector('.nav-menu');
if (hamburger && navMenu) {
hamburger.addEventListener('click', () => {
hamburger.classList.toggle('active');
navMenu.classList.toggle('active');
});
}
// Close mobile menu when clicking a link
document.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', () => {
if (hamburger) hamburger.classList.remove('active');
if (navMenu) navMenu.classList.remove('active');
});
});
// Smooth Scrolling
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
const offset = 70; // navbar height
const targetPosition = target.offsetTop - offset;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
// Navbar scroll effect
let lastScroll = 0;
const navbar = document.querySelector('.navbar');
window.addEventListener('scroll', () => {
if (!navbar) return;
const currentScroll = window.pageYOffset;
if (currentScroll <= 0) {
navbar.style.boxShadow = '0 5px 20px rgba(0,0,0,0.1)';
return;
}
if (currentScroll > lastScroll && currentScroll > 100) {
// Scrolling down
navbar.style.transform = 'translateY(-100%)';
} else {
// Scrolling up
navbar.style.transform = 'translateY(0)';
navbar.style.boxShadow = '0 5px 30px rgba(0,0,0,0.15)';
}
lastScroll = currentScroll;
});
// Leaflet Map Integration - Laurel Canyon Location
function initMap() {
// Coordinates for 2755 Carmar Drive, Los Angeles, CA 90046
const propertyLocation = [34.1144, -118.3683];
// Initialize map
const map = L.map('nosara-map').setView(propertyLocation, 13);
// Add OpenStreetMap tile layer (free, no API key needed)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
maxZoom: 19
}).addTo(map);
// Custom icon for main property
const propertyIcon = L.divIcon({
className: 'custom-property-marker',
html: '<div style="background: #8B4513; color: white; width: 30px; height: 30px; border-radius: 50% 50% 50% 0; transform: rotate(-45deg); display: flex; align-items: center; justify-content: center; border: 3px solid white; box-shadow: 0 3px 10px rgba(0,0,0,0.3);"><i class="fas fa-home" style="transform: rotate(45deg); font-size: 14px;"></i></div>',
iconSize: [30, 30],
iconAnchor: [15, 30],
popupAnchor: [0, -30]
});
// Add marker for property location
const propertyMarker = L.marker(propertyLocation, { icon: propertyIcon }).addTo(map);
propertyMarker.bindPopup(`
<div style="padding: 10px; font-family: 'Inter', sans-serif; min-width: 200px;">
<h3 style="margin: 0 0 5px 0; color: #8B4513; font-size: 16px;">2755 Carmar Drive</h3>
<p style="margin: 0; color: #666; font-size: 14px;">Luxury rental in historic Laurel Canyon</p>
</div>
`).openPopup();
// Add nearby points of interest
const pois = [
{ lat: 34.1016, lng: -118.3465, title: 'Hollywood Bowl', type: 'hotel', icon: 'theater-masks' },
{ lat: 34.0983, lng: -118.3701, title: 'Sunset Strip', type: 'restaurant', icon: 'music' },
{ lat: 34.1097, lng: -118.3544, title: 'Runyon Canyon Park', type: 'beach', icon: 'hiking' },
{ lat: 34.1169, lng: -118.3583, title: 'Canyon Country Store', type: 'restaurant', icon: 'shopping-bag' },
{ lat: 34.1314, lng: -118.3688, title: 'Mulholland Drive Viewpoint', type: 'activity', icon: 'mountain' }
];
const markerColors = {
restaurant: '#e74c3c',
hotel: '#3498db',
beach: '#2ecc71',
activity: '#f39c12'
};
pois.forEach(poi => {
const poiIcon = L.divIcon({
className: 'custom-poi-marker',
html: `<div style="background: ${markerColors[poi.type]}; color: white; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; border: 2px solid white; box-shadow: 0 2px 5px rgba(0,0,0,0.3);"><i class="fas fa-${poi.icon}" style="font-size: 10px;"></i></div>`,
iconSize: [24, 24],
iconAnchor: [12, 12],
popupAnchor: [0, -12]
});
const marker = L.marker([poi.lat, poi.lng], { icon: poiIcon }).addTo(map);
marker.bindPopup(`
<div style="padding: 5px; font-family: 'Inter', sans-serif;">
<strong style="color: ${markerColors[poi.type]}; font-size: 14px;">${poi.title}</strong>
</div>
`);
});
}
// Initialize map when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
if (document.getElementById('nosara-map')) {
initMap();
}
});
// Scroll Animations
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe elements for animation
document.addEventListener('DOMContentLoaded', () => {
const animateElements = document.querySelectorAll('.villa-card, .season-card, .rec-item, .heritage-item, .timeline-item');
animateElements.forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(30px)';
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(el);
});
});
// Stats Counter Animation
function animateCounter(element, target, duration = 2000) {
const start = 0;
const increment = target / (duration / 16);
let current = start;
const timer = setInterval(() => {
current += increment;
if (current >= target) {
element.textContent = target;
clearInterval(timer);
} else {
element.textContent = Math.floor(current);
}
}, 16);
}
// Trigger counter animation when stats section is visible
const statsSection = document.querySelector('.stats');
let statsAnimated = false;
const statsObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !statsAnimated) {
const statNumbers = document.querySelectorAll('.stat-item h3');
statNumbers.forEach(stat => {
const target = parseInt(stat.textContent);
if (!isNaN(target)) {
animateCounter(stat, target);
}
});
statsAnimated = true;
}
});
}, { threshold: 0.5 });
if (statsSection) {
statsObserver.observe(statsSection);
}
// Form Validation
const contactForm = document.querySelector('.contact-form');
if (contactForm) {
contactForm.addEventListener('submit', (e) => {
// Get form values
const formData = new FormData(contactForm);
const data = Object.fromEntries(formData);
// Date validation
if (data['preferred-date']) {
const preferredDate = new Date(data['preferred-date']);
const today = new Date();
today.setHours(0, 0, 0, 0);
if (preferredDate < today) {
alert('Preferred viewing date must be today or later');
e.preventDefault();
return;
}
}
// Form will submit to Formspree
});
}
// Parallax Effect for Hero Section
window.addEventListener('scroll', () => {
const scrolled = window.pageYOffset;
const heroText = document.querySelector('.hero-text');
if (heroText && scrolled < window.innerHeight) {
heroText.style.transform = `translateY(${scrolled * 0.5}px)`;
heroText.style.opacity = 1 - (scrolled / 500);
}
});
// Lazy Loading for Images
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
}
// Current year for footer
const currentYearElement = document.querySelector('.footer-bottom p');
if (currentYearElement) {
const currentYear = new Date().getFullYear();
const footerText = currentYearElement.textContent;
if (footerText && footerText.includes('2025')) {
currentYearElement.textContent = footerText.replace('2025', currentYear);
}
}
// Add active class to navigation based on scroll position
window.addEventListener('scroll', () => {
let current = '';
const sections = document.querySelectorAll('section[id]');
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (pageYOffset >= (sectionTop - 100)) {
current = section.getAttribute('id');
}
});
document.querySelectorAll('.nav-link').forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${current}`) {
link.classList.add('active');
}
});
});
console.log('🎸 2755 Carmar Drive - Laurel Canyon Luxury Rental - Website Loaded Successfully');