Files
smartsoltech_site/smartsoltech/static/assets/js/project-detail.js
Andrey K. Choi b51d79c5a1
Some checks failed
continuous-integration/drone/push Build is failing
Implement modern media gallery with enhanced features
- Fix CSS loading issue in project_detail.html template
- Add comprehensive ModernMediaGallery JavaScript class with touch navigation
- Implement glassmorphism design with backdrop-filter effects
- Add responsive breakpoint system for mobile devices
- Include embedded critical CSS styles for gallery functionality
- Add technology sidebar with vertical list layout and hover effects
- Support for images, videos, and embedded content with thumbnails
- Add lightbox integration and media type badges
- Implement progress bar and thumbnail navigation
- Add keyboard controls (arrow keys) and touch swipe gestures
- Include supplementary styles for video/embed placeholders
- Fix template block naming compatibility (extra_css → extra_styles)
2025-11-26 18:52:07 +09:00

227 lines
7.4 KiB
JavaScript

// Modern Project Detail Page Enhancements
document.addEventListener('DOMContentLoaded', function() {
// Animate counter numbers
function animateCounters() {
const counters = document.querySelectorAll('.stat-number[data-target]');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const counter = entry.target;
const target = parseInt(counter.dataset.target);
const duration = 2000; // 2 seconds
const step = target / (duration / 16); // 60fps
let current = 0;
const timer = setInterval(() => {
current += step;
counter.textContent = Math.floor(current);
if (current >= target) {
counter.textContent = target;
clearInterval(timer);
}
}, 16);
observer.unobserve(counter);
}
});
}, {
threshold: 0.5
});
counters.forEach(counter => observer.observe(counter));
}
// Scroll-triggered animations
function initScrollAnimations() {
const animatedElements = document.querySelectorAll('.content-section, .tech-item, .info-item');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}, index * 100);
}
});
}, {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
});
animatedElements.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);
});
}
// Share functionality
function initShareButton() {
const shareBtn = document.querySelector('.share-btn');
if (shareBtn) {
shareBtn.addEventListener('click', async function() {
const projectTitle = document.querySelector('.hero-title').textContent;
const url = window.location.href;
if (navigator.share) {
try {
await navigator.share({
title: projectTitle,
text: `Посмотрите на этот проект: ${projectTitle}`,
url: url
});
} catch (err) {
console.log('Sharing failed:', err);
fallbackShare(url);
}
} else {
fallbackShare(url);
}
});
}
}
function fallbackShare(url) {
// Copy to clipboard as fallback
if (navigator.clipboard) {
navigator.clipboard.writeText(url).then(() => {
showToast('Ссылка скопирована в буфер обмена!');
});
}
}
function showToast(message) {
// Create toast notification
const toast = document.createElement('div');
toast.className = 'toast-notification';
toast.textContent = message;
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
padding: 1rem 1.5rem;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
z-index: 10000;
transform: translateX(400px);
transition: transform 0.3s ease;
`;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.transform = 'translateX(0)';
}, 100);
setTimeout(() => {
toast.style.transform = 'translateX(400px)';
setTimeout(() => document.body.removeChild(toast), 300);
}, 3000);
}
// Tech item hover effects
function initTechInteractions() {
const techItems = document.querySelectorAll('.tech-item');
techItems.forEach(item => {
item.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-8px) scale(1.02)';
});
item.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(-5px) scale(1)';
});
});
}
// Parallax effect for hero background
function initParallaxEffect() {
const heroBackground = document.querySelector('.hero-pattern');
if (!heroBackground) return;
let ticking = false;
function updateParallax() {
const scrolled = window.pageYOffset;
const rate = scrolled * -0.3;
heroBackground.style.transform = `translateY(${rate}px)`;
ticking = false;
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(updateParallax);
ticking = true;
}
}
window.addEventListener('scroll', requestTick);
}
// Smooth scroll for internal links
function initSmoothScroll() {
const links = document.querySelectorAll('a[href^="#"]');
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetSection = document.querySelector(targetId);
if (targetSection) {
targetSection.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
// Initialize all enhancements
animateCounters();
initScrollAnimations();
initShareButton();
initTechInteractions();
initParallaxEffect();
initSmoothScroll();
// Add loading class removal after page load
window.addEventListener('load', function() {
document.body.classList.add('page-loaded');
});
});
// CSS for page loading animation
const loadingStyles = `
body:not(.page-loaded) .project-hero {
opacity: 0;
transform: translateY(50px);
transition: opacity 0.8s ease, transform 0.8s ease;
}
body.page-loaded .project-hero {
opacity: 1;
transform: translateY(0);
}
.toast-notification {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 0.9rem;
font-weight: 500;
}
`;
// Inject loading styles
const styleSheet = document.createElement('style');
styleSheet.textContent = loadingStyles;
document.head.appendChild(styleSheet);