Spaces:
Running
Running
| // Portfolio Data - In a real app, this would come from an API | |
| const portfolioData = { | |
| categories: [ | |
| { | |
| id: 'ads', | |
| title: 'Commercial Ads', | |
| description: 'Professional advertising campaigns and promotional content', | |
| videos: [ | |
| { | |
| id: 1, | |
| title: 'Tech Startup Launch', | |
| thumbnail: 'http://static.photos/technology/320x240/1', | |
| duration: '2:30', | |
| views: '15.2K', | |
| description: 'Dynamic product launch campaign for innovative tech company', | |
| videoUrl: '#', | |
| tags: ['Technology', 'Corporate', 'Branding'] | |
| }, | |
| { | |
| id: 2, | |
| title: 'Fashion Brand Campaign', | |
| thumbnail: 'http://static.photos/retail/320x240/2', | |
| duration: '1:45', | |
| views: '23.7K', | |
| description: 'High-fashion brand identity video for luxury clothing line', | |
| videoUrl: '#', | |
| tags: ['Fashion', 'Luxury', 'Lifestyle'] | |
| } | |
| ] | |
| }, | |
| { | |
| id: 'music-videos', | |
| title: 'Music Videos', | |
| description: 'Creative visual storytelling for musical artists', | |
| videos: [ | |
| { | |
| id: 3, | |
| title: 'Indie Rock Journey', | |
| thumbnail: 'http://static.photos/music/320x240/3', | |
| duration: '4:15', | |
| views: '89.4K', | |
| description: 'Narrative music video following band tour experience', | |
| videoUrl: '#', | |
| tags: ['Music', 'Tour', 'Narrative'] | |
| }, | |
| { | |
| id: 4, | |
| title: 'Electronic Visualizer', | |
| thumbnail: 'http://static.photos/abstract/320x240/4', | |
| duration: '3:20', | |
| views: '45.1K', | |
| description: 'Abstract visual experience for electronic music track', | |
| videoUrl: '#', | |
| tags: ['Electronic', 'Abstract', 'Visual'] | |
| } | |
| ] | |
| }, | |
| { | |
| id: 'weddings', | |
| title: 'Wedding Films', | |
| description: 'Emotional storytelling for life\'s most precious moments', | |
| videos: [ | |
| { | |
| id: 5, | |
| title: 'Mountain Wedding', | |
| thumbnail: 'http://static.photos/nature/320x240/5', | |
| duration: '8:30', | |
| views: '12.8K', | |
| description: 'Breathtaking outdoor wedding ceremony and celebration', | |
| videoUrl: '#', | |
| tags: ['Outdoor', 'Romantic', 'Nature'] | |
| }, | |
| { | |
| id: 6, | |
| title: 'Urban Celebration', | |
| thumbnail: 'http://static.photos/cityscape/320x240/6', | |
| duration: '6:45', | |
| views: '9.3K', | |
| description: 'Modern city wedding with rooftop reception', | |
| videoUrl: '#', | |
| tags: ['Urban', 'Modern', 'Celebration'] | |
| } | |
| ] | |
| }, | |
| { | |
| id: 'travel', | |
| title: 'Travel Content', | |
| description: 'Capturing the beauty and essence of destinations worldwide', | |
| videos: [ | |
| { | |
| id: 7, | |
| title: 'Japanese Adventure', | |
| thumbnail: 'http://static.photos/travel/320x240/7', | |
| duration: '5:10', | |
| views: '67.2K', | |
| description: 'Cultural exploration through Japan\'s most iconic locations', | |
| videoUrl: '#', | |
| tags: ['Japan', 'Culture', 'Adventure'] | |
| }, | |
| { | |
| id: 8, | |
| title: 'European Summer', | |
| thumbnail: 'http://static.photos/aerial/320x240/8', | |
| duration: '4:45', | |
| views: '52.9K', | |
| description: 'Summer journey across Mediterranean coastlines', | |
| videoUrl: '#', | |
| tags: ['Europe', 'Summer', 'Coastal'] | |
| } | |
| ] | |
| }, | |
| { | |
| id: 'corporate', | |
| title: 'Corporate Videos', | |
| description: 'Professional business communication and brand storytelling', | |
| videos: [ | |
| { | |
| id: 9, | |
| title: 'Company Culture', | |
| thumbnail: 'http://static.photos/office/320x240/9', | |
| duration: '3:15', | |
| views: '8.7K', | |
| description: 'Internal culture video showcasing team values and environment', | |
| videoUrl: '#', | |
| tags: ['Corporate', 'Culture', 'Internal'] | |
| }, | |
| { | |
| id: 10, | |
| title: 'Annual Report', | |
| thumbnail: 'http://static.photos/finance/320x240/10', | |
| duration: '2:50', | |
| views: '6.3K', | |
| description: 'Animated financial report with company achievements', | |
| videoUrl: '#', | |
| tags: ['Finance', 'Report', 'Animation'] | |
| } | |
| ] | |
| }, | |
| { | |
| id: 'reels', | |
| title: 'Social Media Reels', | |
| description: 'Engaging short-form content optimized for social platforms', | |
| videos: [ | |
| { | |
| id: 11, | |
| title: 'Viral Challenge', | |
| thumbnail: 'http://static.photos/gaming/320x240/11', | |
| duration: '0:45', | |
| views: '245K', | |
| description: 'Trending dance challenge with creative transitions', | |
| videoUrl: '#', | |
| tags: ['Trending', 'Dance', 'Viral'] | |
| }, | |
| { | |
| id: 12, | |
| title: 'Brand Awareness', | |
| thumbnail: 'http://static.photos/minimal/320x240/12', | |
| duration: '0:30', | |
| views: '189K', | |
| description: 'Quick brand message with engaging visual effects', | |
| videoUrl: '#', | |
| tags: ['Brand', 'Awareness', 'Effects'] | |
| } | |
| ] | |
| }, | |
| { | |
| id: 'documentary', | |
| title: 'Documentary', | |
| description: 'In-depth storytelling and real-life narratives', | |
| videos: [ | |
| { | |
| id: 13, | |
| title: 'Environmental Impact', | |
| thumbnail: 'http://static.photos/science/320x240/13', | |
| duration: '12:30', | |
| views: '34.6K', | |
| description: 'Climate change documentary featuring expert interviews', | |
| videoUrl: '#', | |
| tags: ['Environment', 'Science', 'Interview'] | |
| }, | |
| { | |
| id: 14, | |
| title: 'Community Stories', | |
| thumbnail: 'http://static.photos/people/320x240/14', | |
| duration: '15:45', | |
| views: '28.9K', | |
| description: 'Local community resilience and cultural preservation', | |
| videoUrl: '#', | |
| tags: ['Community', 'Culture', 'Resilience'] | |
| } | |
| ] | |
| } | |
| ] | |
| }; | |
| // Initialize the portfolio | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initializePortfolio(); | |
| setupEventListeners(); | |
| }); | |
| function initializePortfolio() { | |
| const container = document.getElementById('portfolio-container'); | |
| portfolioData.categories.forEach(category => { | |
| const categorySection = createCategorySection(category); | |
| container.appendChild(categorySection); | |
| }); | |
| } | |
| function createCategorySection(category) { | |
| const section = document.createElement('section'); | |
| section.className = 'mb-12 fade-in-up'; | |
| section.innerHTML = ` | |
| <div class="mb-6"> | |
| <h2 class="text-3xl font-bold text-white mb-2">${category.title}</h2> | |
| <p class="text-gray-400">${category.description}</p> | |
| </div> | |
| <div class="category-row flex space-x-4 overflow-x-auto pb-6"> | |
| ${category.videos.map(video => ` | |
| <div class="video-thumbnail flex-shrink-0 w-64 bg-gray-800 rounded-lg overflow-hidden cursor-pointer group" | |
| data-video-id="${video.id}"> | |
| <div class="relative"> | |
| <img src="${video.thumbnail}" alt="${video.title}" class="w-full h-36 object-cover"> | |
| <div class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-30 transition-all duration-300"></div> | |
| <div class="absolute bottom-2 right-2 bg-black bg-opacity-75 px-2 py-1 rounded text-xs"> | |
| ${video.duration} | |
| </div> | |
| </div> | |
| <div class="p-4"> | |
| <h3 class="font-semibold text-white mb-1 group-hover:text-primary-400 transition-colors"> | |
| ${video.title} | |
| </h3> | |
| <p class="text-gray-400 text-sm mb-2">${video.views} views</p> | |
| <div class="flex flex-wrap gap-1"> | |
| ${video.tags.map(tag => ` | |
| <span class="bg-gray-700 text-gray-300 px-2 py-1 rounded text-xs">${tag}</span> | |
| `).join('')} | |
| </div> | |
| </div> | |
| </div> | |
| `).join('')} | |
| </div> | |
| `; | |
| return section; | |
| } | |
| function setupEventListeners() { | |
| // Video thumbnail click handler | |
| document.addEventListener('click', function(e) { | |
| const thumbnail = e.target.closest('.video-thumbnail'); | |
| if (thumbnail) { | |
| const videoId = thumbnail.getAttribute('data-video-id'); | |
| openVideoModal(videoId); | |
| } | |
| }); | |
| // Navigation scroll behavior | |
| window.addEventListener('scroll', throttle(handleScroll, 100)); | |
| } | |
| function openVideoModal(videoId) { | |
| const modal = document.querySelector('custom-video-modal'); | |
| if (modal) { | |
| modal.setAttribute('video-id', videoId); | |
| modal.setAttribute('open', 'true'); | |
| } | |
| } | |
| function handleScroll() { | |
| const nav = document.querySelector('custom-navigation'); | |
| if (window.scrollY > 100) { | |
| nav.setAttribute('scrolled', 'true'); | |
| } else { | |
| nav.setAttribute('scrolled', 'false'); | |
| } | |
| } | |
| // Utility function to throttle events | |
| function throttle(func, limit) { | |
| let inThrottle; | |
| return function() { | |
| const args = arguments; | |
| const context = this; | |
| if (!inThrottle) { | |
| func.apply(context, args); | |
| inThrottle = true; | |
| setTimeout(() => inThrottle = false, limit); | |
| } | |
| } | |
| } | |
| // Export for use in components if needed | |
| window.portfolioData = portfolioData; |