Initial import of HarborSmith website
Some checks failed
build-website / build (push) Failing after 1m2s
Some checks failed
build-website / build (push) Failing after 1m2s
This commit is contained in:
350
website-mockups/js/voyage-layout.js
Normal file
350
website-mockups/js/voyage-layout.js
Normal file
@@ -0,0 +1,350 @@
|
||||
// Voyage Layout JavaScript - Smooth, Interactive, Engaging
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Theme is now gold by default - no switcher needed
|
||||
|
||||
// Navigation scroll effect and logo switching
|
||||
const nav = document.getElementById('voyageNav');
|
||||
const navLogo = document.getElementById('navLogo');
|
||||
const navBrandText = document.querySelector('.nav-brand span');
|
||||
let lastScroll = 0;
|
||||
|
||||
// Set initial logo state
|
||||
if (navLogo) {
|
||||
navLogo.src = 'HARBOR-SMITH-white.png';
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const currentScroll = window.pageYOffset;
|
||||
|
||||
if (currentScroll > 50) {
|
||||
nav.classList.add('scrolled');
|
||||
// Switch to navy logo when scrolled (white background)
|
||||
if (navLogo) {
|
||||
navLogo.src = 'HARBOR-SMITH_navy.png';
|
||||
navLogo.alt = 'Harbor Smith Navy Logo';
|
||||
}
|
||||
// Also change text color to match
|
||||
if (navBrandText) {
|
||||
navBrandText.style.color = '#1e3a5f';
|
||||
}
|
||||
} else {
|
||||
nav.classList.remove('scrolled');
|
||||
// Switch to white logo when at top (over video)
|
||||
if (navLogo) {
|
||||
navLogo.src = 'HARBOR-SMITH-white.png';
|
||||
navLogo.alt = 'Harbor Smith White Logo';
|
||||
}
|
||||
// Reset text color to white
|
||||
if (navBrandText) {
|
||||
navBrandText.style.color = 'white';
|
||||
}
|
||||
}
|
||||
|
||||
lastScroll = currentScroll;
|
||||
});
|
||||
|
||||
// Smooth scroll for navigation links
|
||||
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'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Parallax effect for hero video
|
||||
const heroSection = document.getElementById('heroSection');
|
||||
const heroVideo = document.querySelector('.hero-video-container');
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const scrolled = window.pageYOffset;
|
||||
const rate = scrolled * 0.5;
|
||||
|
||||
if (heroVideo) {
|
||||
heroVideo.style.transform = `translateY(${rate}px)`;
|
||||
}
|
||||
});
|
||||
|
||||
// Fleet Carousel
|
||||
const yachtCards = document.querySelectorAll('.yacht-card');
|
||||
const dots = document.querySelectorAll('.dot');
|
||||
const prevBtn = document.querySelector('.fleet-prev');
|
||||
const nextBtn = document.querySelector('.fleet-next');
|
||||
let currentYacht = 0;
|
||||
|
||||
function showYacht(index) {
|
||||
yachtCards.forEach(card => card.classList.remove('active'));
|
||||
dots.forEach(dot => dot.classList.remove('active'));
|
||||
|
||||
yachtCards[index].classList.add('active');
|
||||
dots[index].classList.add('active');
|
||||
}
|
||||
|
||||
if (prevBtn && nextBtn) {
|
||||
prevBtn.addEventListener('click', () => {
|
||||
currentYacht = (currentYacht - 1 + yachtCards.length) % yachtCards.length;
|
||||
showYacht(currentYacht);
|
||||
});
|
||||
|
||||
nextBtn.addEventListener('click', () => {
|
||||
currentYacht = (currentYacht + 1) % yachtCards.length;
|
||||
showYacht(currentYacht);
|
||||
});
|
||||
}
|
||||
|
||||
dots.forEach((dot, index) => {
|
||||
dot.addEventListener('click', () => {
|
||||
currentYacht = index;
|
||||
showYacht(currentYacht);
|
||||
});
|
||||
});
|
||||
|
||||
// Auto-rotate fleet carousel
|
||||
setInterval(() => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
currentYacht = (currentYacht + 1) % yachtCards.length;
|
||||
showYacht(currentYacht);
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
// CTA Button interactions
|
||||
document.querySelectorAll('.btn-primary-warm, .btn-secondary-warm').forEach(btn => {
|
||||
btn.addEventListener('click', function(e) {
|
||||
// Create ripple effect
|
||||
const ripple = document.createElement('span');
|
||||
ripple.classList.add('ripple');
|
||||
this.appendChild(ripple);
|
||||
|
||||
const rect = this.getBoundingClientRect();
|
||||
const size = Math.max(rect.width, rect.height);
|
||||
const x = e.clientX - rect.left - size / 2;
|
||||
const y = e.clientY - rect.top - size / 2;
|
||||
|
||||
ripple.style.width = ripple.style.height = size + 'px';
|
||||
ripple.style.left = x + 'px';
|
||||
ripple.style.top = y + 'px';
|
||||
|
||||
setTimeout(() => ripple.remove(), 600);
|
||||
});
|
||||
});
|
||||
|
||||
// Add ripple styles
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.ripple {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
transform: scale(0);
|
||||
animation: ripple 0.6s ease-out;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
to {
|
||||
transform: scale(4);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary-warm,
|
||||
.btn-secondary-warm {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Animate elements on scroll
|
||||
const observerOptions = {
|
||||
threshold: 0.1,
|
||||
rootMargin: '0px 0px -100px 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.querySelectorAll('.welcome-content, .story-card, .booking-card').forEach(el => {
|
||||
el.style.opacity = '0';
|
||||
el.style.transform = 'translateY(30px)';
|
||||
el.style.transition = 'all 0.6s ease';
|
||||
observer.observe(el);
|
||||
});
|
||||
|
||||
// Interactive booking cards
|
||||
document.querySelectorAll('.booking-card').forEach(card => {
|
||||
card.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'translateY(-10px) scale(1.02)';
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', function() {
|
||||
if (this.classList.contains('featured')) {
|
||||
this.style.transform = 'scale(1.05)';
|
||||
} else {
|
||||
this.style.transform = 'translateY(0) scale(1)';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Phone number click handler
|
||||
document.querySelectorAll('.btn-booking.primary').forEach(btn => {
|
||||
if (btn.textContent.includes('415')) {
|
||||
btn.addEventListener('click', () => {
|
||||
window.location.href = 'tel:4155550123';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Video optimization - pause when not visible
|
||||
const video = document.querySelector('.hero-video');
|
||||
if (video) {
|
||||
const videoObserver = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
video.play();
|
||||
} else {
|
||||
video.pause();
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.25 });
|
||||
|
||||
videoObserver.observe(video);
|
||||
}
|
||||
|
||||
// Story cards hover effect
|
||||
document.querySelectorAll('.story-card').forEach(card => {
|
||||
const img = card.querySelector('img');
|
||||
|
||||
card.addEventListener('mouseenter', () => {
|
||||
if (img) {
|
||||
img.style.transform = 'scale(1.1)';
|
||||
img.style.transition = 'transform 0.6s ease';
|
||||
}
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', () => {
|
||||
if (img) {
|
||||
img.style.transform = 'scale(1)';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Dynamic trust badge counter - starts from 0
|
||||
// Target the white text span specifically (the second span in trust-badge)
|
||||
const trustBadge = document.querySelector('.trust-badge > span:last-child');
|
||||
if (trustBadge) {
|
||||
let count = 0;
|
||||
const targetCount = 100;
|
||||
const duration = 2000; // 2 seconds
|
||||
const steps = 50;
|
||||
const increment = targetCount / steps;
|
||||
const stepDuration = duration / steps;
|
||||
|
||||
// Start with 0
|
||||
trustBadge.textContent = `Trusted by 0+ seafarers`;
|
||||
|
||||
const counter = setInterval(() => {
|
||||
count += increment;
|
||||
if (count >= targetCount) {
|
||||
count = targetCount;
|
||||
clearInterval(counter);
|
||||
trustBadge.textContent = `Trusted by 100+ seafarers`;
|
||||
} else {
|
||||
trustBadge.textContent = `Trusted by ${Math.floor(count)}+ seafarers`;
|
||||
}
|
||||
}, stepDuration);
|
||||
}
|
||||
|
||||
// Remove any duplicate red text that might exist
|
||||
const allSpans = document.querySelectorAll('.hero-content span');
|
||||
allSpans.forEach(span => {
|
||||
// Check if this is the red duplicate text (not inside trust-badge)
|
||||
if (span.textContent.includes('Trusted by') &&
|
||||
span.textContent.includes('adventurers') &&
|
||||
!span.closest('.trust-badge')) {
|
||||
span.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Smooth hover for navigation links
|
||||
document.querySelectorAll('.nav-link').forEach(link => {
|
||||
link.addEventListener('mouseenter', function() {
|
||||
this.style.transform = 'translateY(-2px)';
|
||||
});
|
||||
|
||||
link.addEventListener('mouseleave', function() {
|
||||
this.style.transform = 'translateY(0)';
|
||||
});
|
||||
});
|
||||
|
||||
// Mobile menu (would need to add hamburger menu for production)
|
||||
const mobileMenuBtn = document.createElement('button');
|
||||
mobileMenuBtn.className = 'mobile-menu-btn';
|
||||
mobileMenuBtn.innerHTML = '<span></span><span></span><span></span>';
|
||||
mobileMenuBtn.style.cssText = `
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
`;
|
||||
|
||||
// Add mobile menu styles
|
||||
const mobileStyles = document.createElement('style');
|
||||
mobileStyles.textContent = `
|
||||
@media (max-width: 768px) {
|
||||
.mobile-menu-btn {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.mobile-menu-btn span {
|
||||
width: 24px;
|
||||
height: 2px;
|
||||
background: white;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.voyage-nav.scrolled .mobile-menu-btn span {
|
||||
background: var(--primary-blue);
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(mobileStyles);
|
||||
|
||||
// Add mobile menu button to nav
|
||||
const navContainer = document.querySelector('.nav-container');
|
||||
if (navContainer && window.innerWidth <= 768) {
|
||||
navContainer.appendChild(mobileMenuBtn);
|
||||
}
|
||||
|
||||
// Loading animation for images
|
||||
document.querySelectorAll('img').forEach(img => {
|
||||
// Check if image is already loaded
|
||||
if (img.complete && img.naturalHeight !== 0) {
|
||||
img.style.opacity = '1';
|
||||
} else {
|
||||
img.style.opacity = '0';
|
||||
img.addEventListener('load', function() {
|
||||
this.style.opacity = '1';
|
||||
});
|
||||
}
|
||||
img.style.transition = 'opacity 0.6s ease';
|
||||
});
|
||||
|
||||
console.log('Voyage layout initialized successfully!');
|
||||
});
|
||||
Reference in New Issue
Block a user