Vorschau Hero: Video vollstaendig sichtbar + Endcard mit CTA am Ende
Umgebaut zu Video-getriebenem Slider mit Endcard-Phase:
- Video object-fit: contain (komplett sichtbar, Navy-Letterbox-Raender)
- Waehrend Video-Laufzeit: keine Overlays (Video spricht fuer sich)
- ended-Event triggert .ended/.endcard-Klassen -> 3s Endcard
- Endcard zeigt Titel + Tagline (zentriert) + Beispieltext + CTA
- Slide 2 ohne CTA: Endcard zeigt nur Titel + Tagline
- loop-Attribut aus allen 5 Videos entfernt (sonst feuert ended nicht)
- Festes 15s-Autoplay entfernt; Slide-Wechsel ist video-getrieben
- Fallback-Timer 25s falls ended-Event nicht kommt (Ladefehler etc.)
- Manuelle Navigation (Dots/Pfeile/Swipe) cleant alle Timer sofort
- Tab-Wechsel pausiert/resumt korrekt im jeweiligen Zustand
Rollback-Punkt: c693114
Dieser Commit ist enthalten in:
@@ -41,13 +41,21 @@
|
||||
});
|
||||
});
|
||||
|
||||
/* ==================== HERO SLIDER ==================== */
|
||||
/* ==================== HERO SLIDER (video-driven mit Endcard) ==================== */
|
||||
var heroEl = document.querySelector('.hero');
|
||||
var heroSlides = document.querySelectorAll('.hero-slide');
|
||||
var heroDots = document.querySelectorAll('.hero-dot');
|
||||
var heroCurrentSlide = 0;
|
||||
var heroTimer = null;
|
||||
var HERO_INTERVAL = 15000;
|
||||
var heroEndcardTimer = null;
|
||||
var heroFallbackTimer = null;
|
||||
var heroIsTransitioning = false;
|
||||
var HERO_ENDCARD_MS = 3000;
|
||||
var HERO_FALLBACK_MS = 25000;
|
||||
|
||||
function heroClearTimers() {
|
||||
if (heroEndcardTimer) { clearTimeout(heroEndcardTimer); heroEndcardTimer = null; }
|
||||
if (heroFallbackTimer) { clearTimeout(heroFallbackTimer); heroFallbackTimer = null; }
|
||||
}
|
||||
|
||||
function heroPlaySlideVideo(slide) {
|
||||
var v = slide && slide.querySelector('video');
|
||||
@@ -62,16 +70,43 @@
|
||||
if (v) v.pause();
|
||||
}
|
||||
|
||||
function heroEnterEndcard() {
|
||||
if (!heroSlides.length) return;
|
||||
var slide = heroSlides[heroCurrentSlide];
|
||||
if (!slide || slide.classList.contains('ended')) return;
|
||||
slide.classList.add('ended');
|
||||
if (heroEl) heroEl.classList.add('endcard');
|
||||
heroClearTimers();
|
||||
heroEndcardTimer = setTimeout(function () {
|
||||
heroEndcardTimer = null;
|
||||
heroNext();
|
||||
}, HERO_ENDCARD_MS);
|
||||
}
|
||||
|
||||
function heroStartSlide() {
|
||||
var slide = heroSlides[heroCurrentSlide];
|
||||
if (!slide) return;
|
||||
slide.classList.remove('ended');
|
||||
if (heroEl) heroEl.classList.remove('endcard');
|
||||
heroPlaySlideVideo(slide);
|
||||
heroClearTimers();
|
||||
heroFallbackTimer = setTimeout(function () {
|
||||
heroFallbackTimer = null;
|
||||
heroEnterEndcard();
|
||||
}, HERO_FALLBACK_MS);
|
||||
}
|
||||
|
||||
function heroGoTo(index) {
|
||||
if (heroIsTransitioning || index === heroCurrentSlide || !heroSlides.length) return;
|
||||
heroIsTransitioning = true;
|
||||
heroClearTimers();
|
||||
|
||||
var oldIndex = heroCurrentSlide;
|
||||
heroSlides[oldIndex].classList.add('exiting');
|
||||
heroSlides[oldIndex].classList.remove('active');
|
||||
heroSlides[oldIndex].classList.remove('active', 'ended');
|
||||
if (heroEl) heroEl.classList.remove('endcard');
|
||||
if (heroDots[oldIndex]) heroDots[oldIndex].classList.remove('active');
|
||||
|
||||
// Altes Video sofort pausieren, damit es während des Fade-Outs nicht mehr läuft
|
||||
heroPauseSlideVideo(heroSlides[oldIndex]);
|
||||
|
||||
setTimeout(function () {
|
||||
@@ -79,10 +114,7 @@
|
||||
heroCurrentSlide = index;
|
||||
heroSlides[heroCurrentSlide].classList.add('active');
|
||||
if (heroDots[heroCurrentSlide]) heroDots[heroCurrentSlide].classList.add('active');
|
||||
|
||||
// Neues Video von Anfang starten, sobald der Slide sichtbar wird
|
||||
heroPlaySlideVideo(heroSlides[heroCurrentSlide]);
|
||||
|
||||
heroStartSlide();
|
||||
heroIsTransitioning = false;
|
||||
}, 400);
|
||||
}
|
||||
@@ -95,55 +127,66 @@
|
||||
heroGoTo((heroCurrentSlide - 1 + heroSlides.length) % heroSlides.length);
|
||||
}
|
||||
|
||||
function heroStartAutoplay() {
|
||||
heroStopAutoplay();
|
||||
heroTimer = setTimeout(function tick() {
|
||||
heroNext();
|
||||
heroTimer = setTimeout(tick, HERO_INTERVAL);
|
||||
}, HERO_INTERVAL);
|
||||
}
|
||||
|
||||
function heroStopAutoplay() {
|
||||
if (heroTimer) { clearInterval(heroTimer); heroTimer = null; }
|
||||
}
|
||||
// Pro Video: 'ended' → Endcard-Phase starten
|
||||
heroSlides.forEach(function (slide) {
|
||||
var v = slide.querySelector('video');
|
||||
if (!v) return;
|
||||
v.addEventListener('ended', function () {
|
||||
if (slide.classList.contains('active')) heroEnterEndcard();
|
||||
});
|
||||
});
|
||||
|
||||
heroDots.forEach(function (dot, i) {
|
||||
dot.addEventListener('click', function () {
|
||||
heroGoTo(i);
|
||||
heroStartAutoplay();
|
||||
});
|
||||
dot.addEventListener('click', function () { heroGoTo(i); });
|
||||
});
|
||||
|
||||
var heroPrevBtn = document.querySelector('.hero-arrow-prev');
|
||||
var heroNextBtn = document.querySelector('.hero-arrow-next');
|
||||
if (heroPrevBtn) heroPrevBtn.addEventListener('click', function () { heroPrev(); heroStartAutoplay(); });
|
||||
if (heroNextBtn) heroNextBtn.addEventListener('click', function () { heroNext(); heroStartAutoplay(); });
|
||||
if (heroPrevBtn) heroPrevBtn.addEventListener('click', heroPrev);
|
||||
if (heroNextBtn) heroNextBtn.addEventListener('click', heroNext);
|
||||
|
||||
var heroSlider = document.querySelector('.hero-slider');
|
||||
if (heroSlider) {
|
||||
var heroTouchStartX = 0;
|
||||
heroSlider.addEventListener('touchstart', function (e) {
|
||||
heroTouchStartX = e.changedTouches[0].screenX;
|
||||
heroStopAutoplay();
|
||||
}, { passive: true });
|
||||
heroSlider.addEventListener('touchend', function (e) {
|
||||
var diff = e.changedTouches[0].screenX - heroTouchStartX;
|
||||
if (Math.abs(diff) > 50) {
|
||||
if (diff < 0) heroNext(); else heroPrev();
|
||||
}
|
||||
heroStartAutoplay();
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
if (document.hidden) { heroStopAutoplay(); }
|
||||
else { heroStartAutoplay(); }
|
||||
var slide = heroSlides[heroCurrentSlide];
|
||||
if (!slide) return;
|
||||
if (document.hidden) {
|
||||
heroClearTimers();
|
||||
heroPauseSlideVideo(slide);
|
||||
return;
|
||||
}
|
||||
if (slide.classList.contains('ended')) {
|
||||
heroEndcardTimer = setTimeout(function () {
|
||||
heroEndcardTimer = null;
|
||||
heroNext();
|
||||
}, HERO_ENDCARD_MS);
|
||||
} else {
|
||||
var v = slide.querySelector('video');
|
||||
if (v) {
|
||||
var p = v.play();
|
||||
if (p && typeof p.catch === 'function') p.catch(function () {});
|
||||
}
|
||||
heroFallbackTimer = setTimeout(function () {
|
||||
heroFallbackTimer = null;
|
||||
heroEnterEndcard();
|
||||
}, HERO_FALLBACK_MS);
|
||||
}
|
||||
});
|
||||
|
||||
if (heroSlides.length > 1) heroStartAutoplay();
|
||||
|
||||
// Initialer Video-Start für Slide 0 (ersetzt das weggefallene autoplay-Attribut)
|
||||
if (heroSlides.length) heroPlaySlideVideo(heroSlides[0]);
|
||||
// Initialer Start (Slide 0 ist bereits .active im HTML)
|
||||
if (heroSlides.length) heroStartSlide();
|
||||
|
||||
/* ==================== MAP STATE ==================== */
|
||||
var mapInstance = null;
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren