167 lines
5.0 KiB
JavaScript
167 lines
5.0 KiB
JavaScript
// ===================================
|
|
// APPLE-STYLE APP - FRONTEND
|
|
// ===================================
|
|
|
|
const API = {
|
|
products: '/api/products',
|
|
announcements: '/api/announcements',
|
|
settings: '/api/settings'
|
|
};
|
|
|
|
// Load settings and content
|
|
async function init() {
|
|
await loadSettings();
|
|
await loadAnnouncements();
|
|
await loadProducts();
|
|
setupScrollEffects();
|
|
setupRevealAnimations();
|
|
}
|
|
|
|
// Load site settings
|
|
async function loadSettings() {
|
|
try {
|
|
const res = await fetch(API.settings);
|
|
const json = await res.json();
|
|
if (json.ok && json.data) {
|
|
document.getElementById('heroTitle').textContent = json.data.heroTitle || 'Добро пожаловать';
|
|
document.getElementById('heroSubtitle').textContent = json.data.heroSubtitle || 'Инновации здесь';
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to load settings:', e);
|
|
}
|
|
}
|
|
|
|
// Load announcements
|
|
async function loadAnnouncements() {
|
|
try {
|
|
const res = await fetch(API.announcements);
|
|
const json = await res.json();
|
|
const bar = document.getElementById('announcementBar');
|
|
if (json.ok && json.data.length > 0) {
|
|
const ann = json.data[0];
|
|
bar.innerHTML = `${ann.title} — ${ann.content} <a href="#">Подробнее →</a>`;
|
|
} else {
|
|
bar.classList.add('hidden');
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to load announcements:', e);
|
|
document.getElementById('announcementBar').classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
// Load products
|
|
async function loadProducts() {
|
|
try {
|
|
const res = await fetch(API.products);
|
|
const json = await res.json();
|
|
const container = document.getElementById('productsContainer');
|
|
if (!json.ok || !json.data) {
|
|
container.innerHTML = '<p style="text-align:center;color:var(--text-secondary)">Нет продуктов</p>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = json.data.map((p, i) => `
|
|
<div class="product-card" style="transition-delay: ${i * 100}ms">
|
|
<img src="${p.image}" alt="${p.name}" class="product-image">
|
|
<div class="product-content">
|
|
<h3 class="product-name">${p.name}</h3>
|
|
<p class="product-tagline">${p.tagline}</p>
|
|
<p class="product-price">от ${p.price} $</p>
|
|
<div class="product-features">
|
|
${p.features.map(f => `<span class="feature-tag">${f}</span>`).join('')}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
|
|
// Trigger visibility check after load
|
|
setTimeout(checkProductVisibility, 100);
|
|
} catch (e) {
|
|
console.error('Failed to load products:', e);
|
|
document.getElementById('productsContainer').innerHTML = '<p style="text-align:center;color:var(--text-secondary)">Ошибка загрузки</p>';
|
|
}
|
|
}
|
|
|
|
// Scroll effects for navigation
|
|
function setupScrollEffects() {
|
|
const nav = document.getElementById('nav');
|
|
const heroBg = document.getElementById('heroBg');
|
|
const featuresBg = document.getElementById('featuresBg');
|
|
|
|
window.addEventListener('scroll', () => {
|
|
const scrolled = window.pageYOffset;
|
|
|
|
// Nav background
|
|
if (scrolled > 100) {
|
|
nav.classList.add('scrolled');
|
|
} else {
|
|
nav.classList.remove('scrolled');
|
|
}
|
|
|
|
// Hero parallax
|
|
if (scrolled < window.innerHeight) {
|
|
heroBg.style.transform = `translateY(${scrolled * 0.5}px) scale(${1 + scrolled * 0.0008})`;
|
|
}
|
|
|
|
// Features parallax
|
|
const featuresSection = document.getElementById('features');
|
|
if (featuresSection) {
|
|
const rect = featuresSection.getBoundingClientRect();
|
|
if (rect.top < window.innerHeight && rect.bottom > 0) {
|
|
const offset = (window.innerHeight - rect.top) * 0.2;
|
|
featuresBg.style.transform = `translateY(${offset}px)`;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Reveal animations on scroll
|
|
function setupRevealAnimations() {
|
|
const reveals = document.querySelectorAll('.reveal');
|
|
|
|
const checkReveal = () => {
|
|
reveals.forEach(el => {
|
|
const top = el.getBoundingClientRect().top;
|
|
const trigger = window.innerHeight * 0.85;
|
|
if (top < trigger) {
|
|
el.classList.add('active');
|
|
}
|
|
});
|
|
};
|
|
|
|
window.addEventListener('scroll', checkReveal);
|
|
checkReveal();
|
|
}
|
|
|
|
// Check product cards visibility
|
|
function checkProductVisibility() {
|
|
const cards = document.querySelectorAll('.product-card');
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('visible');
|
|
}
|
|
});
|
|
}, { threshold: 0.1 });
|
|
|
|
cards.forEach(card => observer.observe(card));
|
|
}
|
|
|
|
// Smooth scroll for anchor links
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function(e) {
|
|
const href = this.getAttribute('href');
|
|
if (href !== '#') {
|
|
e.preventDefault();
|
|
const target = document.querySelector(href);
|
|
if (target) {
|
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
init();
|
|
});
|