|
84 | 84 | margin-bottom: 40px; |
85 | 85 | } |
86 | 86 |
|
| 87 | + .hero-slide { |
| 88 | + position: absolute; |
| 89 | + top: 0; |
| 90 | + left: 0; |
| 91 | + width: 100%; |
| 92 | + height: 100%; |
| 93 | + opacity: 0; |
| 94 | + transition: opacity 0.8s ease-in-out; |
| 95 | + pointer-events: none; |
| 96 | + } |
| 97 | + |
| 98 | + .hero-slide.active { |
| 99 | + opacity: 1; |
| 100 | + pointer-events: auto; |
| 101 | + } |
| 102 | + |
87 | 103 | .hero-bg { |
88 | 104 | position: absolute; |
89 | 105 | top: 0; |
|
96 | 112 | transition: transform 8s ease-out; |
97 | 113 | } |
98 | 114 |
|
99 | | - .hero:hover .hero-bg { |
| 115 | + .hero-slide.active .hero-bg { |
100 | 116 | transform: scale(1.1); |
101 | 117 | } |
102 | 118 |
|
|
118 | 134 | z-index: 10; |
119 | 135 | } |
120 | 136 |
|
| 137 | + /* Slideshow Navigation */ |
| 138 | + .hero-nav { |
| 139 | + position: absolute; |
| 140 | + bottom: 20px; |
| 141 | + left: 50%; |
| 142 | + transform: translateX(-50%); |
| 143 | + display: flex; |
| 144 | + gap: 10px; |
| 145 | + z-index: 20; |
| 146 | + } |
| 147 | + |
| 148 | + .hero-dot { |
| 149 | + width: 10px; |
| 150 | + height: 10px; |
| 151 | + border-radius: 50%; |
| 152 | + background: rgba(255, 255, 255, 0.3); |
| 153 | + cursor: pointer; |
| 154 | + transition: all 0.3s ease; |
| 155 | + border: none; |
| 156 | + padding: 0; |
| 157 | + } |
| 158 | + |
| 159 | + .hero-dot:hover { |
| 160 | + background: rgba(255, 255, 255, 0.6); |
| 161 | + } |
| 162 | + |
| 163 | + .hero-dot.active { |
| 164 | + background: #667eea; |
| 165 | + transform: scale(1.2); |
| 166 | + } |
| 167 | + |
| 168 | + .hero-arrow { |
| 169 | + position: absolute; |
| 170 | + top: 50%; |
| 171 | + transform: translateY(-50%); |
| 172 | + width: 50px; |
| 173 | + height: 50px; |
| 174 | + background: rgba(0, 0, 0, 0.5); |
| 175 | + border: none; |
| 176 | + border-radius: 50%; |
| 177 | + color: white; |
| 178 | + font-size: 1.5rem; |
| 179 | + cursor: pointer; |
| 180 | + z-index: 20; |
| 181 | + transition: all 0.3s ease; |
| 182 | + display: flex; |
| 183 | + align-items: center; |
| 184 | + justify-content: center; |
| 185 | + opacity: 0; |
| 186 | + } |
| 187 | + |
| 188 | + .hero:hover .hero-arrow { |
| 189 | + opacity: 1; |
| 190 | + } |
| 191 | + |
| 192 | + .hero-arrow:hover { |
| 193 | + background: rgba(102, 126, 234, 0.8); |
| 194 | + } |
| 195 | + |
| 196 | + .hero-arrow-left { |
| 197 | + left: 20px; |
| 198 | + } |
| 199 | + |
| 200 | + .hero-arrow-right { |
| 201 | + right: 20px; |
| 202 | + } |
| 203 | + |
| 204 | + .hero-progress { |
| 205 | + position: absolute; |
| 206 | + bottom: 0; |
| 207 | + left: 0; |
| 208 | + height: 3px; |
| 209 | + background: linear-gradient(90deg, #667eea, #764ba2); |
| 210 | + z-index: 20; |
| 211 | + transition: width 0.1s linear; |
| 212 | + } |
| 213 | + |
121 | 214 | .hero-badge { |
122 | 215 | display: inline-block; |
123 | 216 | padding: 6px 16px; |
|
830 | 923 |
|
831 | 924 | .hero-content { |
832 | 925 | padding: 0 20px; |
| 926 | + bottom: 50px; |
833 | 927 | } |
834 | 928 |
|
835 | 929 | .hero-title { |
836 | 930 | font-size: 2rem; |
837 | 931 | } |
838 | 932 |
|
| 933 | + .hero-arrow { |
| 934 | + width: 40px; |
| 935 | + height: 40px; |
| 936 | + font-size: 1.2rem; |
| 937 | + opacity: 1; |
| 938 | + } |
| 939 | + |
| 940 | + .hero-arrow-left { |
| 941 | + left: 10px; |
| 942 | + } |
| 943 | + |
| 944 | + .hero-arrow-right { |
| 945 | + right: 10px; |
| 946 | + } |
| 947 | + |
| 948 | + .hero-nav { |
| 949 | + bottom: 15px; |
| 950 | + } |
| 951 | + |
| 952 | + .hero-dot { |
| 953 | + width: 8px; |
| 954 | + height: 8px; |
| 955 | + } |
| 956 | + |
839 | 957 | .container { |
840 | 958 | padding: 0 20px; |
841 | 959 | } |
|
885 | 1003 | </div> |
886 | 1004 | </nav> |
887 | 1005 |
|
888 | | - {% if featured_games %} |
889 | | - {% set hero_game = featured_games[0] %} |
890 | | - {% set hero_screenshots = parse_json(hero_game.igdb_screenshots) %} |
891 | | - {% set hero_bg = hero_screenshots[0] if hero_screenshots else (hero_game.igdb_cover_url or hero_game.cover_image) %} |
892 | | - {% set hero_genres = parse_json(hero_game.genres) %} |
893 | | - |
894 | | - <section class="hero"> |
895 | | - {% if hero_bg %} |
896 | | - <img src="{{ hero_bg.replace('t_screenshot_big', 't_1080p') if 't_screenshot' in hero_bg else hero_bg }}" alt="" class="hero-bg"> |
897 | | - {% endif %} |
898 | | - <div class="hero-gradient"></div> |
899 | | - <div class="hero-content"> |
900 | | - <span class="hero-badge">Featured in Your Library</span> |
901 | | - <h1 class="hero-title">{{ hero_game.name }}</h1> |
902 | | - <div class="hero-meta"> |
903 | | - {% if hero_game.total_rating %} |
904 | | - <div class="hero-rating"> |
905 | | - <span class="hero-rating-score">{{ hero_game.total_rating|round|int }}</span> |
906 | | - <span class="hero-rating-label">IGDB Score</span> |
| 1006 | + {% set slideshow_games = featured_games[1:20] if featured_games|length > 1 else [] %} |
| 1007 | + {% if slideshow_games|length >= 5 %} |
| 1008 | + <section class="hero" id="hero-slideshow"> |
| 1009 | + {% for game in slideshow_games %} |
| 1010 | + {% set screenshots = parse_json(game.igdb_screenshots) %} |
| 1011 | + {% set bg = screenshots[0] if screenshots else (game.igdb_cover_url or game.cover_image) %} |
| 1012 | + {% set genres = parse_json(game.genres) %} |
| 1013 | + <div class="hero-slide{% if loop.first %} active{% endif %}" data-slide-index="{{ loop.index0 }}"> |
| 1014 | + {% if bg %} |
| 1015 | + <img src="{{ bg.replace('t_screenshot_big', 't_1080p') if 't_screenshot' in bg else bg }}" alt="" class="hero-bg"> |
| 1016 | + {% endif %} |
| 1017 | + <div class="hero-gradient"></div> |
| 1018 | + <div class="hero-content"> |
| 1019 | + <span class="hero-badge"> |
| 1020 | + {% if popularity_source == 'igdb_popularity' %} |
| 1021 | + Trending in Your Library |
| 1022 | + {% else %} |
| 1023 | + Featured in Your Library |
| 1024 | + {% endif %} |
| 1025 | + </span> |
| 1026 | + <h1 class="hero-title">{{ game.name }}</h1> |
| 1027 | + <div class="hero-meta"> |
| 1028 | + {% if game.total_rating %} |
| 1029 | + <div class="hero-rating"> |
| 1030 | + <span class="hero-rating-score">{{ game.total_rating|round|int }}</span> |
| 1031 | + <span class="hero-rating-label">IGDB Score</span> |
| 1032 | + </div> |
| 1033 | + {% endif %} |
| 1034 | + {% if genres %} |
| 1035 | + <div class="hero-genres"> |
| 1036 | + {% for genre in genres[:3] %} |
| 1037 | + <span class="hero-genre">{{ genre }}</span> |
| 1038 | + {% endfor %} |
| 1039 | + </div> |
| 1040 | + {% endif %} |
907 | 1041 | </div> |
| 1042 | + {% set desc = game.igdb_summary or game.description %} |
| 1043 | + {% if desc %} |
| 1044 | + <p class="hero-description">{{ desc }}</p> |
908 | 1045 | {% endif %} |
909 | | - {% if hero_genres %} |
910 | | - <div class="hero-genres"> |
911 | | - {% for genre in hero_genres[:3] %} |
912 | | - <span class="hero-genre">{{ genre }}</span> |
913 | | - {% endfor %} |
| 1046 | + <div class="hero-actions"> |
| 1047 | + <a href="/game/{{ game.id }}" class="btn btn-primary">View Details</a> |
| 1048 | + <button class="btn btn-secondary" onclick="openExpandedCard({{ game|tojson|e }})">Quick Look</button> |
914 | 1049 | </div> |
915 | | - {% endif %} |
916 | | - </div> |
917 | | - {% set hero_desc = hero_game.igdb_summary or hero_game.description %} |
918 | | - {% if hero_desc %} |
919 | | - <p class="hero-description">{{ hero_desc }}</p> |
920 | | - {% endif %} |
921 | | - <div class="hero-actions"> |
922 | | - <a href="/game/{{ hero_game.id }}" class="btn btn-primary">View Details</a> |
923 | | - <button class="btn btn-secondary" onclick="openExpandedCard({{ hero_game|tojson|e }})">Quick Look</button> |
924 | 1050 | </div> |
925 | 1051 | </div> |
| 1052 | + {% endfor %} |
| 1053 | + |
| 1054 | + <button class="hero-arrow hero-arrow-left" onclick="prevSlide()">‹</button> |
| 1055 | + <button class="hero-arrow hero-arrow-right" onclick="nextSlide()">›</button> |
| 1056 | + |
| 1057 | + <div class="hero-nav"> |
| 1058 | + {% for game in slideshow_games %} |
| 1059 | + <button class="hero-dot{% if loop.first %} active{% endif %}" data-slide="{{ loop.index0 }}" onclick="goToSlide({{ loop.index0 }})"></button> |
| 1060 | + {% endfor %} |
| 1061 | + </div> |
| 1062 | + |
| 1063 | + <div class="hero-progress" id="hero-progress"></div> |
926 | 1064 | </section> |
927 | 1065 | {% endif %} |
928 | 1066 |
|
@@ -1927,8 +2065,110 @@ <h3 class="expanded-card-section-title">Screenshots</h3> |
1927 | 2065 | const row = container.querySelector('.game-row'); |
1928 | 2066 | row.addEventListener('scroll', () => updateScrollButtons(container)); |
1929 | 2067 | }); |
| 2068 | + |
| 2069 | + // Initialize hero slideshow |
| 2070 | + initHeroSlideshow(); |
1930 | 2071 | }); |
1931 | 2072 |
|
| 2073 | + // Hero Slideshow Functions |
| 2074 | + let currentSlide = 0; |
| 2075 | + let slideCount = 0; |
| 2076 | + let slideshowInterval = null; |
| 2077 | + let progressInterval = null; |
| 2078 | + const SLIDE_DURATION = 7000; // 7 seconds per slide |
| 2079 | + const PROGRESS_UPDATE_INTERVAL = 50; |
| 2080 | + |
| 2081 | + function initHeroSlideshow() { |
| 2082 | + const hero = document.getElementById('hero-slideshow'); |
| 2083 | + if (!hero) return; |
| 2084 | + |
| 2085 | + const slides = hero.querySelectorAll('.hero-slide'); |
| 2086 | + slideCount = slides.length; |
| 2087 | + |
| 2088 | + if (slideCount > 0) { |
| 2089 | + startSlideshow(); |
| 2090 | + |
| 2091 | + // Pause on hover |
| 2092 | + hero.addEventListener('mouseenter', pauseSlideshow); |
| 2093 | + hero.addEventListener('mouseleave', startSlideshow); |
| 2094 | + } |
| 2095 | + } |
| 2096 | + |
| 2097 | + function startSlideshow() { |
| 2098 | + if (slideCount <= 1) return; |
| 2099 | + |
| 2100 | + let progressTime = 0; |
| 2101 | + const progressBar = document.getElementById('hero-progress'); |
| 2102 | + |
| 2103 | + // Clear any existing intervals |
| 2104 | + pauseSlideshow(); |
| 2105 | + |
| 2106 | + // Progress bar animation |
| 2107 | + progressInterval = setInterval(() => { |
| 2108 | + progressTime += PROGRESS_UPDATE_INTERVAL; |
| 2109 | + const percent = (progressTime / SLIDE_DURATION) * 100; |
| 2110 | + if (progressBar) { |
| 2111 | + progressBar.style.width = percent + '%'; |
| 2112 | + } |
| 2113 | + }, PROGRESS_UPDATE_INTERVAL); |
| 2114 | + |
| 2115 | + // Auto-advance slides |
| 2116 | + slideshowInterval = setInterval(() => { |
| 2117 | + nextSlide(); |
| 2118 | + progressTime = 0; |
| 2119 | + }, SLIDE_DURATION); |
| 2120 | + } |
| 2121 | + |
| 2122 | + function pauseSlideshow() { |
| 2123 | + if (slideshowInterval) { |
| 2124 | + clearInterval(slideshowInterval); |
| 2125 | + slideshowInterval = null; |
| 2126 | + } |
| 2127 | + if (progressInterval) { |
| 2128 | + clearInterval(progressInterval); |
| 2129 | + progressInterval = null; |
| 2130 | + } |
| 2131 | + } |
| 2132 | + |
| 2133 | + function goToSlide(index) { |
| 2134 | + if (index === currentSlide) return; |
| 2135 | + |
| 2136 | + const hero = document.getElementById('hero-slideshow'); |
| 2137 | + if (!hero) return; |
| 2138 | + |
| 2139 | + const slides = hero.querySelectorAll('.hero-slide'); |
| 2140 | + const dots = hero.querySelectorAll('.hero-dot'); |
| 2141 | + const progressBar = document.getElementById('hero-progress'); |
| 2142 | + |
| 2143 | + // Update slides |
| 2144 | + slides[currentSlide].classList.remove('active'); |
| 2145 | + slides[index].classList.add('active'); |
| 2146 | + |
| 2147 | + // Update dots |
| 2148 | + dots[currentSlide].classList.remove('active'); |
| 2149 | + dots[index].classList.add('active'); |
| 2150 | + |
| 2151 | + currentSlide = index; |
| 2152 | + |
| 2153 | + // Reset progress bar |
| 2154 | + if (progressBar) { |
| 2155 | + progressBar.style.width = '0%'; |
| 2156 | + } |
| 2157 | + |
| 2158 | + // Restart slideshow timer |
| 2159 | + startSlideshow(); |
| 2160 | + } |
| 2161 | + |
| 2162 | + function nextSlide() { |
| 2163 | + const nextIndex = (currentSlide + 1) % slideCount; |
| 2164 | + goToSlide(nextIndex); |
| 2165 | + } |
| 2166 | + |
| 2167 | + function prevSlide() { |
| 2168 | + const prevIndex = (currentSlide - 1 + slideCount) % slideCount; |
| 2169 | + goToSlide(prevIndex); |
| 2170 | + } |
| 2171 | + |
1932 | 2172 | // Expanded Card Functions |
1933 | 2173 | let currentGame = null; |
1934 | 2174 |
|
|
0 commit comments