Initial AI-generated site
This commit is contained in:
@@ -1,132 +1,262 @@
|
||||
/**
|
||||
* 7XL – Interactive Features
|
||||
* Clean, accessible, respects user preferences
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Check for reduced motion preference
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
/**
|
||||
* Header scroll behavior
|
||||
*/
|
||||
function initHeader() {
|
||||
const header = document.querySelector('.site-header');
|
||||
if (!header) return;
|
||||
|
||||
let ticking = false;
|
||||
|
||||
function handleScroll() {
|
||||
if (!ticking) {
|
||||
requestAnimationFrame(() => {
|
||||
if (window.pageYOffset > 40) {
|
||||
header.classList.add('scrolled');
|
||||
} else {
|
||||
header.classList.remove('scrolled');
|
||||
}
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
handleScroll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mobile menu toggle
|
||||
*/
|
||||
function initMobileMenu() {
|
||||
const toggle = document.querySelector('.mobile-toggle');
|
||||
const mobileNav = document.querySelector('.mobile-nav');
|
||||
|
||||
if (!toggle || !mobileNav) return;
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
const isExpanded = toggle.getAttribute('aria-expanded') === 'true';
|
||||
toggle.setAttribute('aria-expanded', !isExpanded);
|
||||
mobileNav.hidden = isExpanded;
|
||||
|
||||
if (!isExpanded) {
|
||||
const firstLink = mobileNav.querySelector('a');
|
||||
if (firstLink) firstLink.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Close on link click
|
||||
mobileNav.querySelectorAll('a').forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
mobileNav.hidden = true;
|
||||
});
|
||||
});
|
||||
|
||||
// Close on escape
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && !mobileNav.hidden) {
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
mobileNav.hidden = true;
|
||||
toggle.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll reveal animations
|
||||
*/
|
||||
// ========================================
|
||||
// SCROLL REVEAL ANIMATIONS
|
||||
// ========================================
|
||||
function initScrollReveal() {
|
||||
if (prefersReducedMotion) return;
|
||||
|
||||
const revealElements = document.querySelectorAll(
|
||||
'.feature-card, .sport-card, .casino-game, .bonus-card, .bonus-main, .section-header'
|
||||
'.feature-card, .sport-item, .game-card, .bonus-card, .download-btn, .section-header'
|
||||
);
|
||||
|
||||
if (!revealElements.length) return;
|
||||
revealElements.forEach(el => {
|
||||
el.classList.add('reveal');
|
||||
});
|
||||
|
||||
revealElements.forEach(el => el.classList.add('reveal'));
|
||||
const observerOptions = {
|
||||
root: null,
|
||||
rootMargin: '0px 0px -100px 0px',
|
||||
threshold: 0.1
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
threshold: 0.15,
|
||||
rootMargin: '0px 0px -40px 0px'
|
||||
}
|
||||
);
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('active');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
revealElements.forEach(el => observer.observe(el));
|
||||
revealElements.forEach(el => {
|
||||
observer.observe(el);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Smooth scroll for anchor links
|
||||
*/
|
||||
// ========================================
|
||||
// HEADER SCROLL EFFECT
|
||||
// ========================================
|
||||
function initHeaderScroll() {
|
||||
const header = document.querySelector('.site-header');
|
||||
if (!header) return;
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const currentScroll = window.pageYOffset;
|
||||
|
||||
if (currentScroll > 100) {
|
||||
header.style.background = 'rgba(10, 22, 40, 0.95)';
|
||||
} else {
|
||||
header.style.background = 'rgba(10, 22, 40, 0.8)';
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// SPORTS TABS INTERACTION WITH KEYBOARD SUPPORT
|
||||
// ========================================
|
||||
function initSportsTabs() {
|
||||
const sportItems = document.querySelectorAll('.sport-item');
|
||||
const sportsList = document.querySelector('.sports-list');
|
||||
|
||||
if (!sportsList || sportItems.length === 0) return;
|
||||
|
||||
// Click handler
|
||||
sportItems.forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
activateTab(item, sportItems);
|
||||
});
|
||||
});
|
||||
|
||||
// Keyboard navigation
|
||||
sportsList.addEventListener('keydown', (e) => {
|
||||
const currentTab = document.activeElement;
|
||||
if (!currentTab.classList.contains('sport-item')) return;
|
||||
|
||||
const tabs = Array.from(sportItems);
|
||||
const currentIndex = tabs.indexOf(currentTab);
|
||||
let newIndex;
|
||||
|
||||
switch(e.key) {
|
||||
case 'ArrowDown':
|
||||
case 'ArrowLeft': // RTL: left is next
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % tabs.length;
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
case 'ArrowRight': // RTL: right is previous
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
newIndex = 0;
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
newIndex = tabs.length - 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
tabs[newIndex].focus();
|
||||
activateTab(tabs[newIndex], sportItems);
|
||||
});
|
||||
|
||||
function activateTab(selectedTab, allTabs) {
|
||||
allTabs.forEach(t => {
|
||||
t.classList.remove('active');
|
||||
t.setAttribute('aria-selected', 'false');
|
||||
t.setAttribute('tabindex', '-1');
|
||||
});
|
||||
selectedTab.classList.add('active');
|
||||
selectedTab.setAttribute('aria-selected', 'true');
|
||||
selectedTab.setAttribute('tabindex', '0');
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// ODDS BUTTON INTERACTION
|
||||
// ========================================
|
||||
function initOddsButtons() {
|
||||
const oddBtns = document.querySelectorAll('.odd-btn');
|
||||
|
||||
oddBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
oddBtns.forEach(b => b.classList.remove('highlighted'));
|
||||
btn.classList.add('highlighted');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// MOBILE MENU
|
||||
// ========================================
|
||||
function initMobileMenu() {
|
||||
const menuBtn = document.querySelector('.mobile-menu-btn');
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
|
||||
if (!menuBtn || !navLinks) return;
|
||||
|
||||
let mobileNav = null;
|
||||
|
||||
menuBtn.addEventListener('click', () => {
|
||||
const isOpen = menuBtn.classList.toggle('open');
|
||||
menuBtn.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
|
||||
if (isOpen) {
|
||||
// Create mobile menu
|
||||
mobileNav = document.createElement('nav');
|
||||
mobileNav.className = 'mobile-nav';
|
||||
mobileNav.id = 'mobile-nav';
|
||||
mobileNav.setAttribute('aria-label', 'תפריט ניווט ראשי');
|
||||
mobileNav.innerHTML = `<ul>${navLinks.innerHTML}</ul>`;
|
||||
mobileNav.style.cssText = `
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
inset-inline: 0;
|
||||
background: rgba(10, 22, 40, 0.98);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 2rem;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
z-index: 999;
|
||||
`;
|
||||
|
||||
const links = mobileNav.querySelectorAll('a');
|
||||
links.forEach((link, i) => {
|
||||
link.style.cssText = `
|
||||
display: block;
|
||||
padding: 1rem 0;
|
||||
font-size: 1.125rem;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.05);
|
||||
opacity: 0;
|
||||
animation: mobileSlideIn 0.3s ease ${i * 0.05}s forwards;
|
||||
`;
|
||||
});
|
||||
|
||||
// Add animation keyframes
|
||||
if (!document.querySelector('#mobile-menu-styles')) {
|
||||
const style = document.createElement('style');
|
||||
style.id = 'mobile-menu-styles';
|
||||
style.textContent = `
|
||||
@keyframes mobileSlideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
document.body.appendChild(mobileNav);
|
||||
|
||||
// Close on link click
|
||||
links.forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
menuBtn.classList.remove('open');
|
||||
menuBtn.setAttribute('aria-expanded', 'false');
|
||||
if (mobileNav) {
|
||||
mobileNav.remove();
|
||||
mobileNav = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Focus first link
|
||||
if (links.length > 0) {
|
||||
links[0].focus();
|
||||
}
|
||||
} else {
|
||||
if (mobileNav) {
|
||||
mobileNav.remove();
|
||||
mobileNav = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Close menu on escape
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && mobileNav) {
|
||||
menuBtn.classList.remove('open');
|
||||
menuBtn.setAttribute('aria-expanded', 'false');
|
||||
mobileNav.remove();
|
||||
mobileNav = null;
|
||||
menuBtn.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// SMOOTH SCROLL FOR ANCHOR LINKS
|
||||
// ========================================
|
||||
function initSmoothScroll() {
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function(e) {
|
||||
const targetId = this.getAttribute('href');
|
||||
if (targetId === '#') return;
|
||||
const href = this.getAttribute('href');
|
||||
if (href === '#') return;
|
||||
|
||||
const target = document.querySelector(targetId);
|
||||
const target = document.querySelector(href);
|
||||
if (target) {
|
||||
e.preventDefault();
|
||||
|
||||
const header = document.querySelector('.site-header');
|
||||
const offset = header ? header.offsetHeight + 16 : 80;
|
||||
const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - offset;
|
||||
const headerHeight = header ? header.offsetHeight : 0;
|
||||
const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - headerHeight - 20;
|
||||
|
||||
window.scrollTo({
|
||||
top: targetPosition,
|
||||
behavior: prefersReducedMotion ? 'auto' : 'smooth'
|
||||
});
|
||||
|
||||
|
||||
// Set focus for accessibility
|
||||
target.setAttribute('tabindex', '-1');
|
||||
target.focus({ preventScroll: true });
|
||||
@@ -135,132 +265,154 @@
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause animations when not in view (performance)
|
||||
*/
|
||||
function initAnimationPausing() {
|
||||
// ========================================
|
||||
// PARALLAX EFFECTS
|
||||
// ========================================
|
||||
function initParallax() {
|
||||
if (prefersReducedMotion) return;
|
||||
|
||||
const roulette = document.querySelector('.roulette-wheel');
|
||||
if (!roulette) return;
|
||||
|
||||
// Add animation via JS so it only runs when visible
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.roulette-wheel.animate {
|
||||
animation: spin 15s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
roulette.classList.add('animate');
|
||||
} else {
|
||||
roulette.classList.remove('animate');
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.3 }
|
||||
);
|
||||
observer.observe(roulette);
|
||||
}
|
||||
|
||||
/**
|
||||
* Floating cards animation (CSS-only when visible)
|
||||
*/
|
||||
function initFloatingCards() {
|
||||
if (prefersReducedMotion) return;
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-12px); }
|
||||
}
|
||||
.floating-card.animate {
|
||||
animation: float 5s ease-in-out infinite;
|
||||
}
|
||||
.floating-card.card-2.animate {
|
||||
animation-delay: 1.5s;
|
||||
}
|
||||
.floating-card.card-3.animate {
|
||||
animation-delay: 3s;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
const cards = document.querySelectorAll('.floating-card');
|
||||
if (!cards.length) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach(entry => {
|
||||
cards.forEach(card => {
|
||||
if (entry.isIntersecting) {
|
||||
card.classList.add('animate');
|
||||
} else {
|
||||
card.classList.remove('animate');
|
||||
}
|
||||
const glows = document.querySelectorAll('.glow');
|
||||
|
||||
let ticking = false;
|
||||
window.addEventListener('scroll', () => {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
const scrollY = window.pageYOffset;
|
||||
|
||||
glows.forEach((glow, index) => {
|
||||
const speed = 0.05 + (index * 0.02);
|
||||
glow.style.transform = `translateY(${scrollY * speed}px)`;
|
||||
});
|
||||
|
||||
ticking = false;
|
||||
});
|
||||
},
|
||||
{ threshold: 0.2 }
|
||||
);
|
||||
|
||||
const heroVisual = document.querySelector('.hero-visual');
|
||||
if (heroVisual) observer.observe(heroVisual);
|
||||
ticking = true;
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Live pulse animation
|
||||
*/
|
||||
function initLivePulse() {
|
||||
// ========================================
|
||||
// LIVE ELEMENTS ANIMATION
|
||||
// ========================================
|
||||
function initLiveElements() {
|
||||
if (prefersReducedMotion) return;
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.4); opacity: 0.6; }
|
||||
}
|
||||
.live-pulse {
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
.live-indicator {
|
||||
animation: blink 1.2s ease-in-out infinite;
|
||||
}
|
||||
@keyframes blink {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
// Only animate when elements are visible
|
||||
const liveMatchCard = document.querySelector('.live-match-card');
|
||||
if (!liveMatchCard) return;
|
||||
|
||||
let animationInterval = null;
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
// Start animations
|
||||
animationInterval = setInterval(() => {
|
||||
// Simulate odds changes
|
||||
const oddValues = document.querySelectorAll('.odd-value');
|
||||
const randomOdd = oddValues[Math.floor(Math.random() * oddValues.length)];
|
||||
if (randomOdd) {
|
||||
const currentValue = parseFloat(randomOdd.textContent);
|
||||
if (!isNaN(currentValue)) {
|
||||
const change = (Math.random() - 0.5) * 0.1;
|
||||
const newValue = Math.max(1.01, currentValue + change).toFixed(2);
|
||||
|
||||
randomOdd.style.transition = 'color 0.3s ease';
|
||||
randomOdd.style.color = change > 0 ? '#22c55e' : '#ef4444';
|
||||
randomOdd.textContent = newValue;
|
||||
|
||||
setTimeout(() => {
|
||||
randomOdd.style.color = '';
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}, 4000);
|
||||
} else {
|
||||
// Stop animations when not visible
|
||||
if (animationInterval) {
|
||||
clearInterval(animationInterval);
|
||||
animationInterval = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.1 });
|
||||
|
||||
observer.observe(liveMatchCard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all features
|
||||
*/
|
||||
// ========================================
|
||||
// BUTTON RIPPLE EFFECT
|
||||
// ========================================
|
||||
function initRippleEffect() {
|
||||
if (prefersReducedMotion) return;
|
||||
|
||||
// Add ripple animation styles once
|
||||
if (!document.querySelector('#ripple-styles')) {
|
||||
const style = document.createElement('style');
|
||||
style.id = 'ripple-styles';
|
||||
style.textContent = `
|
||||
@keyframes ripple {
|
||||
to {
|
||||
transform: scale(4);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.btn, .download-btn, .support-btn {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
const buttons = document.querySelectorAll('.btn, .download-btn, .support-btn');
|
||||
|
||||
buttons.forEach(btn => {
|
||||
btn.addEventListener('click', function(e) {
|
||||
const rect = this.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
const ripple = document.createElement('span');
|
||||
ripple.style.cssText = `
|
||||
position: absolute;
|
||||
background: rgba(255,255,255,0.3);
|
||||
border-radius: 50%;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
left: ${x - 50}px;
|
||||
top: ${y - 50}px;
|
||||
transform: scale(0);
|
||||
animation: ripple 0.6s ease-out;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
this.appendChild(ripple);
|
||||
|
||||
setTimeout(() => ripple.remove(), 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// INITIALIZE ALL
|
||||
// ========================================
|
||||
function init() {
|
||||
initHeader();
|
||||
initMobileMenu();
|
||||
initScrollReveal();
|
||||
initHeaderScroll();
|
||||
initSportsTabs();
|
||||
initOddsButtons();
|
||||
initMobileMenu();
|
||||
initSmoothScroll();
|
||||
initAnimationPausing();
|
||||
initFloatingCards();
|
||||
initLivePulse();
|
||||
initParallax();
|
||||
initLiveElements();
|
||||
initRippleEffect();
|
||||
}
|
||||
|
||||
// Run on DOM ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user